我已经实现了斐波那契序列生成器,如下所示
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
测试失败,打印结果为
预期:[0; 1; 1; 2; 3; 5; 8; 13]结果:seq [0; 1; 1; 2; ...]结果长度:8
当我使用for循环打印结果中的每个元素时,我在预期的序列中得到完全相同的元素。
那么,期望序列和结果序列之间有什么区别?
编辑:我的实施可以在https://github.com/weima/EulerProblems/tree/master/EulerProblems
找到编辑:回答John Palmer的回答 我刚刚在F#交互式窗口中编写了一个测试
let a = seq[1;2;3]
let b = seq[1;2;3]
let c = a = b;;
我得到的结果是 val a:seq = [1; 2; 3] val b: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
现在它奏效了。谢谢!但我仍然不明白为什么一个简单的seq [1; 2; 3] = seq [1; 2; 3]有效,但我的测试用例没有。
答案 0 :(得分:12)
添加到John的答案:可以使用Seq.compareWith
库函数确定序列相等性:
let compareSequences = Seq.compareWith Operators.compare
然后序列相等就是表达式的值
let expectedSameAsResult = (compareSequences expected result = 0)
答案 1 :(得分:10)
虽然您可能希望a=b
比较序列的元素,但它实际上a=b
计算引用相等。
你可以用
之类的东西看到这个seq {1..3} = seq {1..3}
返回false。
但是,在某些情况下,当您使用常量时,您会得到令人困惑的结果,特别是
seq [1;2;3] = seq [1;2;3]
返回true,这令人困惑。
要避免此问题,您需要执行类似
的操作 let test a b = Seq.fold (&&) true (Seq.zip a b |> Seq.map (fun (aa,bb) -> aa=bb))
比较元素。
或者,您可以使用Gene的答案中概述的Seq.compareWith
。但是,这要求元素还实现比较运算符和相等运算符,对于某些事情(如实现=
而不是比较的歧视联盟)可能不是这种情况。
答案 2 :(得分:4)
Operators.seq<'T>函数的MSDN说:使用序列表达式语法构建序列。 如果你研究its implementation,你会发现它基本上只是身份函数,只有在与序列表达式语法一起使用时才对编译器有特殊意义。如果您使用列表调用 - 您将获得相同的列表(升级到seq&lt; _&gt;)。
结构平等,按照F#规范:
默认情况下,记录,联合和结构类型定义 - 称为结构类型 - 隐式包含 编译器生成的结构相等,散列和比较的声明。这些隐式声明 由结构相等和散列组成:
override x.GetHashCode() = ...
override x.Equals(y:obj) = ...
interface System.Collections.IStructuralEquatable with
member x.Equals(yobj: obj, comparer: System.Collections.IEqualityComparer) = ...
member x.GetHashCode(comparer: System.IEqualityComparer) = ...
以下声明启用结构比较:
interface System.IComparable with
member x.CompareTo(y:obj) = ...
interface System.Collections.IStructuralComparable with
member x.CompareTo(yobj: obj, comparer: System.Collections.IComparer) = ...
对于异常类型,会生成结构相等和哈希的隐式声明,但不会生成结构比较的声明。永远不会为接口,委托,类或枚举类型生成隐式声明。枚举类型通过其基础表示形式隐式派生对相等,散列和比较的支持
所以列表(基本上是联合) - 支持结构相等和序列 - 不是。要成对检查元素,您还可以使用Seq.forall2
let isEqual = (s1, s2) ||> Seq.forall2 (=)
答案 3 :(得分:1)
序列不支持结构相等。
如果您将seq
视为.NET IEnumerable<T>
或许这更有意义?这里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>
在此使用list
代替seq
。
[<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)
参见其他答案。
答案 4 :(得分:1)
对于发现这个答案的人来说,只想要一种比较两个序列的简单方法,可以选择Seq.compareWith:
有一种.NET方法Enumerable.SequenceEqual
。我在测试时一直使用它。
使用示例:
let sequenceA = seq { 1..5 }
let sequenceB = seq { 1..5 }
Enumerable.SequenceEqual (sequenceA, sequenceB) // True