这是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似乎已再次执行。我不明白,我认为函数式编程的目的是避免副作用。为什么我会出现这种行为?
答案 0 :(得分:2)
原因是语句let vals = Seq.sort values
实际上并没有对值进行排序,直到某些代码消耗了vals变量,即Seq.fold在toString方法中的作用,它使用了vals序列并且当时排序发生了,那时值数组中的值是什么,这些值是排序的,所以基本上排序是在你调用toString方法时发生的。
此外,我不会将其称为FP :),因为您基本上通过创建具有私有状态的类型来执行OOP,并且类型成员可以访问该状态。
您的问题与序列如何工作有关,而不是一般适用于FP。
答案 1 :(得分:1)
F#Seq.sort是否返回输入序列的副本?
是。它还能做什么 - 改变你需要复制的一组值类型的顺序(在所有.NET语言中都是如此)。
(这包括C#和VB中的LINQ运算符:懒惰的方面是只在需要第一个复制元素时才进行复制,此时会创建一个完整的新集合。)
答案 2 :(得分:0)
您实际上可以直接在f#here的源代码中检查这一点,但简而言之,它的作用是调用Seq.toArray,对数组进行排序并将该数组作为序列返回。