我正在读一本关于clojure的书,我来的是一个我不完全理解的例子。
以下是repl中的代码:
user=> (repeatedly 10 (rand-int 10))
ClassCastException java.lang.Integer cannot be cast to clojure.lang.IFn clojure.core/repeatedly/fn--4705 (core.clj:4642)
user=> (repeatedly 10 (partial rand-int 10))
(5 0 5 5 2 4 8 8 0 0)
我的问题是:
为什么这里需要partial
,以及它如何适合partial
定义,
和repeatedly
定义&句法。部分......
Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args.
那么这又如何适应呢?
答案 0 :(得分:10)
Partial只是一种更简单的方法来定义一个匿名函数,该函数修复函数的一些参数,然后将其余参数从参数传递给创建的函数。
在这种情况下
user> (repeatedly 10 (partial rand-int 10))
(3 1 3 6 1 2 7 1 5 3)
相当于:
user> (repeatedly 10 #(rand-int 10))
(9 5 6 0 0 5 7 6 9 6)
Partial在这里用词不当,因为partial
被用于事先将所有参数(或者更确切地说是唯一的参数)修复为rand-int。
更多的局部使用部分说明了它的功能更好:
(partial + 4)
产生相当于:
(fn [& unfixed-args] (apply + (concat [4] unfixed-args)))
(它不会产生这种情况)
我们的想法是构建一个接受未修复参数的函数,将它们与固定参数组合在一起,并调用传递给partial
的函数,并使用足够的参数来正常工作。
user> ((fn [& unfixed-args] (apply + (concat [4] unfixed-args))) 5 6 7 8 9)
39
user> ((partial + 4) 5 6 7 8 9)
39
当参数的数量可变时,我只在实践中使用partial。否则,我个人倾向于使用匿名函数阅读器表单#( ... )
答案 1 :(得分:6)
partial
实际上并没有检查它的第一个参数支持哪个arities;一个可以说是更准确的docstring会说它“需要一个函数f和f的一些参数”。 (显然,如果你提供了太多的参数,那么结果部分应用的函数将被破坏,但是当你试图调用它时,这只会是可观察的。)所以即使(partial rand-int 10)
的参数数量为rand-int
也是如此。提供的{1}}并非“少于正常”。
此处需要partial
或类似#(rand-int 10)
之类的原因是repeatedly
期望其最终参数是可以重复调用的函数,而(rand-int 10)
将是一个数字。
将此与repeat
进行比较,后者返回一个序列,其中所提供的项重复指定的次数(或在一元情况下无限次多次)。这里(rand-int 10)
是一个合适的第二个参数,但当然它会是一个特定的数字,所以结果看起来像(8 8 8 8 8 ...)
;对于返回的序列中的每个项,repeatedly
将单独调用(partial rand-int 10)
,因此您将从中获得一系列(可能是不同的,独立的)随机数。
答案 2 :(得分:0)
repeatedly
签名:(repeatedly number function)
在这种情况下,partial将简单地将rand-int 10
包装到一个可以由外部函数(在这种情况下为repeatedly
返回和使用的函数中)。
没有partial
(或#
)内部表达式先于外部表达式解析(有例外,但现在让我们保持简单),所以当{{ 1}}是在没有repeatedly
的情况下调用的,将传递给它的是返回值partial
,即rand-int
而不是函数。