什么时候创建一个集合?

时间:2012-04-28 16:09:58

标签: collections clojure side-effects

如果我理解正确,Clojure可以返回列表(如在其他Lisps中),还可以返回矢量和集合。

我真正得到的是为什么总是返回的集合。

例如,如果我采用以下代码:

(loop [x 128]
  (when (> x 1)
    (println x)
    (recur (/ x 2))))

它打印128 64 32 16 8 4 2.但这只是因为 println 被调用而 println 具有打印内容的副作用(?)。 / p>

所以我尝试用它替换它(删除println):

(loop [x 128]
  (when (> x 1)
    x
    (recur (/ x 2))))

我期待得到一些收集(据说是一个列表),像这样:

(128 64 32 16 8 4 2)

但我得到 nil

我不明白哪个决定什么创建了一个集合,什么没有,以及你如何从一个集合切换到另一个集合。另外,看到Clojure以某种方式鼓励编程的“功能”方式,你不应该几乎总是返回集合吗?

为什么这么多功能显然不会返回任何集合?那些制作这些回归集合的惯用方法是什么?

例如,如何通过首先构造一个集合然后以惯用的方式迭代(?)其他结果列表/向量来解决上述问题?

首先,我不知道如何转换循环,以便它生成除 nil 之外的其他内容,然后我尝试了以下内容:

(reduce println '(1 2 3))

但是它会打印“1 2nil 3nil”而不是我期待的“1 2 3nil”

我意识到这是基本的东西,但我刚开始,我显然在这里缺少基本的东西。

(P.S。:适当地重新考虑,我不知道我应该在这里使用哪些术语)

2 个答案:

答案 0 :(得分:3)

其他一些评论指出,什么时候真的没有效果 - 但我认为这不是你的问题。

循环和重复表单创建一个迭代 - 就像其他语言中的for循环一样。在这种情况下,当你打印时,它确实只是副作用。如果要返回序列,则需要构建一个序列:

(loop [x 128                                                                                                        
       acc []]                                                                                                      
  (if (< x 1)                                                                                                       
    acc                                                                                                             
    (recur (/ x 2)                                                                                                  
           (cons x acc))))                                                                                          

=> (1 2 4 8 16 32 64 128)

在这种情况下,我用一个recur 替换了你调用printf的地方,这个形式将x添加到该累加器的前面。在x小于1的情况下,代码返回累加器 - 因此返回序列。如果要添加到向量的末尾而不是前面,请将其更改为conj:

(loop [x 128                                                                                                    
       acc []]                                                                                                  
  (if (< x 1)                                                                                                   
    acc                                                                                                         
    (recur (/ x 2)                                                                                              
           (conj acc x))))                                                                                      

=> [128 64 32 16 8 4 2 1] 

你得到的是因为那是你表达的结果 - 最后的println返回的结果。

这一切都有意义吗?

reduce不是完全相同的东西 - 它用于通过重复应用二进制函数(一个带有2个参数的函数)来初始值和序列的第一个元素,或者前两个来减少列表第一次迭代的序列元素,然后传递后一次迭代的结果和序列中的下一个值。一些例子可能有所帮助:

(reduce + [1 2 3 4])                                                                                            

10  

执行以下操作:

(+ 1 2) => 3
(+ 3 3) => 6
(+ 6 4) => 10

Reduce将导致正在执行的二进制函数的最终结果 - 在这种情况下,我们将序列中的数字减少为所有元素的总和。

您还可以提供初始值:

(reduce + 5 [1 2 3 4])                                                                                            

15  

执行以下操作:

(+ 5 1)  => 6
(+ 6 2)  => 8
(+ 8 3)  => 11
(+ 11 4) => 15

HTH,

凯尔

答案 1 :(得分:2)

对集合的广义抽象在Clojure中称为序列,许多数据结构实现了这种抽象,因此您可以在这些数据结构上使用所有与序列相关的操作,而无需考虑将哪个数据结构传递给您的函数。

就示例代码而言 - 循环,recur用于递归 - 所以基本上任何你想用递归解决的问题都可以使用它解决,经典的例子是阶乘。虽然你可以使用循环创建一个向量/列表 - 通过使用累加器作为向量并继续向它添加项目,并且在递归的存在条件下返回累积向量 - 但是你可以使用reductions和{{1}功能如下所示。这将返回一个懒惰的序列。

例如:

take-while