F# 比较 lambdas 的相等性

bra*_*ing 3 lambda serialization f# equality

我想尝试比较 F# lambdas 的相等性。乍一看,这是不可能的。

let foo = 10
let la = (fun x y -> x + y + foo)
let lb = (fun x y -> x + y + foo)
printfn "lambda equals %b" (la = lb)
Run Code Online (Sandbox Code Playgroud)

产生错误

类型 '('a -> 'b -> int)' 不支持 'equality' 约束,因为它是一个函数类型 F# Compiler(1)

然而,令人惊讶的是,可以序列化 lambda 函数。

open System.Runtime.Serialization.Formatters.Binary
open System.IO
let serialize o =
    let bf = BinaryFormatter()
    use ms = new MemoryStream()
    bf.Serialize(ms,o)
    ms.ToArray()

let ByteToHex bytes = 
    bytes 
    |> Array.map (fun (x : byte) -> System.String.Format("{0:X2}", x))
    |> String.concat System.String.Empty

let foo = 10
let la = (fun x y -> x + y + foo)
let lb = (fun x y -> x + y + foo)

let a = serialize la 
let b = serialize lb

printfn "%s" (ByteToHex a)
printfn "%s" (ByteToHex b)
printfn "lambda equals %b" (a = b)
Run Code Online (Sandbox Code Playgroud)

这表明如果它们可以被序列化,它们就可以进行比较。但是,对本示例的字节流的检查显示了存在差异的两个字节。

在此处输入图片说明

有没有可能通过智能比较字节数组来解决这个问题的策略?

Ast*_*sti 6

从等价的角度来看,函数没有被有意义地序列化。

F# 中的Curryable函数是从FSharpFunc.

let la = (fun x y -> x + y + foo)
Run Code Online (Sandbox Code Playgroud)

将作为以下类的实例实现(在等效的 C# 中):

[Serializable] class Impl : FSharpFunc<int, int, int>
{
    public int foo;
    Impl(int foo_) => foo = foo_;

    public override int Invoke(int x, int y) =>
        x + y + _foo;
}
Run Code Online (Sandbox Code Playgroud)

二进制序列化捕获的将是完整的类型名和 foo 的值。事实上,如果我们查看字节流中的字符串,我们会看到:

test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Program+la@28
foo
Run Code Online (Sandbox Code Playgroud)

...在哪里 la@28我们的派生类的名称。

la和的字节流lb不同的是实现类的名称。的实现la,并lb可能是完全不同的。

例如,您可以更改lb为let lb = (fun x y -> x * y + foo),并且两次运行的结果都相同。


但是,您可以使用代码引用来做到这一点:

let foo = 10
let la = <@ fun x y -> x + y + foo @>
let lb = <@ fun x y -> x + y + foo @>

printfn "Is same: %b" (la.ToString() = lb.ToString()) //true
Run Code Online (Sandbox Code Playgroud)

F# 还支持Expression<Func<>>(C# 的表达式树)——这也是一个有效的比较途径。