F#Seq.sort是否返回输入序列的副本?

时间:2011-08-10 06:39:22

标签: f# functional-programming c#-to-f#

这是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似乎已再次执行。我不明白,我认为函数式编程的目的是避免副作用。为什么我会出现这种行为?

3 个答案:

答案 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,对数组进行排序并将该数组作为序列返回。