F#比较Lambda表达式的相等性。

3

我想尝试并比较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#编译器(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)

这表明如果它们可以串行化,就可以进行比较。然而,对于这个示例的字节流的检查显示出现了两个不同的字节。

enter image description here

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


2
两个函数相等是什么意思?例如,你是否认为函数 fun x y -> x + yfun x y -> y + x 相等? - Mark Pattison
正在https://unisonweb.org建立一种基于代码具有规范表示意义的有趣语言。 - Asti
1个回答

6

从等价性的角度来看,函数没有实际上被序列化。

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是我们派生类的名称。

lalb的字节流差异在于实现类的名称。 lalb的实现可以完全不同。

例如,您可以将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

F#也支持Expression<Func<>>(C#的表达式树),这也是进行比较的有效途径。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接