div*_*ero 5 clojure declare forward-declaration
我喜欢我的代码有一个"自上而下"的结构,这意味着我想要与Clojure中的自然完全相反:函数在使用之前被定义.这应该不是问题,因为理论上declare我理论上我的所有功能都是第一个,然后继续享受生活.但它似乎在实践declare中无法解决每一个问题,我想了解以下代码无法正常工作的原因.
我有两个函数,我想通过组合这两个函数来定义第三个函数.以下三段代码实现了这一点:
1
(defn f [x] (* x 3))
(defn g [x] (+ x 5))
(defn mycomp [x] (f (g x)))
(println (mycomp 10))
2
(defn f [x] (* x 3))
(defn g [x] (+ x 5))
(def mycomp (comp f g))
3
(declare f g)
(defn mycomp [x] (f (g x)))
(defn f [x] (* x 3))
(defn g [x] (+ x 5))
但我真正想写的是
(declare f g)
(def mycomp (comp f g))
(defn f [x] (* x 3))
(defn g [x] (+ x 5))
这给了我
Exception in thread "main" java.lang.IllegalStateException: Attempting to call unbound fn: #'user/g,
这意味着在许多情况下向前声明工作,但仍有一些情况我不能只是declare我的所有函数,并以任何方式和我喜欢的任何顺序编写代码.这个错误的原因是什么?前向声明真正允许我做什么,以及我必须具有已定义的功能的情况是什么,例如comp在这种情况下使用?如何确定定义何时严格必要?
如果您利用Clojure(记录不佳)var行为,您可以实现目标:
(declare f g)
(def mycomp (comp #'f #'g))
(defn f [x] (* x 3))
(defn g [x] (+ x 5))
(mycomp 10) => 45
请注意,语法#'f只是简写(技术上是"读者宏"),可以转换为(var f).所以你可以直接写这个:
(def mycomp (comp (var f) (var g)))
并得到相同的结果.
请查看此答案,以获得更详细的答案,了解Clojure符号(如符号所指的Clojure变量)之间的(主要是隐藏的)交互f,即#'f或者(var f).然后,var会指向一个值(例如您的函数)(fn [x] (* x 3)).
当你编写一个表达式时(f 10),工作中有两步间接.首先,对符号f进行"评估"以找到相关的var,然后对var进行"评估"以找到相关的函数.大多数Clojure用户并不是真的意识到这个两步过程存在,几乎所有的时间我们都可以假装符号f和函数值之间存在直接连接(fn [x] (* x 3)).
原始代码不起作用的具体原因是
(declare f g)
创建2个"空"变量.就像(def x)在符号x和空变量之间创建关联一样,这就是你declare所做的.因此,当comp函数尝试从和中提取值时,不存在任何内容:vars存在但它们是空的.fg
PS
上述情况有例外.如果您有let表格或类似表格,则不var涉及:
(let [x 5
      y (* 2 x) ]
  y)  
;=> 10
在let表单中,没有var存在.相反,编译器在符号及其关联值之间建立直接连接; 即x => 5和y => 10.