【问题标题】:Loop - Calculated last element different循环 - 计算的最后一个元素不同
【发布时间】:2019-03-20 07:12:15
【问题描述】:

大家好(抱歉标题不好),

我有一个循环,每次通过时我都可以得到一个舍入差异。我想累积它们并将其添加到我的结果的最后一条记录中。

var cumulatedRoundDifference = 0m;
var resultSet = Enumerable.Range(0, periods)
    .Select(currentPeriod => {
        var value = this.CalculateValue(currentPeriod);    
        var valueRounded = this.CommercialRound(value);

        // Bad part :(
        cumulatedRoundDifference += value - valueRounded; 
        if (currentPeriod == periods - 1)
           valueRounded = this.CommercialRound(value + valueRounded);

    return valuesRounded;
}

目前我认为代码不是很好。 是否有这种事情的模式/算法,或者它在某种程度上对 Linq 很聪明,没有循环外的变量?

许多问候

【问题讨论】:

  • 你的意思是return valueRounded; 没有s
  • cumulatedRoundDifference 似乎不是返回值的一部分。
  • @Magnus 累积实际上是最后结果的主要内容,因为它在运行循环后发生了变化。我有另一个问题。为什么,对于最后一个值(currentPeriod == period - 1),路由是这样“再次”完成的?

标签: c# algorithm linq


【解决方案1】:

您似乎在做两件事 - 舍入所有内容,并计算总舍入误差。

可以删除 lambda 之外的变量,但是您需要 2 个查询。

var baseQuery = Enumerable.Range(0, periods)
   .Select(x => new { Value = CalculateValue(x), ValueRounded = CommercialRound(x) });
var cumulateRoundDifference = baseQuery.Select(x => x.Value - x.ValueRounded).Sum();
// LINQ isn't really good at doing something different to the last element
var resultSet = baseQuery.Select(x => x.ValueRounded).Take(periods - 1).Concat(new[] { CommercialRound(CalculateValue(periods - 1) + CommericalRound(periods - 1)) });

【讨论】:

    【解决方案2】:

    这样的事情是否有模式/算法,或者它是否巧妙地使用了 Linq,没有循环外的变量?

    我不太同意你想要完成的事情。您正在尝试完成两个非常不同的任务,那么为什么要尝试将它们合并到同一个迭代块中呢?后者(处理最后一项)甚至不应该是迭代。

    为了便于阅读,我建议将两者分开。它更有意义,并且不需要您检查是否处于迭代的最后一个循环中(这样可以节省一些代码和嵌套)。


    虽然我不太了解计算本身,但我可以回答您直接要求的算法(虽然我不确定这是最好的方法,我稍后会解决在答案中)。

    var allItemsExceptTheLastOne = allItems.Take(allItems.Count() - 1);
    
    foreach(var item in allItemsExceptTheLastOne)
    {
        // Your logic for all items except the last one
    }
    
    var theLastItem = allItems.Last();
    
    // Your logic for the last item
    

    在我看来,这是一种更简洁、更易读的方法。我不喜欢将 lambda 方法用作可读性不那么微不足道的迷你方法。这可能是主观的,并且与个人风格有关。


    在重读时,我认为我对计算的理解更好,因此我添加了实现它的尝试,同时仍尽我所能最大限度地提高可读性:

    // First we make a list of the values (without the sum)
    
    var myValues = Enumerable
                        .Range(0, periods)
                        .Select(period => this.CalculateValue(period))
                        .Select(period => period - this.CommercialRound(period))
                        .ToList();
    
    // myValues = [ 0.1, 0.2, 0.3 ]
    
    myValues.Add(myValues.Sum());
    
    // myValues = [ 0.1, 0.2, 0.3, 0.6 ] 
    

    这遵循与我最初建议的算法相同的方法:迭代可迭代项,然后分别处理预期结果列表的最后一个值。

    请注意,我将逻辑分成两个后续的 Select 语句,因为我认为它是最易读(没有过多的 lambda 主体)和最有效(没有重复的 CalculateValue 调用)的方式。但是,如果您更关心性能,例如当您期望处理大量列表时,您可能希望再次合并这些列表。

    我建议您始终尝试默认编写有利于可读性而不是(过度)优化的代码;并且仅在明确需要额外优化时才偏离该路径(我无法根据您的问题做出决定)。


    在第二次重读时,我不确定您对实际计算的解释是否足够好,因为您的计算中实际上并未使用 cumulatedRoundDifference,但代码似乎表明它的值应该很重要到最后结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多