请解释以下Clojure代码

6
我在这篇博客文章中找到了以下代码(用于解决硬币找零问题):
(defn change-for [amount]
  (let [denominations [25 10 5 1]
        amounts (reductions #(rem %1 %2) amount denominations)
        coins (map #(int (/ %1 %2)) amounts denominations)]
    (mapcat #(take %1 (repeat %2)) coins denominations)))

我觉得困难的部分是: (reductions #(rem %1 %2) amount denominations).

据我所知,reductions 根据给定的函数逐步计算结果集合。例如: (reductions + [1 2 3]) 得到的结果是 [1 3 6]

1         ; first  element
1 + 2     ; second element
1 + 2 + 3 ; third  element

下一个函数,rem,它用来计算余数,非常容易理解。为了理解代码的其余部分,我尝试了以下方法:
; first try, to see if this call works
; outside the original code (the change-for function)
(reductions #(rem %1 %2) 17 [10 5 1]) ; --> [17 7 2 0]

; tried to use the reductions which takes only one argument
; notice that 17 is now inside the array
(reductions #(rem %1 %2) [17 10 5 1]) ; --> [17 7 2 0]

; further simplified the expression
(reductions rem [17 10 5 1]) ; --> [17 7 2 0]

最后一步是按照这篇博客文章所述删除匿名函数。

在这里,事情变得令人困惑(至少对我来说):rem需要两个参数,当使用数组[17 10 5 1]时,我不知道它们是如何应用的。我尝试了以下调用:

(rem [17 10 5 1]) ; --> gives error
(rem [17 10 5 1] [17 10 5 1]) ; --> also gives error
(rem 17 10) ; --> works, but how do you use it with collections?

请问有人能解释一下,这个rem函数是如何和reductions函数一起工作的吗?
还有一件事我不太明白:那些百分数参数(在#(rem %1 %2)中)是如何应用的?我的意思是它们从哪里来?我尝试以以下方式调用rem,但出现错误:(#(rem %1 %2) 17 [10 5 1])。肯定是reductions函数在背后做了一些工作使其正常运行,对吧?
起初我以为#(rem %1 %2)是一个集合。这些与集合声明方式类似并且可以轻易被Clojure新手误用:
(type #{1 2 3})   ; --> clojure.lang.PersistentHashSet
(type #(1 2 3))   ; --> user$eval12687$fn__12688

有人能给我指一些解释Clojure技巧的网站/书籍/其他资源吗?比如“匿名函数的特殊形式”?大多数资源只介绍最简单的构造(类似于所有其他lisp衍生物),而没有深入探讨Clojure的复杂性。我找到了一个看起来不错的网站(也解释了我上面提到的匿名函数)。还有其他类似的资源吗?
1个回答

5
这是:

(reductions #(rem %1 %2) amount denominations)

等价于:

(reductions rem amount denominations)

而正如您所注意到的,
(reductions function start collection) 

使用function(以start作为减少的第一步的第一个参数)对collection进行减少,并返回中间结果序列。 function必须接受两个参数。

因此,以下操作的结果是:

(reductions function start [1 2 3 4 5]) 

is

((function start 1) (function (function start 1) 2) ...) 
#(rem %1 %2)语法只是定义一个匿名函数的一种简写方式,该函数接受两个参数(%1%2),对它们调用rem并返回结果。
相当于以下代码:
(fn [a b] (rem a b))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接