在Clojure中计算一个序列

时间:2013-05-23 21:19:49

标签: clojure

我正在从4Clojure网站解决Problem 22,要求我编写一个计算序列中元素的函数。因为我曾经一度搞过Haskell,所以我知道使用fold可能就是这样做的方法。阅读完之后,我已经明白我应该使用reduce来达到同样的目的。这是我提交的答案:

#(reduce inc 0 %)

这背后的原因是迭代列表,并且每次在最初为0的值上调用inc。但是,这不起作用。该网站抱怨说“错误的args数量(2)传递给:core $ inc”。所以我尝试在inc附近添加parens:

#(reduce (inc) 0 %)

现在它认为零参数传递给inc。我在这里做错了什么?

5 个答案:

答案 0 :(得分:10)

所以我尝试在公司附近添加parens ......

不要使用clojure 永远中的parens来指导编译器/解释器。在clojure中,每个括号都是重要的,这样你只需要调用一个零参数的函数。

现在,看看clojure doc on reduce

  

如果提供了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 %))