F# 比较 lambda 是否相等
F# comparing lambdas for equality
我想尝试比较 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)
产生错误
The type '('a -> 'b -> int)' does not support the 'equality' constraint because it is a function typeF# 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)
这表明如果它们可以序列化,它们就可以进行比较。但是,检查此示例的字节流显示存在差异的两个字节。
是否有可能通过智能比较字节数组来解决这个问题的策略?
从等价的角度来看,函数没有进行有意义的序列化。
F# 中的 Curryable 函数实现为派生自 FSharpFunc
.
let la = (fun x y -> x + y + foo)
将作为以下 class 的实例实现(在等效的 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
是我们派生的名称 class.
la
和 lb
的字节流不同之处在于实现 class 的名称。 la
和 lb
的实现可能完全不同。
例如,您可以将 lb
更改为 let lb = (fun x y -> x * y + foo)
,两次运行的结果将相同。
但是,您可以使用 Code Quotations:
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
F# 还支持 Expression<Func<>>
(C# 的表达式树)——这也是比较的有效途径。
我想尝试比较 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)
产生错误
The type '('a -> 'b -> int)' does not support the 'equality' constraint because it is a function typeF# 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)
这表明如果它们可以序列化,它们就可以进行比较。但是,检查此示例的字节流显示存在差异的两个字节。
是否有可能通过智能比较字节数组来解决这个问题的策略?
从等价的角度来看,函数没有进行有意义的序列化。
F# 中的 Curryable 函数实现为派生自 FSharpFunc
.
let la = (fun x y -> x + y + foo)
将作为以下 class 的实例实现(在等效的 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
是我们派生的名称 class.
la
和 lb
的字节流不同之处在于实现 class 的名称。 la
和 lb
的实现可能完全不同。
例如,您可以将 lb
更改为 let lb = (fun x y -> x * y + foo)
,两次运行的结果将相同。
但是,您可以使用 Code Quotations:
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
F# 还支持 Expression<Func<>>
(C# 的表达式树)——这也是比较的有效途径。