C# C选择累加器不工作时的过载方法

C# C选择累加器不工作时的过载方法,c#,linq,functional-programming,C#,Linq,Functional Programming,我在C中创建了一个映射函数,在许多方面起作用,因为它是JavaScript,相当于项目对象类型。此后,我将这些方法重命名为“Select”,用作重载,使它们感觉更加“集成”。这是一个链,所以请容忍我,但受影响的函数看起来像这样 public static TResult Project<TInput, TResult>(this TInput input, Func<TInput, TResult> projectionMapping) =>

我在C中创建了一个映射函数,在许多方面起作用,因为它是JavaScript,相当于项目对象类型。此后,我将这些方法重命名为“Select”,用作重载,使它们感觉更加“集成”。这是一个链,所以请容忍我,但受影响的函数看起来像这样

    public static TResult Project<TInput, TResult>(this TInput input, Func<TInput, TResult> projectionMapping)
        => projectionMapping(input);

    public static TResult Project<TInput, TAccumulatedValue, TIncrementingValue, TResult>(this TInput input, Func<TInput, TAccumulatedValue, TResult> projectionMapping, 
        Func<TAccumulatedValue, TIncrementingValue, TAccumulatedValue> accumulator, TAccumulatedValue initialAccumulatorValue, TIncrementingValue increment)
        => projectionMapping(input, accumulator(initialAccumulatorValue, increment));

    public static IEnumerable<TResult> Select<TInput, TAccumulatedValue, TIncrementingValue, TResult>(this IEnumerable<TInput> input,
        Func<TInput, TAccumulatedValue, TResult> projectionMapping, Func<TAccumulatedValue, TIncrementingValue, TAccumulatedValue> accumulator, 
        TAccumulatedValue initialAccumulatorValue, TIncrementingValue increment)
        => input.Select(item => item.Project(projectionMapping, accumulator, initialAccumulatorValue, increment));

    // This doesn't work.
    public static IEnumerable<TResult> Select<TInput, TResult>(this IEnumerable<TInput> input,
        Func<TInput, int, TResult> projectionMapping, int initialAccumulatorValue = -1, int increment = 1)
    {
        return input.Select(projectionMapping, (acc, inc) => acc + inc,
            initialAccumulatorValue, increment);
    }
rowValues = new string[] { "Item 1", "Item 2", "Item 3" };
我希望有一个包含以下数据的MyObject项列表

10 |项目1 20 |项目2 30 |项目3
问题是您总是使用InitialAccumeratorValue调用累加器

为了实现该目标,您需要维护累积值,最简单正确的方法是使用C迭代器方法:

public static IEnumerable<TResult> Map<TInput, TAccumulatedValue, TIncrementingValue, TResult>(this IEnumerable<TInput> input,
    Func<TInput, TAccumulatedValue, TResult> projectionMapping, Func<TAccumulatedValue, TIncrementingValue, TAccumulatedValue> accumulator,
    TAccumulatedValue initialAccumulatorValue, TIncrementingValue increment)
{
    var accumulatedValue = initialAccumulatorValue;
    foreach (var item in input)
        yield return projectionMapping(item, accumulatedValue = accumulator(accumulatedValue, increment));
}
这根本不起作用,因为返回的select查询的多次执行将共享累加的值,因此它们将产生不正确的结果。迭代器方法没有这样的问题,因为只要调用GetEnumerator方法,代码实际上就会执行。

假设:

输入源为:

var rows = new[] { "Item 1", "Item 2", "Item 3", "Item 4" };
解决方案

要获得您所提供的输入的特定结果,您可以简单地使用框架中提供的Select:

var result = rows.Select((v, i) => new MyObject((i+1)*10, v)).ToList();
诚然,这看起来不太好,但确实奏效了


Ivan的答案更简洁,但我会坚持上面的内容,以防有人发现它有用。

我从更改第三个函数开始,这样它只需要一个累加器函数,返回序列中的下一个索引。这允许函数具有计算递增累加器值所需的状态

public static IEnumerable<TResult> Project<TInput, TAccumulatorValue, TResult>(this IEnumerable<TInput> input,
    Func<TInput, TAccumulatorValue, TResult> projectionMapping, 
    Func<TAccumulatorValue> accumulator)
{
    return input.Select(item => projectionMapping(item, accumulator()));
}

请为源数据添加源数据和所需结果。任何带有索引的内容都是此操作的有效源数据。问题在于索引值的累加器,而不是数据。我将添加调用该方法的测试,但我认为这没有多大帮助。可能重复:您似乎在这方面投入了很多精力,但是有一个超负荷的Select也需要索引:我认为它已经实现了我上面提到的链接中描述的内容。我相信这就是我的头一直在撞击的那堵墙。谢谢你,伊万。我将尝试一下,看看它是否对我有效。这对我有效-该方法现在像一个符咒一样工作,作为一个额外的选择重载,需要一个累加器,我有整数和时间变量。看起来你没有读我关于闭包和选择的文章的结尾:你的第二个和第三个选项都受到闭包的影响正在重新初始化,这可以通过两次枚举或列出result2或result3变量来轻松看到。@IvanStoev Touché!我没有考虑多次迭代。我将从我的答案中删除这些选项,因为它们可能会产生极大的误导。谢谢你让我注意到这一点。这可能适用于超过一半的将来需要类似的情况。我的需要对int和DateTimes进行奇怪的排序,因此通用的core方法.Zip对我来说是一个新函数-这看起来像是另一个非常巧妙的解决方案
var rows = new[] { "Item 1", "Item 2", "Item 3", "Item 4" };
var result = rows.Select((v, i) => new MyObject((i+1)*10, v)).ToList();
public static IEnumerable<TResult> Project<TInput, TAccumulatorValue, TResult>(this IEnumerable<TInput> input,
    Func<TInput, TAccumulatorValue, TResult> projectionMapping, 
    Func<TAccumulatorValue> accumulator)
{
    return input.Select(item => projectionMapping(item, accumulator()));
}
public static IEnumerable<TResult> Project<TInput, TResult>(this IEnumerable<TInput> input,
    Func<TInput, int, TResult> projectionMapping, int initialAccumulatorValue = 0, int increment = 1)
{
    int curValue = initialAccumulatorValue;
    return input.Project(projectionMapping, 
        () => { var ret = curValue; curValue += increment; return ret; });
}
public static IEnumerable<int> Range(int start, int increment)
{
    for (; ; )
    {
        yield return start;
        start += increment;
    }
}
var items = new[] { "a", "b", "c" };

// use Project, returns: a10, b20, c30
var results = items.Project((s, i) => s + i.ToString(), 10, 10).ToList();

// use Zip with custom range, returns: a10, b20, c30
var results2 = items.Zip(Range(10, 10), (s, i) => s + i.ToString()).ToList();

// use Zip with standard range, returns: a1, b2, c3
var results3 = items.Zip(Enumerable.Range(1, int.MaxValue), (s, i) => s + i.ToString()).ToList();