我正在从4Clojure网站解决Problem 22,要求我编写一个计算序列中元素的函数。因为我曾经一度搞过Haskell,所以我知道使用fold
可能就是这样做的方法。阅读完之后,我已经明白我应该使用reduce
来达到同样的目的。这是我提交的答案:
#(reduce inc 0 %)
这背后的原因是迭代列表,并且每次在最初为0的值上调用inc
。但是,这不起作用。该网站抱怨说“错误的args数量(2)传递给:core $ inc”。所以我尝试在inc
附近添加parens:
#(reduce (inc) 0 %)
现在它认为零参数传递给inc
。我在这里做错了什么?
答案 0 :(得分:10)
所以我尝试在公司附近添加parens ......
请不要使用clojure 永远中的parens来指导编译器/解释器。在clojure中,每个括号都是重要的,这样你只需要调用一个零参数的函数。
如果提供了val,则返回将f应用于val的结果 在coll中的第一项,然后将f应用于该结果和第二项, 等
所以当你写
(reduce inc 0 [1 2 3])
实际发生的是
(inc
(inc
(inc 0 1) 2) 3)
正确的功能可能看起来像
#(reduce
(fn [c _] (inc c))
0 %)
答案 1 :(得分:3)
假设列表是
[1 2 3 4]
在你的第一个例子中,你告诉reduce执行这些:
(inc 0 1)
(inc 0 2)
(inc 0 3)
(inc 0 4)
显然inc在这里传递了两个参数,当它需要一个时。
在第二次尝试中,您告诉reduce(inc)将返回一个函数,然后该函数将与值和列表一起使用。 (inc)是对零参数的inc函数的调用。
要减少的第一个参数需要的是一个函数,它接受两个值并返回第一个增加1的值。
答案 2 :(得分:2)
Clojure的reduce
可以被认为与haskell的左折函数foldl
类似。
计算列表xs
中的项目的haskell方式将是这样的
foldl (const . (+1)) 0 xs
更好地理解为foldl (\acc _ -> acc + 1) 0 xs
。
这个想法是在fold函数中递增第一个操作数,所以我很想把它写成
(reduce #(inc (%1)) 0 xs)
除了错误之外,因为匿名函数的arity由表达式中的highest argument referenced决定,我们的fold函数必须具有2的arity。
所以,一个聪明的解决方法:
(reduce #(inc (first %&)) 0 xs)
答案 3 :(得分:1)
我也经历过问题列表学习clojure。想出了这个问题的答案:
reduce + (#(for [o %] 1))
答案 4 :(得分:0)
还有另一种方法是将序列转换为Java数组并调用alength函数:
#(alength (to-array %))