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
看作.NETIEnumerable
,这可能更有意义?这里的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
被重写以实现结构相等,但对于其他类型的序列,实现可能会依赖于引用相等的默认值(或执行其他操作)。编译器未按预期操作会影响您-trya=(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