【问题标题】:Difference in F# and Clojure when calling redefined functions调用重定义函数时 F# 和 Clojure 的区别
【发布时间】:2010-04-03 08:09:34
【问题描述】:

在 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 的旧版本。为什么会这样?它是如何工作的?

【问题讨论】:

    标签: f# comparison clojure


    【解决方案1】:

    在 Clojure 中,f 符号捕获名称 f,而在 F# 中,f 符号捕获 f 的值。因此,在 Clojure 中,每次调用 g 时,它都会查找 f 以找出当时的名称所指的内容,而在 F# 中,每次调用 g 时都会使用 fg 时的值函数是最初创建的。

    【讨论】:

      【解决方案2】:

      正如 gabe 所说,当您输入一个名称已存在的函数时,F# 交互式使用 shadowing 值(有关阴影的更多信息,请参见例如 @987654321 @)。这意味着当您运行代码时,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 类型的参数,但是第三行改变了函数的类型。这使得 Clojure 方法对于类型检查语言来说有点棘手。

      【讨论】:

      • 您的意思是在较低范围内具有相同名称的变量“阴影”在较高范围内的变量?在 F# 交互中,我们应该将连续的 let 语句读取为嵌套范围吗?这样就可以解释了!在 Clojure 案例中,这不是范围问题,而是真正改变 var f 的根绑定(var 是可变的)。
      • @Michiel - 是的,完全正确。使用 F# 的非轻量级语法,您的示例将是 let f = ... in (let g = ... in (g 10; let f = ... in g 10)),其中新作用域的创建更加明显。
      【解决方案3】:

      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
      

      【讨论】:

        猜你喜欢
        • 2011-08-18
        • 2018-09-20
        • 2016-06-01
        • 2012-02-02
        • 1970-01-01
        • 1970-01-01
        • 2013-07-20
        • 2017-06-29
        • 1970-01-01
        相关资源
        最近更新 更多