Clojure线程宏

时间:2016-05-06 01:04:35

标签: clojure

我不确定这是发布的最佳位置,但为什么不能发挥作用?线程宏没有将seq的结果传递给(map str)吗?

;; 1
(map str (seq (str (* 8 8)))) -> ("6" "4")

;; 2
(defn a [x y]
  (-> (* x y)
      str
      seq
      (map str)))


(a 8 8) -> Don't know how to create ISeq from: clojure.core$str

4 个答案:

答案 0 :(得分:6)

您正在使用thread-first宏,它将每个表单作为下一个项目的第二项插入,如下所示:

(-> (* x y) str seq (map str))
(-> (str (* x y)) seq (map str))
(-> (seq (str (* x y))) (map str))
(map (seq (str (* x y))) str)

你想要的是thread-last宏:

(defn a [x y]
  (->> (* x y) str seq (map str)))

(a 8 8) ;=> ("6" "4")

答案 1 :(得分:4)

正如Elogent所指出的那样,宏将参数置于错误的位置。通常在处理宏(尤其是编写它们)时,有助于了解macroexpand-1并将其与clojure.pprint/pprint结合起来,以清楚地了解实际运行的内容:

user> (clojure.pprint/pprint
       (macroexpand-1
        '(-> (* x y)
             str
             seq
             (map str))))
(map (seq (str (* x y))) str)
nil

我们可以看到它看起来并不合适。接下来我们摆弄它,直到它扩展到我们的期望:

user> (clojure.pprint/pprint
       (macroexpand-1
        '(->> (* x y)
              str
              seq
              (map str))))
(map str (seq (str (* x y))))
nil

有各种各样的线程宏可以帮助解决这类问题,尤其是as->,它允许您在需要使用第一个和最后一个参数进行交替的线程函数时为线程值指定一个显式名称。重要的输入:

user> (as-> (* 7 42) x
        (str x)
        (seq x)
        (map str x))
("2" "9" "4")

答案 2 :(得分:1)

这是一个很好的选择(从右到左阅读):

(defn a [x y]
  ((comp (partial map str) seq str *)
    x y))

这可能在您要将要应用的转换与应用的数据分开的上下文中有用(我欢迎批评该主题)。

答案 3 :(得分:1)

您也可以考虑使用gradle_opts中的it->线程表单,该表单旨在避免线程优先与线程最后的担忧。请考虑以下代码:

(ns clj.core
  (:use tupelo.core))

(defn a [x y]
  (it-> (* x y)
      (str it)
      (seq it)
      (map str it)))

(spyx (a 8 8))

(defn -main [] )

产生:

(a 8 8) => ("6" "4")

it->宏使用占位符it来明确选择每个连续表单的结果放置位置。这是一种简化代码并避免此类错误的简便方法。