2

我有一个包含日期和值的对象列表。每个日期有一个对象,过去几个月的每个日期都有一个对象。我正在寻找将值更改为最新值的日期。

这是我的意思的一个例子:

<datevalue>
    <date>8-9</date>
    <value>5</value>
</datevalue>
<datevalue>
    <date>8-10</date>
    <value>6</value>
</datevalue>
<datevalue>
    <date>8-11</date>
    <value>5</value>
</datevalue>
<datevalue>
    <date>8-12</date>
    <value>5</value>
</datevalue>
<datevalue>
    <date>8-13</date>
    <value>5</value>
</datevalue>

在上面的示例中,当前值为 5,因为它是最近日期 8-13 上的值。我想返回 8-11 日期值对象,因为它是值更改为最新值的那一天。我不想要 8-9 值,因为即使它是当前值的最早日期,但该值在该日期之后发生了更改。

这是我第一次尝试解决这个问题:

DateValue FindMostRecentValueChange(List<DateValue> dateValues)
{
    var currentValue = dateValues
                        .OrderByDesc(d => d.date)
                        .Select(d => d.value)
                        .First();
    var mostRecentChange = dateValues
                            .OrderByDesc(d => d.date)
                            .TakeWhile(d => d.value = currentValue)
                            .Last();
    return mostRecentChange;
}

这行得通。但是,有人向我指出,我正在为这两个操作重复 OrderByDesc。考虑到 OrderByDesc 可能是一项昂贵的操作,我不想做两次。因此我做了一个改变:

DateValue FindMostRecentValueChange(List<DateValue> dateValues)
{
    var orderedDateValues = dateValues.OrderByDesc(d => d.date);
    var currentValue = orderedDateValues;
                        .Select(d => d.value)
                        .First();
    var mostRecentChange = orderedDateValues
                            .TakeWhile(d => d.value = currentValue)
                            .Last();
    return mostRecentChange;
}

现在我只调用 OrderByDesc 一次。这是一种改进,对吧?好吧,也许不是。OrderByDesc 是延迟执行。

据我了解,这意味着在您向其索取价值之前,实际订购并未完成。因此,当您在查找 currentValue 时调用 First() 时执行 OrderByDesc,然后在查找 mostRecentChange 时调用 Last() 时再次执行它。那么这是否意味着我仍在执行 OrderByDesc 两次?

我是否正确解释了延迟执行的运作方式?我希望编译器能够识别这种情况并在幕后对其进行优化,以便只调用一次执行,但我找不到任何信息来支持这一理论。您能帮我了解优化此解决方案的最佳方法吗?

4

2 回答 2

3

那么这是否意味着我仍在执行 OrderByDesc 两次?

对,那是正确的。

我希望编译器能够识别这种情况并在幕后对其进行优化,以便只调用一次执行,但我找不到任何信息来支持这一理论。

它不能,因为这会在几个关键方面改变预期的功能。

  1. 如果基础数据发生更改,则应在再次迭代序列时反映这些更改。如果您dateValues在第一个查询和第二个查询之间添加了一个新项目,它应该在第二个查询中。如果您删除了一个项目,它不应该在那里,等等。

  2. 为了得到你想要的东西,它需要将所有物品存储在某种集合中,即使在第一个消费者“完成”它们之后也是如此。这是不可取的。这里的想法是您可以流式传输数据,并且一旦您处理完一个项目,您就“完成”了它,并且不需要将它保存在内存中。如果您没有足够的内存来保存查询中的所有项目以供后续运行怎么办?

您能帮我了解优化此解决方案的最佳方法吗?

这很微不足道。只需使用查询结果填充数据结构。最简单的方法就是将它们全部放在一个列表中。在查询的末尾添加一个ToList调用,它将评估一次,然后可以多次迭代结果列表而不会产生负面影响。因为这个解决方案,当需要这种语义时,很容易获得,而延迟执行的语义更难获得,尽管更强大,他们选择不基于物化集合的 LINQ。

于 2013-08-13T19:19:41.710 回答
0

不,如果您使用First()orLast()和其他一些,您的查询将被正确执行。这意味着您调用OrderBy两次(包括OrderByDescending)。

你可以试试这个:

var mostRecentChange = dateValues.OrderBy(d=>d.Date)
                                 .SkipWhile((x,i)=>i==dateValues.Count-1||x.Value == dateValues[i+1].Value)
                                 .Take(1);
于 2013-08-13T19:16:41.013 回答