F#序列比较

F#序列比较,f#,F#,我实现了一个斐波那契序列生成器,如下所示 let getNext upperLimit current= let (e1, e2) = current let next = e1 + e2 if next > upperLimit then None else Some (next, (e2,next)) let fib upperLimit = (0,1) |> Seq.unfold (getNext upperL

我实现了一个斐波那契序列生成器,如下所示

let getNext upperLimit current= 
        let (e1, e2) = current
        let next = e1 + e2
        if next > upperLimit then None
        else Some (next, (e2,next))

let fib upperLimit = (0,1) |> Seq.unfold (getNext upperLimit) |> Seq.append [0;1] 
我的测试代码是

[<Test>]
member Spec.``fib not exeeding 20 should be 0,1,1,2,3,5,8,13``()=
    let expected = seq [0;1;1;2;3;5;8;13] 
    let result = fib 20
    let expectedSameAsResult = (expected = result)
    printfn "Expected: %A  Result: %A result length: %d" expected result (Seq.length result) 
    Assert.That expectedSameAsResult
我得到的结果是 vala:seq=[1;2;3] valb:seq=[1;2;3] val c:bool=true

所以F#也可以对序列进行结构比较

编辑以反映Gene Belitski的答案 我把考试改为

[<Test>]
member Spec.``fib not exeeding 20 should be 0,1,1,2,3,5,8,13``()=
    let expected = seq [0;1;1;2;3;5;8;13] 
    let result = Problem2.fib 20
    let comparedResult =  Seq.compareWith (fun a b -> a - b) expected result  
    let expectedSameAsResult = (comparedResult = 0)
    Assert.That expectedSameAsResult
[]
成员规范“未超过20的fib应为0,1,1,2,3,5,8,13”()=
设期望值=序号[0;1;1;2;3;5;8;13]
让结果=问题2.fib 20
让comparedResult=Seq.compareWith(乐趣a b->a-b)预期结果
让expectedSameAsResult=(comparedResult=0)
Assert.That expectedSameAsResult

现在它成功了。谢谢但是我仍然不明白为什么一个简单的seq[1;2;3]=seq[1;2;3]可以工作,但是我的测试用例不能

虽然您可能期望
a=b
会比较序列的元素,但实际上
a=b
会计算引用相等

你可以用类似的东西看到这一点

seq {1..3} = seq {1..3}
 let test a b = Seq.fold (&&) true (Seq.zip a b |> Seq.map (fun (aa,bb) -> aa=bb))
返回false

然而,在某些情况下,当您使用常量时,您会得到令人困惑的结果,尤其是

seq [1;2;3] = seq [1;2;3]
返回true,这令人困惑

要避免此问题,您需要执行以下操作

seq {1..3} = seq {1..3}
 let test a b = Seq.fold (&&) true (Seq.zip a b |> Seq.map (fun (aa,bb) -> aa=bb))
在元素方面进行比较


或者,您可以使用Gene答案中概述的
Seq.compareWith
。但是,这要求元素还实现一个比较运算符和相等,这可能不适用于某些情况,例如实现
=
但不实现比较的歧视联合。

添加到John的答案中:序列相等可以通过库函数确定:

let compareSequences = Seq.compareWith Operators.compare
那个么序列相等就是表达式的值

let expectedSameAsResult = (compareSequences expected result = 0)
函数的MSDN说明:使用序列表达式语法构建序列。 如果深入研究,您会发现它基本上只是一个identity函数,它只有在与序列表达式语法一起使用时才对编译器具有特殊意义。如果你用list打电话,你会得到同样的列表(上排到seq)

根据F#规范,重建结构平等性:

默认情况下,称为结构类型的记录、联合和结构类型定义隐式包括 编译器为结构相等、哈希和比较生成声明。这些隐式声明 由以下内容组成,用于结构相等和散列:

以下声明支持结构比较:

对于异常类型,会生成结构相等和哈希的隐式声明,但不会生成结构比较的声明。从不为接口、委托、类或枚举类型生成隐式声明。枚举类型通过其作为整数的底层表示隐式派生对相等、哈希和比较的支持

所以列表(本质上是联合)——支持结构相等和序列——不是。要成对检查元素,还可以使用Seq.forall2

let isEqual = (s1, s2) ||> Seq.forall2 (=)
原因是: 序列不支持结构相等

如果您将
seq
看作.NET
IEnumerable
,这可能更有意义?这里的
seq[1;2;3]=seq[1;2;3]
是一个不幸的巧合。考虑非纯<代码> SEQ < /代码>:

let rnd = System.Random()
let x = seq { yield rnd.Next() }
printfn "x is %A" x
printfn "x is %A" x
结果:

x is seq [372511654]
x is seq [1026368248]

val rnd : System.Random
val x : seq<int>
详情如下:
请参阅其他答案。

对于找到此答案的任何人来说,如果他们只想用一种简单的方法比较两个序列,那么除了Seq.compareWith外,还有一种选择:

有一个.NET方法
可枚举.SequenceEqual
。我在测试时一直使用这个

用法示例:

let sequenceA = seq { 1..5 }
let sequenceB = seq { 1..5 }

Enumerable.SequenceEqual (sequenceA, sequenceB) // True

Don Syme在他的博客中说f#支持“结构性”平等,我认为比较两个序列属于这一类category@WeiMa这适用于
list
但不适用于
Seq
我认为
list
Seq
的差异是由于
list
具有固定的大小,然而,
Seq
可能有一个不确定的大小(您如何比较无限序列?)。序列不是通过引用等式进行比较的。
Equals
方法通常由
=
的一般实现使用(这里有一些细微之处-例如,数组是专门处理的)。对于列表,
Equals
被重写以实现结构相等,但对于其他类型的序列,实现可能会依赖于引用相等的默认值(或执行其他操作)。编译器未按预期操作会影响您-try
a=(b |>Seq.map id)
@WeiMa:为了理解发生了什么,试着用FSI中真正序列的一个稍微不同的简单例子
seq{1..3}=seq{1..3}
-这个表达式等于
false
谢谢,伙计们,我现在知道了。或者使用seq.forall2并不能满足严格结构相等的要求,特别是如果两个序列的长度不同<代码>序号2(=)[1;2;3][1;2;3;4];;val it:bool=true
[<Test>]
member Spec.``fib not exeeding 20 should be 0,1,1,2,3,5,8,13``()=
    let expected = [0;1;1;2;3;5;8;13] 
    let result = fib 20 |> Seq.toList
    let expectedSameAsResult = (expected = result)
let sequenceA = seq { 1..5 }
let sequenceB = seq { 1..5 }

Enumerable.SequenceEqual (sequenceA, sequenceB) // True