【问题标题】:How to render this map-reduce javascript code to an equivalent LINQ Select-Aggregate?如何将此 map-reduce javascript 代码呈现为等效的 LINQ Select-Aggregate?
【发布时间】:2016-11-20 21:07:16
【问题描述】:

如何将此 map-reduce javascript 函数呈现为等效的 c# LINQ Select-Aggregate 语法?

/**
 * Multiply transforms into one.
 *
 * @param {Array} input transforms array
 * @return {Array} output matrix array
 */
exports.transformsMultiply = function(transforms) {

    // convert transforms objects to the matrices
    transforms = transforms.map(function(transform) {
        if (transform.name === 'matrix') {
            return transform.data;
        }
        return transformToMatrix(transform);
    });

    // multiply all matrices into one
    transforms = {
        name: 'matrix',
        data: transforms.reduce(function(a, b) {
            return multiplyTransformMatrices(a, b);
        })
    };

    return transforms;

};

【问题讨论】:

  • 您有一本字典作为此帖子的标签。所以字典看起来像这样: var matrices = transforms.GroupBy(x => x.a, y => y.b) .ToDictionary(x => x.Key, y => y.ToList());
  • @jdweng 谢谢。但是,如果我还有一个字典作为输入参数“转换”呢?类似于: Dictionary ,其中 decimal[] 是矩阵系数的数组?
  • 单个数组如何映射到 (a,b)?
  • @jdweng C# 的 reduce 等价物是 Aggregate 方法。使用该方法,参数 (a, b) 是同一单个数组的元素 [i] 和 [i+1]。你可以在这里找到一个很好的解释:tutorialsteacher.com/linq/linq-aggregation-operator-aggregate
  • 该示例未显示“矩阵系数”。系数通常是对项的常数乘数。该示例仅显示术语数组。

标签: c# linq dictionary select


【解决方案1】:

直接等效项如下所示:

public Transform TransformsMultiply(IEnumerable<Transform> transforms)
{
    var matrices = transforms.Select(transform =>
        transform.Name == "matrix"
            ? transform.Data
            : transformToMatrix(transform));

    return new Transform
    {
        Name = "matrix",
        Data = matrices.Aggregate((a, b) =>
            multiplyTransformMatrices(a, b));
    };
}

当然,您需要定义Transform 类以及transformToMatrixmultiplyTransformMatrices 方法;

虽然通过Name 属性区分转换类型不是惯用的 C#。如果您为每种转换类型创建一个抽象基 Transform 类型和子类会更好:

public abstract class Transform { /* ... */ }

public class Matrix : Transform
{
    public int[,] Data { get; set; }
}

public class OtherTypeOfTransform : Transform
{
    /* ... */
}

方法的第一部分如下所示:

var matrices = transforms.Select(transform =>
    transform is Matrix 
        ? transform.Data 
        : transformToMatrix(transform));

但这不是非常面向对象的,因为transformToMatrix 需要知道如何将每种类型的变换转换为矩阵。您应该考虑在基类中添加一个抽象的ToMatrix 方法并在子类中实现它,从而将自身转换为矩阵的责任委托给每个子类。

public abstract class Transform
{
    public abstract Matrix ToMatrix();
}

public class Matrix : Transform
{
    public int[,] Data { get; set; }

    public Matrix ToMatrix() { return this; }
}

public class OtherTypeOfTransform : Transform
{
    public Matrix ToMatrix()
    {
        // Type-specific implementation
    }
}

之后,实现将如下所示:

public Matrix TransformsMultiply(IEnumerable<Transform> transforms)
{
    return new Matrix
    {
        Data = transforms
            .Select(transform => transform.ToMatrix())
            .Aggregate((a, b) => multiplyTransformMatrices(a, b));
    };
}

将乘法移到 Matrix 类中会更好:

public Matrix TransformsMultiply(IEnumerable<Transform> transforms)
{
    return new Matrix
    {
        Data = transforms
            .Select(transform => transform.ToMatrix())
            .Aggregate((a, b) => a.MultiplyBy(b));
    };
}

这就是我将 JavaScript 转换为惯用的面向对象的 C# 代码的方式。

编辑

您问我如何使用Dictionary&lt;string, decimal[]&gt; 而不是IEnumerable&lt;Transform&gt; 来实现它,所以这里是:

public KeyValuePair<string, decimal[]> TransformsMultiply(
    Dictionary<string, decimal[]> transforms)
{
    var matrices = transforms.Select(kvp =>
        kvp.Key == "matrix"
            ? kvp.Value
            : transformToMatrix(kvg.Value));

    return new KeyValuePair<string, int>(
        "matrix", 
        matrices.Aggregate((a, b) => multiplyTransformMatrices(a, b));
}

Dictionary&lt;TKey, TValue&gt;IEnumerable&lt;KeyValuePair&lt;TKey, TValue&gt;&gt;,因此在此解决方案中,转换由 KeyValuePair&lt;string, decimal[]&gt; 表示。

请注意,字典键是唯一的,因此您只能使用键 "matrix" 进行一次转换。我不确定这是否是你想要的。

这种方案的缺点是:

  • 由于字典键的唯一性,每种类型的转换只能有一个实例
  • 它掩盖了代码的意图并使其更难阅读(在我看来
  • 逻辑上属于变换本身的行为(如何将其转换为矩阵以及如何将一个矩阵与另一个矩阵相乘)现在没有被封装,它在transformToMatrixmultiplyTransformMatrices 方法中。李>

如果你真的想要一个通用容器来代替命名类,你可以使用Tuple&lt;T, U&gt;:

public Tuple<string, decimal[]> TransformsMultiply(
    IEnumerable<Tuple<string, decimal[]>> transforms)
{
    var matrices = transforms.Select(t =>
        t.Item1 == "matrix" ? t.Item2 : transformToMatrix(t.Item2));

    return new Tuple<string, decimal[]>(
        "matrix",
        matrices.Aggregate((a, b) => multiplyTransformMatrices(a, b)));
}

在这里你可以有多个同名的变换,但上面提到的第二个和第三个缺点仍然适用。

【讨论】:

  • 谢谢,这很有帮助!
  • 如果我有一本字典作为“转换”呢?类似于: Dictionary ,其中 decimal[] 是矩阵系数的数组?而Name 和Data 是Dictionary 条目的Key 和Value 属性?
  • 这也可以,但只有一个字典项可以有键“矩阵”。我不确定这是否是你想要的。我将使用字典添加答案。但我不建议这样做。
  • 非常感谢您的进一步澄清!然后我将使用命名类。
  • 很高兴能帮上忙 :)
猜你喜欢
  • 1970-01-01
  • 2023-01-19
  • 2013-09-27
  • 2010-10-09
  • 1970-01-01
  • 2012-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多