Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/ssh/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 为什么';t工作:“;defn的第一个参数必须是符号;_Clojure - Fatal编程技术网

Clojure 为什么';t工作:“;defn的第一个参数必须是符号;

Clojure 为什么';t工作:“;defn的第一个参数必须是符号;,clojure,Clojure,为什么会出现错误: IllegalArgumentException First argument to defn must be a symbol clojure.core/defn (core.clj:277) 当我尝试定义这样的函数时: (defn (symbol "f[]") 1) (defn (symbol "f") [] 1) 或者像这样: (defn (symbol "f[]") 1) (defn (symbol "f") [] 1) 为什么这些不等同于下面的直截了当的

为什么会出现错误:

IllegalArgumentException First argument to defn must be a symbol  clojure.core/defn (core.clj:277)
当我尝试定义这样的函数时:

(defn (symbol "f[]") 1)
(defn (symbol "f") [] 1)
或者像这样:

(defn (symbol "f[]") 1)
(defn (symbol "f") [] 1)
为什么这些不等同于下面的直截了当的例子

(defn f [] 1)

我知道这很深奥:但我突然想到,我可能想在某个时候动态命名一个函数。(这里没有真正的用例-只是试图理解Clojure的想法…

当您将参数传递给宏时,它们不会事先进行评估。由于
defn
是一个宏,因此在这两种情况下传递的参数是不等价的。

将参数传递给宏时,它们不会事先进行计算。因为
defn
是一个宏,所以在这两种情况下传递它的内容是不等价的。

您是在混合代码和数据。这是一个非常常见的错误。例如

(+ 4 5) ; ==> 9
('+ 4 5) ; ==> Error
”+
计算为符号。它与变量
+
不同,后者是对函数进行编码和计算的变量。通过评估它们很容易进行检查:

+  ; ==> #<core$_PLUS_ clojure.core$_PLUS_@312aa7c>
'+ ; ==> +
因此,发生的情况是,在代码运行之前,
(取笑“f”1)
被替换为
(clojure.core/defn f[]1)
,运行时永远看不到它来自何处。虽然这看起来很有用,但您仍然不能使用绑定或输入来生成函数:

(def fun-name "f")
(def fun-value 1)
(macroexpand-1 '(make-fun fun-name fun-value))
; ==> (clojure.core/defn fun-name [] fun-value)
宏只是简化和抽象语法的一种方式。如果您总是编写一个类似于
(defn name[&args](let…)的模式
您可以在宏中创建不同绑定的部分,并缩短新宏使用抽象的每个位置。这是一种代码翻译服务。在编译时,参数只是支持替换的文字代码,而且您从一开始就没有机会查看变量或表达式是否具有特定值ly知道代码,但从不知道它们实际代表什么。因此,当最终结果中的代码运行时,通常会出现错误

最后,你可以在运行时使用
eval
做任何事情。在我19年的专业程序员生涯中,我已经两次看到
eval
以合理的方式被使用。你可以做:

(defn make-fun [name value]
  (eval `(defn ~(symbol name) [] ~value)))

(make-fun fun-name fun-value)
; #'user/f
(f)
; ==> 1

现在,虽然这样做是可行的,但你不应该这样做,除非这是一种用于测试或处理代码的工具,而不是作为服务运行的代码的一部分,字符串来自不安全的源。我会选择使用字典,这样你就不会更新自己的环境。想象一下,如果输入是
取笑
或者你的代码的其他部分,让客户控制你的软件。

你在混合代码和数据。这是一个非常常见的错误

(+ 4 5) ; ==> 9
('+ 4 5) ; ==> Error
'+
求值为一个符号。它与变量
+
不同,变量
+
是一个函数的编码和求值。通过求值可以轻松检查:

+  ; ==> #<core$_PLUS_ clojure.core$_PLUS_@312aa7c>
'+ ; ==> +
因此,在代码运行之前(取笑“f”1)被替换为
(clojure.core/defn f[]1)
,运行时永远看不到它来自何处。虽然这似乎很有用,但您仍然无法使用绑定或输入来生成函数:

(def fun-name "f")
(def fun-value 1)
(macroexpand-1 '(make-fun fun-name fun-value))
; ==> (clojure.core/defn fun-name [] fun-value)
宏只是简化和抽象语法的一种方式。如果您总是编写一个类似于
(defn name[&args](let…)的模式
您可以在宏中创建不同绑定的部分,并缩短新宏使用抽象的每个位置。这是一种代码翻译服务。在编译时,参数只是支持替换的文字代码,而且您从一开始就没有机会查看变量或表达式是否具有特定值ly知道代码,但从不知道它们实际代表什么。因此,当最终结果中的代码运行时,通常会出现错误

最后,你可以在运行时使用
eval
做任何事情。在我19年的专业程序员生涯中,我已经两次看到
eval
以合理的方式被使用。你可以做:

(defn make-fun [name value]
  (eval `(defn ~(symbol name) [] ~value)))

(make-fun fun-name fun-value)
; #'user/f
(f)
; ==> 1

现在,虽然这样做是可行的,但你不应该这样做,除非这是一种用于测试或处理代码的工具,而不是作为服务运行的代码的一部分,字符串来自不安全的源。我会选择使用字典,这样你就不会更新自己的环境。想象一下,如果输入是
开玩笑
或者让客户端控制软件的代码的其他部分。

答案是Josh所说的(
defn
是一个宏;如果它是一个函数,那么你的代码真的会以这种方式工作)。您可以定义自己的
defn
变量宏,该宏将执行您想要的操作,或者只使用
eval

(eval `(defn ~(symbol "f") [] 1))
; => #'user/f
(f)
; => 1

答案是Josh所说的(
defn
是一个宏;如果它是一个函数,那么你的代码真的会以这种方式工作)。你可以定义你自己的
defn
变量宏来做你想做的事,或者只使用
eval

(eval `(defn ~(symbol "f") [] 1))
; => #'user/f
(f)
; => 1

您真的不需要使用
eval

您遇到了一个名为“”的问题。一旦您尝试将宏视为函数(例如,可能将其传递给
map
),您会发现,如果不编写另一个宏,您将无法执行此操作。宏#2等也是如此

因此,你不能像编写函数那样编写宏。这就是一般建议的由来,“当你可以使用函数时,千万不要使用宏。”

在本例中,
defn
是一个宏,因此您别无选择,只能编写另一个宏(
def
的行为方式相同,即使它是一种特殊形式而不是宏)。我们的新宏
dyn defn
从字符串列表中动态创建函数名:

(defn fun-1 [] 1)
(def  fun-2 (fn [] 2))

; (def (symbol (str "fun" "-3")) (fn [] 3))
;   => Exception: First argument to def must be a Symbol

(defmacro dyn-defn
  "Construct a function named dynamically from the supplied strings"
  [name-strs & forms]
  (let [name-sym (symbol (str/join name-strs)) ]
    (spyx name-sym)
    `(defn ~name-sym ~@forms)))

(dyn-defn ["fun" "-3"]
  []
  3)
结果:

*************** Running tests ***************
:reloading (tst.demo.core)

name-sym => fun-3      ; NOTE:  this is evaluated at compile-time

Testing _bootstrap

-------------------------------------
   Clojure 1.9.0    Java 1.8.0_161
-------------------------------------

Testing demo.core

Testing tst.demo.core

(fun-1) => 1         ; NOTE:  these are all evaluated at run-time
(fun-2) => 2
(fun-3) => 3
请注意,函数名是
defn
宏的参数,必须是符号,而不是函数调用


注:
正确,您无法通过查看表单来判断表单是“调用”函数还是宏。