5

如果我执行以下操作:

user=> (-> ["1" "2"] (partial apply str)) 
#<core$partial__5034$fn__5040 clojure.core$partial__5034$fn__5040@d4dd758>

...我得到了部分功能。但是,如果我将它绑定到一个变量:

user=> (def apply-str (partial apply str))
#'user/apply-str
user=> (-> ["1" "2" "3"] apply-str)       
"123"

...代码按我的预期工作。我会假设它们是同一件事,但显然情况并非如此。有人可以解释为什么这对我来说吗?

4

5 回答 5

6

-> 是一个宏,因此它不必遵循您在应用方面所期望的规则。宏在计算表单之前转换源。尝试对表单进行宏扩展:

user> (macroexpand '(-> ["1" "2"] (partial apply str)))
(partial ["1" "2"] apply str)

你想通过使用'->'宏在这里实现什么?

编辑:请注意:

user> ((partial apply str) ["1" "2"])
"12"
于 2010-04-05T02:49:44.147 回答
5

你根本不必这样做。

(->> ["1" "2" "3"] (apply str))

为什么不这样做呢?

于 2010-04-05T11:22:24.917 回答
4

第一个表达式(-> ["1" "2"] (partial apply str))扩展为:

(partial ["1" "2"] apply str)这基本上意味着:

使用 Vars创建一个函数["1" "2"](这也是一个函数,因为向量是索引键的函数!),apply并且str已经作为前两个参数提供。这个函数被打印为奇怪的#<core$partial...>字符串。只有当这个函数被调用时,你才会得到一个 IllegalArgumentException,因为向量只接受一个整数参数,而不是两个 Var 参数。

于 2010-04-05T16:07:27.387 回答
1

->通过表单将 expr 作为第二个参数线程化。在您的情况下,最终扩展为:(partial ["1" "2"] apply str),创建基于向量的 parital 函数。

但是您想在线程 expr 上调用基于 apply 和 str 的 parital 函数,因此需要:

(-> ["1" "2"] ((partial apply str)))

好吧:这段代码我很困惑,而不是惯用的 Clojure。

于 2010-04-05T19:12:48.450 回答
0

->宏在您的第二个版本中添加了括号apply-str,这就是宏扩展为最终调用您的函数的代码的原因。查看源代码->,您可以看到:

(defmacro ->
  "Threads the expr through the forms. Inserts x as the
  second item in the first form, making a list of it if it is not a
  list already. If there are more forms, inserts the first form as the
  second item in second form, etc."
  ([x] x)
  ([x form] (if (seq? form)
              (with-meta `(~(first form) ~x ~@(next form)) (meta form))
              (list form x)))
  ([x form & more] `(-> (-> ~x ~form) ~@more)))

相关部分是当它处理两个参数时,xform. 如果form是一个序列,x则作为该列表中的第二个参数插入。否则,宏会将formx放入列表本身。这样您就可以使用裸符号作为包含一个符号的列表的简写。

user> (macroexpand '(-> 123 (foo)))
(foo 123)
user> (macroexpand '(-> 123 foo))
(foo 123)
于 2010-04-05T17:18:12.793 回答