Clojure:将comp和部分参数分开

Clojure: separating comp and partial arguments

说我有一个需要两个参数的函数,参数的顺序会影响结果。

是否可以将第一个参数传递给部分函数或comp函数,而将另一个传递给它,像这样:

1
2
3
4
5
6
(defn bar [arg1 arg2] (- arg1 arg2))
(def baz (partial (bar arg1)))
(def qux (comp str (bar arg1)))

(baz arg2)
(qux arg2)

如果我想将arg2传递给函数,可以做这样的事情吗?

1
2
3
4
5
(def baz2 (partial (bar _ arg2)))
(def qux2 (comp str (bar _ arg2)))

(baz arg1)
(qux arg1)

partial仅在左侧"填充"参数,因此,如果需要跳过参数,则必须使用fn

1
(def baz2 #(bar %1 arg2))

还请注意,comp要求其所有参数均为函数,因此您的quxqux2实际上是废话。它们应该是:

1
2
(def qux (comp str baz))
(def qux2 (comp str baz2))

通常,Clojure核心函数会将最可能改变的变量置于最后,以使具有comppartial的合成更加自然。 (例如,集合参数几乎总是在Clojure中排在最后,除了into之类的东西放在首位是有意义的。)在设计自己的函数时,应遵循此约定,以便可以更轻松地编写函数。


方案SRFI 26具有一个有用的宏cut,用于分配这样的参数。

用法就像您减去bar

1
2
3
4
 ((cut bar <> 1) 2)
 ;=> 1
 ((comp str (cut - <> 1)) 2)
 ;=>"1"

<>符号表示要填充的插槽。

在Clojure中自己实现cut是一个有趣的练习,但这是@Baishampayan Ghose提供的一个端口。


这是我得到的代表:

1
2
3
4
5
6
7
8
9
10
user=> (defn bar [arg1 arg2] (- arg1 arg2))
#'user/bar
user=> (def baz (partial bar 5))
#'user/baz
user=> (def qux (comp str baz))
#'user/qux
user=> (baz 2)
3
user=> (qux 2)
"3"

这是您从第一个示例中得到的最接近的结果。

对于第二个示例,也许简单的defn优于部分def:

1
2
3
4
5
6
7
8
user=> (defn baz2 [x] (bar x 5))
#'user/baz2
user=> (def qux2 (comp str baz2))
#'user/qux2
user=> (baz2 2)
-3
user=> (qux2 2)
"-3"

我建议您开始一个repl并自己尝试,这是发现我所知道的语言的第二个最佳方法(TDD是第一个)。

干杯!