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)
这表明如果它们可以被序列化,它们就可以进行比较。但是,对本示例的字节流的检查显示了存在差异的两个字节。
有没有可能通过智能比较字节数组来解决这个问题的策略?
从等价的角度来看,函数没有被有意义地序列化。
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# 的表达式树)——这也是一个有效的比较途径。