F#序列比较

Wei*_* Ma 10 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 upperLimit) |> Seq.append [0;1] 
Run Code Online (Sandbox Code Playgroud)

我的测试代码是

[<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
Run Code Online (Sandbox Code Playgroud)

测试失败,打印结果为

预期:[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;;
Run Code Online (Sandbox Code Playgroud)

我得到的结果是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
Run Code Online (Sandbox Code Playgroud)

它现在起作用了.谢谢!但我仍然不明白为什么一个简单的seq [1; 2; 3] = seq [1; 2; 3]有效,但我的测试用例没有.

Gen*_*ski 14

添加到John的答案:可以使用Seq.compareWith库函数确定序列相等性:

let compareSequences = Seq.compareWith Operators.compare
Run Code Online (Sandbox Code Playgroud)

那么序列相等就是表达式的值

let expectedSameAsResult = (compareSequences expected result = 0)
Run Code Online (Sandbox Code Playgroud)


Joh*_*mer 12

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

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

seq {1..3} = seq {1..3}
Run Code Online (Sandbox Code Playgroud)

返回false.

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

seq [1;2;3] = seq [1;2;3]
Run Code Online (Sandbox Code Playgroud)

返回true,这很令人困惑.

要避免此问题,您需要执行类似的操作

 let test a b = Seq.fold (&&) true (Seq.zip a b |> Seq.map (fun (aa,bb) -> aa=bb))
Run Code Online (Sandbox Code Playgroud)

比较元素.

或者,您可以Seq.compareWith按照Gene的答案中所述使用.然而,这要求元素也实现比较运算符以及相等性,对于诸如实现=但不是比较的区分联合的某些事情可能不是这种情况.


des*_*sco 5

MSDN for Operators.seq <'T>函数说:使用序列表达式语法构建序列。如果查看其实现,您会发现它基本上只是身份函数,仅当与序列表达式语法一起使用时,它才对编译器具有特殊含义。如果使用list进行呼叫-您将获得相同的列表(向上转换为seq <_>)。

按照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) = ...
Run Code Online (Sandbox Code Playgroud)

以下声明可进行结构比较:

interface System.IComparable with 
    member x.CompareTo(y:obj) = ...
interface System.Collections.IStructuralComparable with 
    member x.CompareTo(yobj: obj, comparer: System.Collections.IComparer) = ...
Run Code Online (Sandbox Code Playgroud)

对于异常类型,将生成用于结构相等性和哈希的隐式声明,但不会生成用于结构比较的声明。绝对不会为接口,委托,类或枚举类型生成隐式声明。枚举类型通过其作为整数的基础表示形式隐式地获得对相等性,哈希和比较的支持

因此,列表(本质上是并集)-支持结构相等和顺序-不支持。要成对检查元素,您也可以使用Seq.forall2

let isEqual = (s1, s2) ||> Seq.forall2 (=)
Run Code Online (Sandbox Code Playgroud)


Ric*_*ban 5

对于找到这个答案的任何人,如果只是想要一种简单的方法来比较两个序列,可以使用 Seq.compareWith 的替代方法:

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

用法示例:

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

Enumerable.SequenceEqual (sequenceA, sequenceB) // True
Run Code Online (Sandbox Code Playgroud)