如何在Clojure中进行for循环?

4

我正在学习Clojure,并使用Quil。我想知道如何将for循环转换为Clojure:

以下是我在Java或类似语言中的实现方式:

for ( int i = 0; i < numSides; i++ ) {
    float posX = cos( theta * i );
    float posY = sin( theta * i );
    ellipse( posX, posY, polySize, polySize );
}

我的Clojure尝试:

  (let [theta (/ PI num-sides)
        angle (range 0 num-sides)
        pos-x (cos (* theta angle))
        pos-y (sin (* theta angle))]
    (dorun (map #(ellipse % % % %) pos-x pos-y poly-size poly-size)))

在这里找到了类似的问题:https://dev59.com/Fmkw5IYBdhLWcg3wUI7x - Asraful
4个回答

7

所有你尝试过的方式基本上是处理序列,而循环则是执行特定次数的操作。Clojure 提供 dotimes 用于指定执行次数:

(dotimes [i 10]
  (println i))

那么你的代码将变成这样:

 (dotimes [i num-sides]
   (let [pos-x (cos (* theta i))
         pos-y (sin (* theta i))]
         (ellipse pos-x pos-y poly-size poly-size)))

4
如果您真正需要一个类C风格的for循环,那么我的clojure-utils库有一个方便的for-loop宏,让您可以做如下操作:
(for-loop [i 0 , (< i num-sides) , (inc i)]
  ... do stuff.....)

通常情况下,我会使用以下其中之一:

  • (dotimes [i num-sides] ....) - 按指定次数执行某个操作
  • (doseq [x some-sequence] ....) - 针对序列中的每个元素执行某些操作
  • (for [i (range n)] ...) - 构建一个包含n个元素的列表

让宏更像C语言风格怎么样,这样我们就可以这样做:(for-loop [ [i 1 j 1] (< i 10) [i (inc i) j (* j i)] ] .....) - Ankur
@Ankur 有趣的想法...我自己没有需要,但可以看到在某些用例中的价值。 - mikera

2

也许这有点学术化,但是我喜欢使用Clojure的“for comprehensions”来完成这样的事情。代码应该是这样的:

(dorun
  (for [i (range num-sides)
        :let [pos-x (Math/cos (* i theta))
              pos-y (Math/sin (* i theta))]]
    (quil.core/ellipse pos-x pos-y poly-size poly-size)))

1
但是在这种情况下,for构建了一个惰性序列,这是不必要的。如果你不需要输出序列,就像这个例子一样,你可以使用相同的绑定技巧,使用doseq来完成相同的事情。只需在上面的代码中用doseq替换for即可。在许多情况下,产生额外序列的成本是可以忽略不计的,因此doseq的唯一优点是它向读者传达了你不想创建序列的意图。然而,在某些情况下,创建不必要的序列的成本可能会影响性能。 - Mars

1
使用rangedoseq通常适用于循环特定数量的值以创建副作用。我会将您的循环实现如下:
(doseq [i (range 0 num-sides)]
  (ellipse (cos (* theta i)) 
           (sin (* theta i)) 
           poly-size 
           poly-size))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接