shxiao 2019-09-07
先看一个简单的例子:
(defn pinc [n] (prn ".") (inc n))
定义nums
:
(def nums (map pinc [1 2 3]))
REPL没有任何输出.
输入nums
:
nums "." "." "." => (2 3 4)
nums
才被真正的计算.nums
在定义的时候并没有被计算,只有在使用的时候才会真正的计算.
许多函数式编程语言都是惰性的.Haskell是完全惰性,在Clojure中,主要的序列操作像map
,reduce
,filter
,repeatedly
都是惰性求值.
例如
(def n (pinc 0)) "." => #'logic.core/n
上个例子被立刻求值因为没有序列的存在.
最常见的惰性求值是无限序列或流.如果我们想要定义一个list包含所有的质数,这个列表是无穷大的.
如果我们在C++或其他语言定义了这样一个质数序列,程序将无限的计算下去.如果在Clojure或者Haskell中定义了序列,计算不会立刻发生.我们可以只打印前100个质数.因为惰性求值只计算所需要的部分序列.
想象我们需要制作一个游戏服务器,游戏中有许多的怪兽,每个怪兽都有一个随机生成的物品清单:
(defn gen-item [] {:name "sword" :attack (rand-int 100)}) (def monster {:name "wolf" :level 3 :inventory (repeatedly 10 gen-item)})
我们的游戏十分巨大,每秒产生1000个怪物,每个怪物都随身携带10个随机生成的物品.
如果非惰性求值,服务器不得不消耗大量的资源在随机生成这些物品上.
感谢这些序列都是惰性计算!尽管每秒都有1000个怪物被生成,但实际上没有任务物品是被实际产生.这些随机物品被实际的生成仅在玩家查看死亡的怪物装备时才发生!
如果只有50%的怪物被杀死,我们的服务器就减轻了一半的计算量.感谢惰性求值的威力!