如何在Clojure中实现For循环

33

我想在Clojure中实现这段小代码,但我遇到了困难:

struct mystruct {
   int id;
   int price;
};

mystruct mydata[10];

for (int i=0; i<10; i++) {
  myfunction(mydata[i].id, mydata[i].price);
  //other things...
}

我是Clojure的初学者,对于像这样简单的事情要做起来真的很复杂,但我真的在尽力学习,因为我知道使用Clojure有很多优势,比如使用refs等...

如果有人能帮我,我会非常感激。谢谢!


通常在函数式编程领域中,最好坚持使用map、apply和reduce。Map具有隐含的for循环 ^.^ - sova
3个回答

44

将一个命令式的for循环翻译成Clojure的一种方法是使用for宏。

(for [i (range 10)] (inc i))

上面的函数将返回从0到9递增1的所有数字。然而,看起来你只想遍历一个顺序集合并使用每个项。如果这就是你需要的所有内容,那么你不需要引用索引值,而是可以直接引用每个项。

(for [d my-vec-of-data] (my-function d))

然而,对于这个简单的情况,map 函数可能是更好的选择,因为它旨在从集合中调用带参数的函数。下面的示例等同于上面使用 for 的示例。

(map my-function my-vec-of-data)

在Clojure中,mapfor 都返回一个值的集合,这些值是由 my-function 返回的值组成的。这是因为Clojure的数据结构是不可变的,所以必须返回一个新的集合。如果这不是你需要的,或者如果你的函数有副作用,那么可以使用 doseq 而不是 for,它返回 nil


2
'i'会自动增加吗? - nuvio
1
@nuvio:i将是序列中的实际项,而不是索引。是的,for宏将为您处理迭代序列。 - Chuck
"doseq"非常适用于那些不需要返回值的函数,因为它返回单个nil而不是每个元素都返回一个值。 - jm0
@Chuck,你怎么能得到实际计数,因为i是实际项目? - Ike Mawira

39

Jeremy的回答介绍了如何用Clojure的惯用方式编写for循环。

如果你真的想在Clojure中使用命令式风格的for循环,可以使用这个宏来创建:

(defmacro for-loop [[sym init check change :as params] & steps]
 `(loop [~sym ~init value# nil]
    (if ~check
      (let [new-value# (do ~@steps)]
        (recur ~change new-value#))
      value#)))

使用方法如下:

(for-loop [i 0 (< i 10) (inc i)] 
  (println i))

36
我喜欢Clojure的一点是,如果你缺少某个语言功能,不用等待它被添加到未来版本中,你可以自己编写。 - Dale

2

doseq 做的事情类似于一个 for-loop

用法:
(doseq [i (for [i (range 10)] (inc i))]
    (println "i=" i))

这种绑定类似于Clojure中的for语句。但是,它不会通过在doseq内部评估表达式来返回列表。它将为序列中的每个值执行表达式,并返回nil

要循环遍历一个序列,您可以简单地使用:

(doseq [value list]
    (println "Your expression here" value)

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