我想尝试比较F#lambda的相等性。乍一看,这是不可能的。
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)
产生错误
类型'('a->'b-> int)'不支持'平等'约束,因为它是一个函数类型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)
这表明如果可以序列化它们,可以对其进行比较。但是,对于此示例,检查字节流将显示两个字节之间存在差异。
是否有可能通过智能比较字节数组来解决此问题的策略?
从等效性的角度看,函数没有被有意义地序列化。
Curryable F#中的功能是从FSharpFunc
派生的。
let la = (fun x y -> x + y + foo)
将被实现为此类的实例(等效于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;
}
二进制序列化捕获的内容将是完整的类型名和foo的值。实际上,如果我们查看字节流中的字符串,则会看到:
test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Program+la@28
foo
la@28
是派生类的名称。
la
和lb
的字节流不同之处是实现类的名称。la
和lb
的内容可能完全不同。
例如,您可以将lb
更改为let lb = (fun x y -> x * y + foo)
,并且两次运行的结果都是相同的-重要的是它们在代码中的位置。