如何将此map-reduce javascript代码渲染为等效的LINQ Select-Aggregate?

时间:2016-11-20 21:07:16

标签: c# linq dictionary select

如何将此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;

};

1 个答案:

答案 0 :(得分:2)

直接等价物看起来像这样:

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<string, decimal[]>代替IEnumerable<Transform>来实现这一点,所以这里是:

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<TKey, TValue>IEnumerable<KeyValuePair<TKey, TValue>>,因此在此解决方案中,变换由KeyValuePair<string, decimal[]>表示。

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

此解决方案的缺点是:

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

如果您确实需要变换的通用容器而不是命名类,则可以使用Tuple<T, U>

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)));
}

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