在Clojure中重写《Lisp之国》的巫师游戏

9

我正在试图重写《Lisp之地》中的巫师游戏 http://landoflisp.com/wizards_game.lisp

请注意,这是一段关于IT技术的文本,需要翻译成通俗易懂的中文。
(def *nodes* {:living-room "you are in the living-room. a wizard is snoring loudly on the couch."
          :garden "you are in a beautiful garden. there is a well in front of you."
          :attic "you are in the attic. there is a giant welding torch in the corner."})

(def *edges* {:living-room '((garden west door) (attic upstairs ladder))
          :garden '(living-room east door)
          :attic '(living-room downstairs ladder)})

(defn describe-location [location nodes]
  (nodes location))

(defn describe-path-raw [edge]
  `(there is a ~(last edge) going ~(second edge) from here.))

(defn describe-path [edge]
   (map #(symbol (name %)) (describe-path-raw edge)))

(defn describe-paths [location edges]
   (apply concat (map describe-path-raw (location edges))))

当尝试时:

   (println (describe-paths :attic *edges*))

我遇到了这个异常:

主线程中的异常: java.lang.RuntimeException: java.lang.IllegalArgumentException: 不知道如何从clojure.lang.Symbol创建ISeq (wizard-game.clj:0)

我还没有Lisp的眼光,我做错了什么?

2个回答

8
将此放入REPL中并运行trace:
user> (ns foo (:use clojure.contrib.trace))
nil

现在我将你的代码复制到REPL中。(未显示)

接下来,我运行一个跟踪:

foo> (dotrace [describe-location describe-path-raw describe-path describe-paths]
              (describe-paths :attic *edges*))
TRACE t1662: (describe-paths :attic {:living-room ((garden west door) (attic upstairs ladder)),     :garden (living-room east door), :attic (living-room downstairs ladder)})
TRACE t1663: |    (describe-path-raw living-room)
; Evaluation aborted.
foo> 

因此问题在于(描述路径原始living-room)。正如错误消息所指出的,living-room是一个符号,并且该函数试图对其执行像调用last和second这样的操作,这些操作仅适用于序列。
那么为什么会发生这种情况呢?
在describe-paths中,您正在调用(location edges)。在这里,位置是:attic,边缘是一个映射。因此,(location edges)变成了(living-room downstairs ladder)。您正在将describe-path-raw映射到此列表上,结果如下:
((describe-path-raw living-room) (describe-path-raw downstairs) (describe-path-raw ladder))

在第一次调用时会抛出异常,因为living-room是一个符号,而不是序列。


为什么调用变成了:(describe-path-raw living-room)?:attic的值是列表类型。 - Chiron
@4bu3li:请看我回答的补充内容。希望这能有所帮助。 - Rob Lachlan
阁楼映射到一个列表。我的意思是这是一个列表,对吗?'(客厅 楼下 梯子)?我运行了这段代码:(class(:attic '(客厅 楼下 梯子))),它返回PersistentList。我解决了问题,感谢你和 seh,但我还是没搞清楚真正的问题。 - Chiron
@4bu3li:那是一个列表,对吧。它由(位置边缘)返回,在describe-paths函数内部。但是然后describe-paths调用map describe-path-raw到该列表上。那是做什么的?它将describe-path-raw应用于该列表中的每个元素。该列表中的第一个元素是living-room,因此评估(map describe-path-raw living-room)的第一个函数调用是(describe-path-raw living-room)。而living-room是一个符号,这不是describe-path-raw所需的。 - Rob Lachlan

1

看起来describe-paths期望在*edges*映射中查找的值将是一系列列表,而不仅仅是一个列表。请注意:living-room条目与:garden:attic条目之间的区别:前者具有顶级脊柱,在此之下您会发现两个三元组,而后两个每个只有一个三元组。

功能describe-path-raw期望接收至少大小为2的元组,在这里,它只有在大小为3的情况下才有意义;将*edges*映射中的四个三元组之一提供给它都可以工作。您遇到的问题是由于将map应用于:attic*edges*条目造成的,该条目获取列表。

(living-room downstairs ladder)

并逐一将列表对象提供给describe-path-raw

(describe-path-raw living-room)
(describe-path-raw downstairs)
(describe-path-raw ladder)

在这三种形式中,传递给describe-path-raw的参数是一个符号,而不是describe-path-raw所期望的列表。
简而言之,请尝试在*edges*映射中的第二个和第三个值周围添加额外的括号,将每个列表嵌套在一个新的顶级列表中。

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