clojure - Clojure 中前向声明的限制是什么?为什么我不能在这个例子中使用 comp?
问题描述
我喜欢我的代码具有“自上而下”的结构,这意味着我想做与 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 符号(例如f
, 和符号指向的(匿名)Clojure var 之间的(主要是隐藏的)交互的更详细答案),即#'f
or 或(var f)
。var 反过来又指向一个值(例如你的 function (fn [x] (* x 3))
.
当您编写类似 的表达式时(f 10)
,有两步间接在起作用。首先,符号f
被“求值”以找到关联的 var,然后 var 被“求值”以找到关联的函数。大多数 Clojure 用户并没有真正意识到这个两步过程的存在,而且几乎所有时候我们都可以假装在符号f
和函数值之间存在直接联系(fn [x] (* x 3))
。
您的原始代码不起作用的具体原因是
(declare f g)
创建 2 个“空”变量。就像(def x)
在符号和空变量之间创建关联一样x
,这就是您declare
所做的。因此,当comp
函数试图从和中提取值时,什么都不存在:变量存在但它们是空的。f
g
附言
上述情况有一个例外。如果您有let
表格或类似表格,则不var
涉及:
(let [x 5
y (* 2 x) ]
y)
;=> 10
在let
表单中,不存在 var。相反,编译器在符号与其关联值之间建立直接联系;即x => 5
和y => 10
。
推荐阅读
- android - 使用 Kotlin 的类实例中的 java.lang.OutOfMemoryError
- javascript - 如何在 Eclipse 中学习 node.js?
- php - 如何使用 AJAX 返回 php 会话变量?
- scala - 从 spark scala shell 将列族添加到现有的 hbase 表
- php - laravel 控制器新方法全局变量
- c++ - 打印优先队列
- r - 8 天 MODIS 栅格到 R 中的月平均值
- php - 如何从 PHP 中的静态函数调用 ngettext()
- reactjs - React 和 requestAnimationFrame 的上下文问题
- ruby-on-rails - Rails 5.2:ActiveRecord::RecordInvalid (声称用户名被占用,虽然它不是)