Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Common Lisp case and quoted elements

I'm writing a dungeon crawler game in CL, and I'm having trouble with the case form.

Two things:

  • Common Lisp complains Duplicate keyform QUOTE in CASE statement
  • (make-instance 'cl-rogue:tile tile-type 'wall) should print as "#", but the object prints as " " no matter which tile-type I use.

The code:

(in-package :cl-user)

(defpackage :cl-rogue
  (:use :common-lisp)
  (:export
    :*rows*
    :*cols*
    :*levels*
    :tile
    :tile-type
    :tile-contents
    :tile-hidden
    :tile-locked
    :tile-closed
    :main))

(in-package :cl-rogue)

(defparameter *cols* 80)
(defparameter *rows* 24)
(defparameter *levels* 26)

The class:

(defclass tile ()
  ((tile-type
    :initarg :tile-type
    :accessor tile-type
    :initform 'floor
    :documentation "Type of tile")
    (tile-contents
      :initarg :tile-contents
      :accessor tile-contents
      :initform '()
      :documentation "Any items the tile holds")
    (tile-hidden
      :initarg :tile-hidden
      :accessor tile-hidden
      :initform nil
      :documentation "Whether the tile is hidden or shown")
    (tile-locked
      :initarg :tile-locked
      :accessor tile-locked
      :initform nil
      :documentation "Whether the tile is locked")
    (tile-closed
      :initarg :tile-closed
      :accessor tile-closed
      :initform nil
      :documentation "Whether the tile is open or closed")))

The print method:

(defmethod print-object ((object tile) stream)
  (with-slots (tile-type tile-contents tile-hidden tile-locked tile-closed) object
    (if tile-hidden
      (format stream " ")
      (let ((an-item (car tile-contents)))
        (if an-item
          (format stream "~a" an-item)
          (format stream (case tile-type
            ('wall "#")
            ('upstair "<")
            ('downstair ">")
            ('door (if tile-closed "+" "\\"))
            (otherwise " "))))))))
like image 534
mcandre Avatar asked Dec 16 '22 15:12

mcandre


1 Answers

You don't need to quote the symbols in CASE.

Actually you shouldn't quote symbols in CASE clauses.

See:

CL-USER 31 > (case 'quote
               ('not-quote 'oops-really-quote)
               ('quote 'it-is-a-quote))
OOPS-REALLY-QUOTE

Above is the unintended consequence of using 'not-quote instead of the correct unquoted not-quote.

The following is correct:

CL-USER 32 > (case 'quote
               (not-quote 'oops-really-quote)
               (quote 'it-is-a-quote))
IT-IS-A-QUOTE

The syntax of CASE in Common Lisp

case keyform
  {normal-clause}*
  [otherwise-clause]

=> result*

normal-clause::= (keys form*) 
otherwise-clause::= ({otherwise | t} form*) 
clause::= normal-clause | otherwise-clause 
keys ::= object | (object*)

As you can see the keys are either a single object or a list of objects. The objects are not evaluated.

Symbols in CASE clauses are not evaluated.

(case tile-type
  (wall ...)
  (door ...))

WALL and DOOR are purely symbols and not evaluated as variables.

The Lisp reader reads 'fooas (quote foo).

You wrote:

(case tile-type
  ('wall ...)
  ('door ...))

Which is the equivalent of:

(case tile-type
  ((quote wall) ...)
  ((quote door) ...))

But you can't quote a symbol in CASE. You have to provide the symbols as literal constants.

If you write:

(let ((bar 'foo)
      (baz 'foo))
  (case bar
    (baz :we-have-a-foo-through-baz)
    (foo :we-really-have-a-foo)))

This returns :WE-REALLY-HAVE-A-FOO. Because CASE uses constant data, not variables.

CASE accepts a list of items. Since you have QUOTE as a symbol in more than clause, the compiler showed a warning.

As I said, there is no quoting possible, since the items are not evaluated.

As for CASE accepting a list of items in the clauses, it looks like this:

(case tile-type
  ((door wall) ...)
  ((floor window painting) ...))

For the WALL symbol, you need to make sure that it is in the right package when you create the object.

Better use a keyword symbol, such as :wall. Then you don't need to export it and there is no confusion about in which package the symbol is.

About the formatting of the code: You had a bullet list and right after it a code section. This is not rendered as you expect. I have added the text 'The code:' before the code. Then the rendering works as expected.

like image 122
Rainer Joswig Avatar answered Jan 30 '23 22:01

Rainer Joswig