我正在修补Clojure,目前正在试验clojure.lang.PersistentQueue
来模拟Dijkstra的睡觉理发师问题中的等候室。
barber.core=> (def q (ref clojure.lang.PersistentQueue))
#'barber.core/q
barber.core=> q
#<Ref@37c3a6f0: clojure.lang.PersistentQueue>
barber.core=> @q
clojure.lang.PersistentQueue
barber.core=> (dosync (alter q concat :customer))
IllegalArgumentException Don't know how to create ISeq from: java.lang.Class clojure.lang.RT.seqFrom (RT.java:505)
barber.core=> (dosync (alter q conj :customer))
IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Keyword clojure.lang.RT.seqFrom (RT.java:505)
barber.core=> (dosync (alter q conj :customer))
(:customer)
如您所见,我发送了两次相同的命令。它第一次抛出异常。然而,第二次似乎工作得很好。我现在可以conj
和pop
我的q
直到希基的奶牛回家。
在哪个世界这是可以接受的?幕后发生了什么,我没有看到?
答案 0 :(得分:9)
PersistentQueue
是一个类,而不是该类的实例。请改用PersistentQueue/EMPTY
。
其次,concat
返回一个惰性序列,无论您传入的是什么类型。您不能在队列上concat
并获得队列。请使用多态的conj
。
很多都是未定义的行为。垃圾进,垃圾出。
我认为正在发生的是懒惰与错误的相互作用。当您在concat
课程上尝试PersistentQueue
时,成功会返回延迟序列。尝试打印值时,错误发生在事务外部。打印导致评估序列的第一个元素(PersistentQueue
类),但concat
是懒惰的:它还没有尝试制作第二个参数(:customer
)。您的Ref现在包含一个惰性序列,其第二个元素尚未被评估。
第二次尝试修改Ref时,conj
强制评估Ref中延迟序列的下一个可用元素,产生另一个错误。由于序列无法生成任何值,因此其值为空延迟序列。
现在您的Ref包含一个空序列,其conj
被定义为调用cons
。现在你的Ref包含一个非懒惰的一个元素序列,实际上是一个列表。