F# Seq.sort 是否返回输入序列的副本?
以下是 F# 中的一些(我自己)意想不到的行为。我有一个对序列进行排序的简单类:
type MyQueue<'a when 'a : comparison> ( values : 'a[] ) =
let vals =
Seq.sort values
member this.First = Seq.nth 0 vals
override this.ToString() =
Seq.fold ( fun s a -> s + a.ToString() + ";" ) "" vals
我编写了一个稍微人为的单元测试(在 C# 中)来测试这一点:
private class TestObject : IComparable
{
public TestObject( double Value )
{
this.Value = Value;
}
public void Update(double NewValue)
{
this.Value = NewValue;
}
public double Value { get ; private set; }
public int CompareTo(object Comparable)
{
return this.Value.CompareTo( (Comparable as TestObject).Value );
}
public override string ToString ()
{
return Value.ToString();
}
}
[Test]
public void TestUpdate_OK()
{
var nums = new double[]{7,4,3,12,11,3,8};
var values = nums.Select( n => new TestObject(n) ).ToArray();
var q = new MyQueue<TestObject>( values );
Console.WriteLine ( q.ToString() );
// update one of the values in the collection - should not re-sort the collection
values[3].Update( 2.0 );
Console.WriteLine ( q.ToString() );
Assert.AreEqual( q.First.Value, 3.0 );
}
Seq.sort 确实对序列进行了排序,并且第一个输出是正确的:
3;3;4;7;8;11;12;
但是,更新测试(引用类型)对象会导致序列重新排序:
2;3;3;4;7;8;11;
我预计 MyQueue 对象中的 val 现在不会排序,因为引用对象中的值已更改,但 Seq.sort 似乎已再次执行。我不明白,我认为函数式编程的目的是避免副作用。为什么我会出现这种行为?
Here is some unexpected (by me) behaviour in F#. I have a simple class that sorts a sequence :
type MyQueue<'a when 'a : comparison> ( values : 'a[] ) =
let vals =
Seq.sort values
member this.First = Seq.nth 0 vals
override this.ToString() =
Seq.fold ( fun s a -> s + a.ToString() + ";" ) "" vals
I have written a slightly contrived unit test (in C#) to test this:
private class TestObject : IComparable
{
public TestObject( double Value )
{
this.Value = Value;
}
public void Update(double NewValue)
{
this.Value = NewValue;
}
public double Value { get ; private set; }
public int CompareTo(object Comparable)
{
return this.Value.CompareTo( (Comparable as TestObject).Value );
}
public override string ToString ()
{
return Value.ToString();
}
}
[Test]
public void TestUpdate_OK()
{
var nums = new double[]{7,4,3,12,11,3,8};
var values = nums.Select( n => new TestObject(n) ).ToArray();
var q = new MyQueue<TestObject>( values );
Console.WriteLine ( q.ToString() );
// update one of the values in the collection - should not re-sort the collection
values[3].Update( 2.0 );
Console.WriteLine ( q.ToString() );
Assert.AreEqual( q.First.Value, 3.0 );
}
the Seq.sort does sort the sequence, and the first output is correct :
3;3;4;7;8;11;12;
However, updating the test (reference type) object causes the sequence to be re-sorted :
2;3;3;4;7;8;11;
I expected that the vals in the MyQueue object would now be unsorted, since the value in the reference object has changed, but the Seq.sort appears to have been performed again. I don't understand, I thought the object of functional programming was to avoid side effects. Why do I get this behaviour?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
造成这种情况的原因是语句
let vals = Seq.sort value
实际上并没有对值进行排序,直到某些代码消耗了 vals 变量,即您的 Seq.fold 在 toString 方法中所做的事情,它消耗了 vals 序列那时排序发生,无论当时值数组中有什么值,这些值都会被排序,所以基本上排序是在调用 toString 方法时发生的。另外,我不会将其称为 FP :),因为您基本上是通过创建具有私有状态的类型来进行 OOP,并且该状态由类型成员访问。
您的问题与序列如何工作有关,并且通常不适用于 FP。
The cause of this is the statement
let vals = Seq.sort values
is not actually sorting the values until some code consumes the vals variable i.e what your Seq.fold does in toString method, it consumes the vals sequence and at that time the sorting happens and whatever values are there in the values array at that time, those values are sorted, so basically the sorting is happening at the time when you call toString method.Also, I won't call it FP :) as you are basically doing OOPs by creating type with private state and that state is accessed by type members.
Your problem is related to how sequences works and not in general applicable to FP.
是的。它还可以做什么 – 更改需要复制的一组值类型的顺序(在所有 .NET 语言中都是如此)。
(这包括 C# 和 VB 中的 LINQ 运算符:惰性方面是仅在需要第一个复制元素时才进行复制,此时会创建一个完整的新集合。)
Yes. What else could it do – to change the order of a set of value types you need to copy (true in all .NET languages).
(This includes LINQ operators in C# and VB: the lazy aspect is that the copy is only made when the first copied element is needed, and at that point a complete new collection is created.)
实际上,您可以直接在 f# 此处 的源代码中检查这一点,但简而言之它的作用是调用 Seq.toArray,对数组进行排序并将该数组作为序列返回。
You can actually check this directly in the sourcecode for f# here but in short what it does is call Seq.toArray, sort the array in place and return that array back as the sequence.