如果我理解正确,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。:适当地重新考虑,我不知道我应该在这里使用哪些术语)
答案 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