Clojure可以很轻松的生成无穷列表1,如下代码:
(println (take 10 (range)))
;;-> (0 1 2 3 4 5 6 7 8 9)
通过打印函数结果可知,我们使用take取的了range返回的前10个数。range返回的一组序列被为惰性序列。
在调用range时我们给传递了一个参数来限定序列的范围,若我们不提供参数,默认会是无穷的。直接运行会导致电脑卡机,repl奔溃。要注意,不用轻易的尝试(试过,就知道)。
我们也可以使用(range 10)来生成前十个数序列:
(range 10)
count函数,用来统计序列的长度,我们可以取1000个序列数,并计算长度:
(println (count (take 1000 (range))))
;;-> 1000
注意range需要用()括起来,因为range可以是无参数的调用,在clojure中括号就是一个执行单位,若不加()则会不识别range这个函数的操作,上述的例子我们也可以这样写:
(println (count (range 1000)))
;;-> 1000
repeat,可以生成重复项的无穷序列,可以指定一个参数表示重复的次数,也可以无参数,此时生成的序列是无穷序列,与range一样是无穷的,但repeat生成的是重复项:
(println (repeat "aa"))
;;-> 生成"aa"无穷序列,不要轻易尝试
(println (take 10 (repeat "aa")))
;;-> (aa aa aa aa aa aa aa aa aa aa)
;;-> 取出前十个
rand-int,生成一个随机数无穷序列,
随机生成一个0-10以内的随机整数:
(println (rand-int 10))
加上repeat:
(println (repeat 5 (rand-int 10)))
;;-> (8 8 8 8 8)
生成的数一样,可见这只是将数重复了5次,并不是将后面的函数执行五次。
若想重复执行五次函数,需要使用repeatedly参数而不是repeat:
(println (repeatedly 5 #(rand-int 10)))
;;-> (9 9 5 0 7)
注意repeatedly的参数为repeatedly[f] ,[n f],两种参数模式,一种是只传入一个函数,另一种是传入一个数值和函数,我们输入的(rand-int 10)是一个结果,通过加#将其变成一个匿名函数,该函数无参数,作为repeatedly的参数使用,结果就变成了生成5个0-10的随机数。
当然我们也可以使用(repeatedly #(rand-int 10))生成一个无穷序列,通过take来获取我们想要的个数。
(println (take 10 (repeatedly #(rand-int 10))))
;;-> (3 6 5 2 1 3 0 5 8 1)
cycle,接受一个collection作为参数,返回其无限循环的序列。
(println (take 10 (cycle ["aa" "bb" "vv"])))
;;-> (aa bb vv aa bb vv aa bb vv aa)
递归应该使用过很多,归根结底就是调用自身的函数,先看一个递归的小例子2:
(defn Example []
(loop [i 0]
(when (< i 5)
(println i)
(recur (inc i)))))
(Example)
loop为声明一个参数[i 0],绑定i为0,当i小于5的时候打印i,并且通过recur执行inc函数将i加1,输出应该为:0 1 2 3 4。其中recur跳回了递归点,也即是loop的开头,当i大于等于5时,此时不再执行recur操作,程序也就结束了。
loop-recur与for循环类似,不过它是递归的形式,通过recur来代替函数名称,返回到递归点loop,完成类似循环的操作。此外,recur是Clojure避免栈消耗的办法,它一次只调用一个栈。
Living Clojure(中文版):中国电力出版社 ↩︎
Clojure递归:https://www.w3cschool.cn/clojure/clojure_recursion.html ↩︎