定义一个将2个s表达式串在一起的函数

时间:2016-10-19 20:33:46

标签: clojure functional-programming

如果我跑

(defn c [a b]
  (-> a
      b))

(c (+ 1 2 3)
   (partial * 4))

它会返回(* 4 (+ 1 2 3)) 24

的结果

假设我想调用c而不使用第二个s表达式的部分,即

(c (+ 1 2 3)
   (* 4))

并获得相同的结果24

(defn c...)会是什么样的?

2 个答案:

答案 0 :(得分:2)

你想要的是macro,它是一个接受代码并返回代码的函数。例如,您需要此代码

(c (+ 1 2 3)
   (* 4))

等同于此代码:

(-> (+ 1 2 3)
    (* 4))

您可以使用Clojure的syntax quote编写c宏,如下所示:

(defmacro c [a b]
  `(-> ~a ~b))

然后,您可以使用macroexpand-1函数测试您的宏:

(macroexpand-1
 '(c (+ 1 2 3)
     (* 4)))
;;=> (clojure.core/-> (+ 1 2 3) (* 4))

成功!

(c (+ 1 2 3)
   (* 4))
;;=> 24

答案 1 :(得分:1)

在进行函数调用之前,将clojure函数的参数计算为值。如果要传递一个值,该函数在您传递给它的函数内部执行某些操作,那么函数是一个很好的选择值。 始终更喜欢功能的值,以及宏的功能。因为此处的值无效(* 4)仅评估为4

您的第一个示例是获取一个值和一个函数,并使用该值调用该函数。为了更清楚,我可以使用不同的变量名重写该示例:

(defn c' [initial-value function-to-call]
    (-> initial-value
        function-to-call)

如果我删除线程的第一个宏,它会变得更清晰:

(defn c'' [initial-value function-to-call]
   (function-to-call initial-value))

所以我们可以看到第二个参数在用值调用之前需要是一个函数。如果它作为一个尚未编译成函数的s表达式传递,则需要先将其作为一个函数,然后才能使用值运行。

你可以通过几种方式实现这一目标,从最好的方式(在几乎每种情况下几乎可以肯定地做到这一点)订购到更糟糕的情况(这是我从未见过的,因为七年来我一直在以Clojure为生):

  1. 只使用现有的->宏(您可能有理由不这样做,因为您在询问)
  2. 传递一个函数,例如#(* % 4)或使用部分
  3. 将其设为宏
  4. 使用eval在函数内运行表达式(这不太可能)
  5. 我真的提倡选项一,它会做你要求的一切,每个阅读你代码的人都会知道它的作用。

    选项二紧随其后,因为阅读它的每个人都会很快弄清楚它的作用。

    选项三是非常遥远的第三,因为它意味着你不能将它用于map,reduce等功能,每个人都必须花时间搞清楚它的作用。