gpt4 book ai didi

f# - 在 F# 中,是否可以将对可变默认值的引用作为参数传递?

转载 作者:行者123 更新时间:2023-12-04 08:05:12 25 4
gpt4 key购买 nike

对于Froto项目(F# 中的 Google Protobuf),我正在尝试使用 'a ref 更新反序列化代码传递值的对象 byref<'a> , 性能。

但是,下面的代码在 hydrator &element field 上失败了。线:

type Field = TypeA | TypeB | Etc

let hydrateRepeated
(hydrator:byref<'a> -> Field -> unit)
(result:byref<'a list>)
(field:Field) =
let mutable element = Unchecked.defaultof<'a>
hydrator &element field
result <- element :: result

error FS0421: The address of the variable 'element' cannot be used at this point



在不更改 hydrator 的签名的情况下,我能做些什么来让这段代码正常工作吗?范围?

我很清楚我可以使用 hydrator:'a ref -> Field -> unit让事情发挥作用。但是,目标是支持反序列化为 record类型而不需要创建一堆 ref每次反序列化记录时堆上的对象。

请注意,以下代码是完全合法的,并且与 hydrator 具有相同的签名。上面的函数声明,所以我不清楚问题是什么。
let assign (result:byref<'a>) (x:'a) =
result <- x

let thisWorks() =
let mutable v = Unchecked.defaultof<int>
assign &v 5
printfn "%A" v

最佳答案

我会尽量澄清我在评论中所说的话。你是对的,你对 assign 的定义是正确的。非常好,它似乎有签名byref<'a> -> 'a -> unit .但是,如果您查看生成的程序集,您会发现它在 .NET 表示级别的编译方式是:

Void assign[a](a ByRef, a)

(也就是说,它是一个接受两个参数并且不返回任何内容的方法,而不是一个接受一个参数并返回一个函数的函数值,该函数接受下一个参数并返回一个 unit 类型的值 - 编译器使用了一些额外的元数据来确定方法的实际声明方式)。

不涉及 byref 的函数定义也是如此。 .例如,假设您有以下定义:
let someFunc (x:int) (y:string) = ()

然后编译器实际上创建了一个带有签名的方法
Void someFunc(Int32, System.String)

当你尝试使用像 someFunc 这样的函数时,编译器足够聪明,可以做正确的事情。作为第一类值 - 如果您在不应用于任何参数的上下文中使用它,编译器将生成 int -> string -> unit 的子类型(在 .NET 表示级别上是 FSharpFunc<int, FSharpFunc<string, unit>>),并且一切都可以无缝运行。

但是,如果您尝试对 assign 做同样的事情, ,它不会工作(或不应该工作,但是有几个编译器错误可能会使某些变体看起来像实际上它们不工作 - 你可能不会收到编译器错误,但你可能会得到一个输出程序集而是格式错误)- .NET 类型实例使用 byref 是不合法的类型作为泛型类型参数,所以 FSharpFunc<int byref, FSharpFunc<int, unit>>不是有效的 .NET 类型。当有 byref 时,F# 表示函数值的基本方式就不起作用了。论据。

因此,解决方法是使用采用 byref 的方法创建您自己的类型。参数,然后创建具有您想要的行为的子类型/实例,有点像手动执行编译器在非 byref 中自动执行的操作。案子。您可以使用命名类型执行此操作
type MyByrefFunc2<'a,'b> =
abstract Invoke : 'a byref * 'b -> unit

let assign = {
new MyByrefFunc2<_,_> with
member this.Invoke(result, x) =
result <- x }

或使用委托(delegate)类型
type MyByrefDelegate2<'a,'b> = delegate of 'a byref * 'b -> unit

let assign = MyByrefDelegate2(fun result x -> result <- x)

请注意,当调用像 Invoke 这样的方法时在委托(delegate)或名义类型上,没有创建实际的元组,因此您不应该担心那里的任何额外开销(它是一个带有两个参数的 .NET 方法,并被编译器视为这样)。虚拟方法调用或委托(delegate)调用是有成本的,但在大多数情况下,在以第一类方式使用函数值时也存在类似的成本。一般来说,如果您担心性能,那么您应该设定一个目标并对其进行衡量,而不是过早地进行优化。

关于f# - 在 F# 中,是否可以将对可变默认值的引用作为参数传递?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35849351/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com