在F#中:
> let f x = x + 2;;
val f : int -> int
> let g x = f x;;
val g : int -> int
> g 10;;
val it : int = 12
> let f x = x + 3;;
val f : int -> int
> g 10;;
val it : int = 12
在Clojure中:
1:1 user=> (defn f [x] (+ x 2))
#'user/f
1:2 user=> (defn g [x] (f x))
#'user/g
1:3 user=> (g 10)
12
1:4 user=> (defn f [x] (+ x 3))
#'user/f
1:5 user=> (g 10)
13
请注意,在Clojure中,f的最新版本将在最后一行中调用。在F#中,仍然会调用旧版本的f。为什么会这样,这是如何工作的?
答案 0 :(得分:12)
在Clojure中,f
符号捕获名称f
,而在F#中,f
符号捕获f
的值。所以在Clojure每次调用g
时,它会查找f
以查找当时名称所指的内容,而在F#中,每次调用g
都会使用{{1}的值在最初创建f
函数时使用。
答案 1 :(得分:8)
正如 gabe 所说,当您输入名称已存在的函数时,F#interactive使用 shadowing 值(有关阴影的更多信息,请参阅示例{{ 3}})。这意味着当您运行代码时,F#编译器会看到类似的内容:
> let f@1 x = x + 2;;
> let g@1 x = f@1 x;;
> g@1 10;;
val it : int = 12
> let f@2 x = x + 3;;
> g@1 10;;
val it : int = 12
F#使用一些错误的名称(如@),您不能直接使用它来区分值的版本。另一方面,Clojure的行为可能最好被理解为功能的大词典。使用伪语法,如下所示:
> symbols[f] = fun x -> x + 2;;
> symbols[g] = fun x -> symbols[f] x;;
> symbols[g] 10;;
val it : int = 12
> symbols[f] = fun x -> x + 3;;
> symbols[g] 10;;
val it : int = 13
这应该使区分非常明确。
作为旁注,Clojure方法存在一个可能的问题(至少对于像F#这样的语言)。您可以声明某种类型的函数,使用它然后,下一个命令可以更改函数的类型。如果F#使用了Clojure方法,那么以下示例应如何工作?
> let f a b = a + b;;
> let g x = f x x;;
> let f () = printf "f!";;
> g 0;;
函数g
使用f
,好像它有两个int
类型的参数,但是thrid行更改了函数的类型。这使得Clojure方法对于类型检查语言来说有点棘手。
答案 2 :(得分:5)
Gabe和Tomas已经很好地介绍了基础知识。请注意,如果您希望F#的行为与Clojure相同,则可以使用可变绑定并重新分配f
:
let mutable f = fun x -> x + 2
let g x = f x
g 10;; // 12
f <- fun x -> x + 3 // note, assign new value, don't create new binding
g 10;; //13