如何使用Linq计算可为null的数字的变化
本文关键字:null 数字 变化 何使用 Linq 计算 | 更新日期: 2023-09-27 17:58:59
我需要计算可为null的数字的时间序列的变化。以下代码完成了任务:
public static double?[] GetChanges(double?[] x)
{
if(x.Length == 1)
throw new Exception("Time Series Too Short");
var ret = new double?[x.Length - 1];
for (int i = 1; i < x.Length; i++)
{
ret[i-1] = (x[i - 1].HasValue && x[i].HasValue) ? x[i] - x[i - 1] : null;
}
return ret;
}
有没有更好的方法可以和林克一起做到这一点?库正在使用。净额3.5。现在我不能使用Zip,因为它附带了。净4。
编辑:根据mquander和Eric Lippert的建议,我提出了以下在3.5上运行的代码:
public class Tuple<T>
{
public Tuple(T first)
{
First = first;
}
public T First { get; set; }
}
public class Tuple<T, T2> : Tuple<T>
{
public Tuple(T first, T2 second)
: base(first)
{
Second = second;
}
public T2 Second { get; set; }
public static Tuple<T1, T2> New<T1, T2>(T1 t1, T2 t2)
{
return new Tuple<T1, T2>(t1, t2);
}
}
public static class EnumerableExtensions
{
public static IEnumerable<Tuple<T, T>> Pairs<T>(this IEnumerable<T> seq)
{
using (var enumerator = seq.GetEnumerator())
{
enumerator.MoveNext();
var prior = enumerator.Current;
while (enumerator.MoveNext())
{
yield return Tuple<T, T>.New(prior, enumerator.Current);
prior = enumerator.Current;
}
}
}
}
我使用这个代码如下:
public static IEnumerable<double?> GetChanges2(double?[] x)
{
if (x.Length == 1)
throw new Exception("Time Series Too Short");
return x.Pairs().Select(p => p.Second - p.First);
}
欢迎提出进一步改进的建议。当我有VS2010和时,我会回来的。Net 4,这样我就可以尝试两个答案中建议的方法。
谢谢!
也许只是
Enumerable.Zip(
x.Skip(1),
x,
(a, b) => (a.HasValue && b.HasValue) ? (a - b) : null)
)
顺便说一下,我只会用替身和替身。NaN而不是null。这样代码就可以简化为
Enumerable.Zip(x.Skip(1), x, (a, b) => a - b)
在这个地方,也许还有其他一些地方。
编辑:
根据@Eric Lippert的建议,即使在Nullable
的情况下,删除空检查也是可能的。所以答案只是
Enumerable.Zip(x.Skip(1), x, (a, b) => a - b)
即使在这种情况下。
另一个想法(受到这个答案的启发)是将前一项保留在捕获的变量中:
static IEnumerable<double?> GetChanges(IEnumerable<double?> x)
{
double? previous = x.First();
return x.Skip(1).Select(d =>
{ double? result = d - previous; previous = d; return result; });
}
这必须起作用,因为捕获的变量在函数中是"隐藏的"。
不是。我会按照你的方式做。如果你觉得功能特别强大,那么最好在IEnumerable<T>
上定义一个Pairs
方法,将序列分解为一系列连续的重叠对,然后将每个对映射到其第一个值和第二个值之间的delta。
EDIT,因为请求了一个示例:
public static class EnumerableExtensions
{
public static IEnumerable<Tuple<T, T>> Pairs<T>(this IEnumerable<T> seq)
{
using (var enumerator = seq.GetEnumerator())
{
enumerator.MoveNext();
var prior = enumerator.Current;
while (enumerator.MoveNext())
{
yield return Tuple.Create(prior, enumerator.Current);
prior = enumerator.Current;
}
}
}
}
则GetChanges
被简化为:
var changes = values.Pairs().Select(x => x.Item2 - x.Item1);
(请注意,如果values
包含的值少于两个,则我的实现返回一个空序列,而不是抛出异常。)
(再次编辑--最后清理了可为null的类型处理,感谢Eric指出!)