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

Clojure 定义一个将两个s表达式串在一起的函数,clojure,functional-programming,Clojure,Functional Programming,如果我跑 (defn c [a b] (-> a b)) (c (+ 1 2 3) (partial * 4)) 它返回(*4(+1 2 3))的结果,即24 假设我想调用c,而不使用第二个s表达式的partial,即 (c (+ 1 2 3) (* 4)) 并得到相同的结果24 (defn c…)看起来像什么?您需要的是一个函数,它接受代码并返回代码。例如,您需要此代码 (c (+ 1 2 3) (* 4)) 与本规范等效: (-> (+

如果我跑

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

(c (+ 1 2 3)
   (partial * 4))
它返回
(*4(+1 2 3))
的结果,即
24

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

(c (+ 1 2 3)
   (* 4))
并得到相同的结果
24

(defn c…)
看起来像什么?

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

(c (+ 1 2 3)
   (* 4))
与本规范等效:

(-> (+ 1 2 3)
    (* 4))
您可以使用Clojure编写
c
宏,如下所示:

(defmacro c [a b]
  `(-> ~a ~b))
然后,您可以使用以下函数测试宏:

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

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

在进行函数调用之前,clojure函数的参数将被计算为值。如果您希望传入一个值,该值在您要传递给它的函数中起作用,那么函数是一个很好的选择始终将值优先于函数,将函数优先于宏。因为一个值在这里没有用处,
(*4)
的计算结果只是
4

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

(defn c' [initial-value function-to-call]
    (-> initial-value
        function-to-call)
如果我删除thread first宏,它会变得更清晰:

(defn c'' [initial-value function-to-call]
   (function-to-call initial-value))
所以我们可以看到,第二个参数必须是一个函数,才能用值调用它。如果它作为尚未编译成函数的s表达式传递,则需要先将其编译成函数,然后才能使用值运行

你可以通过几种方式来实现这一点,从最好的(几乎可以肯定你在任何情况下都应该这样做)到非常糟糕的(我从来没有看到过有理由在七年的时间里以写Clojure为生):

  • 只需使用现有的
    ->
    宏(您可能有理由不这样做,因为您提出了要求)
  • 向它传递一个函数,例如
    #(*%4)
    ,或者像现在一样使用partial
  • 让它成为一个宏
  • 使用eval在函数中运行表达式(不太可能)
  • 我真的支持选项一,它可以满足您的所有要求,每个阅读您代码的人都已经知道它的功能

    选项二是一个非常接近的第二个选项,因为每个阅读它的人都会很快发现它的作用


    选项三是非常遥远的第三个选项,因为它意味着你不能将它与map、reduce等函数一起使用,每个人都必须花时间弄清楚它的功能。

    它需要看起来像一个宏。非常感谢Sam。我想知道我最后何时才能开始编写宏…@monch1962好吧,在这种特殊情况下,您仍然不需要编写任何宏,因为这里的
    c
    只提供了
    ->
    功能的一个子集。您不太可能真正需要在这里使用宏。“或者至少你应该先努力找到另一条路。”阿瑟鲁费尔德同意。当你需要宏时,宏是很棒的,但这比你想象的要少;尽可能避免编写宏。请不要让宏成为调用函数的唯一方式,我写clojure的时间越长,每年编写的宏就越少(ps:2016年为零)