【发布时间】:2020-03-24 20:14:28
【问题描述】:
在 F# 中,函数不允许有可变参数。但如果我有如下功能:
let f x =
while x>0 do
printfn "%d" x
x <- x-1;;
当我编译这个时,我得到一个编译器错误。(不可变) 我将如何修复此功能?
【问题讨论】:
标签: f#
在 F# 中,函数不允许有可变参数。但如果我有如下功能:
let f x =
while x>0 do
printfn "%d" x
x <- x-1;;
当我编译这个时,我得到一个编译器错误。(不可变) 我将如何修复此功能?
【问题讨论】:
标签: f#
对于传值语义,您可以使用parameter shadowing。
这里,在堆栈上声明了一个阴影(因为它是同名)可变值x。
let f x =
let mutable x = x
while x > 0 do
printfn "%d" x
x <- x - 1
对于传递引用语义,您必须使用reference cell,这只是传递具有可变字段的对象的一种奇特方式。类似于 C# 中的ref:
let f x =
while !x > 0 do
printfn "%d" !x
x := !x - 1
使用参考单元格:
let x = (ref 10)
f x
Debug.Assert(!x = 0)
【讨论】:
你可以这样重写:
let f x = [x.. -1..1] |> List.iter (printfn "%d")
如果你想保留 while 循环,你可以这样做:
let f (x : byref<int>)=
while x>0 do
printfn "%d" x
x <- x-1
let mutable x = 5
f &x
【讨论】:
byref 有一些怪癖,使其难以通用。
取决于您是希望将 x 的最终变异值传递回函数的调用者还是仅在本地对其进行变异。
对于仅本地突变,只需声明另一个变量,该变量将是可变的,但最初将具有 x 的值:
let f x =
let mutable i = x
while i>0 do
printfn "%d" i
i <- i-1
要将结果传递回调用者,您可以使用 ref-cell:
let f (x: int ref) =
while x.Value>0 do
printfn "%d" x.Value
x := x.Value - 1
请注意,现在您必须通过.Value 属性引用参考单元内容(或者您可以改为使用运算符!,如x := !x - 1),现在通过:= 完成突变.另外,这种函数的使用者现在必须在传递它之前创建一个 ref-cell:
let x = ref 5
f x
printfn "%d" x.Value // prints "0"
话虽如此,我必须指出,突变通常比纯值更不可靠,更容易出错。在纯函数式编程中编写“循环”的正常方法是通过递归:
let rec f x =
if x > 0 then
printf "%d" x
f (x-1)
在这里,对f 的每次调用都会对f 进行另一次调用,x 的值会减少1。这将具有与循环相同的效果,但现在没有突变,这意味着更容易调试和测试。
【讨论】:
rec