如何使用 Linq 计算可为 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;
}
是否有更好的方法使用 Linq 来完成此任务? 该库正在使用 .Net 3.5。 现在我无法使用 Zip,因为它是 .Net 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 时,我会回来,这样我就可以尝试这两个答案中建议的方法。
谢谢!
I need to calculate changes off of time series of nullable numbers. The following code gets the job done:
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;
}
Is there a better way to accomplish that with Linq?
The library is using .Net 3.5.
Right now I cannot use Zip, because that comes with .Net 4.
Edit: following the advice by mquander and Eric Lippert, I have come up with the following code which runs on 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;
}
}
}
}
I am using this code as follows:
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);
}
Any suggestions on further improvement are welcome.
I will be back when I have VS2010 and .Net 4, so that I can try out the approaches suggested in both answers.
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
也许只是
?
顺便说一句,我只会使用双精度和 double.NaN 而不是空值。这样,代码就可以简化为仅
在这个地方,也许还有其他一些地方。
编辑:
遵循 @Eric Lippert 的建议,即使对于
Nullable
情况,也可以删除 null 检查。所以即使在这种情况下,答案也很简单。
Maybe just
?
By the way, I would use just doubles and double.NaN instead of nulls. This way the code can be simplified to just
in this and perhaps some other places.
EDIT:
Following @Eric Lippert's advice, removing null checks is possible even for
Nullable
case. So the answer would be simplyeven in this case.
另一个想法(受到这个答案的启发)是将前一项保留在捕获的变量中:
这必须有效,因为捕获的变量“隐藏”在函数中。
Another idea (inspired by this answer) would be to keep the previous item in a captured variable:
This must work because the captured variable is "hidden" in the function.
并不真地。我会按照你的方式去做。如果您觉得功能特别强大,那么最好的方法是在 IEnumerable上定义一个
Pairs
方法,将序列分成一系列连续的重叠对,然后将每对映射到其第一个值和第二个值之间的增量。编辑,因为请求了一个示例:
然后
GetChanges
减少为:(请注意,如果
values
包含少于两个值,我的实现将返回一个空序列,而不是抛出异常。)(再次编辑——最后清理了可空类型处理,感谢埃里克指出!)
Not really. I would do it your way. If you're feeling particularly functional, the way to go would be to define a
Pairs
method onIEnumerable<T>
that breaks a sequence into a series of consecutive overlapping pairs, and then map each pair to the delta between its first and second value.EDIT since an example was requested:
Then
GetChanges
is reduced to:(Note that my implementation returns an empty sequence instead of throwing an exception if
values
contains less than two values.)(edit again -- cleaned up the nullable type handling at the end, thanks Eric for pointing it out!)