【问题标题】:Seemingly magical behaviour in my Clojure REPL我的 Clojure REPL 中看似神奇的行为
【发布时间】:2014-09-27 09:08:05
【问题描述】:

我正在修改 Clojure,目前正在尝试使用 clojure.lang.PersistentQueue 来模拟 Dijkstra 的 Sleeping Barber 问题中的候诊室。

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)

如您所见,我发送了两次相同的命令。第一次它抛出异常。然而,第二次似乎工作得很好。我现在可以 conjpop 我的 q 直到 Hickey 的奶牛回家。

这在哪个世界可以接受?我没有看到的幕后发生了什么?

【问题讨论】:

    标签: clojure leiningen


    【解决方案1】:

    PersistentQueue 是一个类,而不是该类的实例。请改用PersistentQueue/EMPTY

    其次,concat 返回一个惰性序列,不管你传入的类型是什么。你不能 concat 在一个队列上并得到一个队列。请改用conj,它是多态的。

    其中很多是未定义的行为。垃圾进,垃圾出。

    我认为正在发生的是懒惰和错误的相互作用。当您尝试在 PersistentQueue 类上使用 concat 时,它成功返回一个惰性序列。当您尝试打印该值时,错误发生在事务之外。打印导致对序列的 first 元素(PersistentQueue 类)的求值,但 concat 是懒惰的:它还没有尝试创建 second 的序列强> 论点(:customer)。您的 Ref 现在包含一个惰性序列,其 second 元素尚未被评估。

    第二次尝试修改 Ref 时,conj 强制评估 Ref 中惰性序列的下一个可用元素,从而产生另一个错误。由于该序列未能产生任何值,因此它的值是一个惰性序列。

    现在你的 Ref 包含一个空序列,conj 被定义为调用cons。现在您的 Ref 包含一个元素的非惰性序列,实际上是一个列表。

    【讨论】:

    • 这种语言是巫毒教。
    • 第二个错误来自原来的concat 调用(第二个是conj)。 ref 是否以某种方式缓存每个异常,直到它被清除?
    • 嗯,我明白了...concat 将错误的转换(类到序列和关键字到序列)都包装在 thunk 中,并且需要两次尝试才能实现惰性序列强制他们。
    • @JezenThomas 你并不需要真正理解 Stuart 在这里所说的细节:这听起来像巫毒教,因为他正在解释当你做一些非法的事情时实际发生的事情的实施细节,仅此而已比较深奥。但是,如果您只听从他第一句话的建议,您将避免陷入奇怪的错误场景;您的 Clojure 程序不必关心 voodoo。
    猜你喜欢
    • 2012-01-31
    • 2011-06-28
    • 2019-06-09
    • 1970-01-01
    • 1970-01-01
    • 2016-10-18
    • 2016-12-27
    • 2014-11-28
    • 2013-06-06
    相关资源
    最近更新 更多