3

I'm going through Casting SPELs in Lisp and this is the proposed solution to handling picking up objects:

(define *location* 'living-room)

(define *object-locations*
  '((whiskey-bottle living-room)
    (bucket living-room)
    (chain garden)
    (frog garden)))

(define (pickup-object object)
  (cond [(is-at? object *location* *object-locations*)
         (push! (list object 'body) *object-locations*)
         (string-append "You're now carrying the " (symbol->string object) ".")]
        [else "There's no such object in here."]))

Am I the only one who finds this inefficient? As far as I understand the push! function conses a new pair to *object-locations* every time the player picks up an object. While this may not be a major problem in a small game like this, if one would to add the option of putting down items from the inventory, the *object-locations* list could grow infinitely... Shouldn't pickup-object replace the cdr of (whiskey-bottle living-room), for example, instead of adding another copy of the pair?

I'm new to Lisp and may be mistaken... Could somebody please explain whether my assumptions are right, and if so, what would be the best way to handle picking up of objects in a Lisp text adventure?

Philip Seyfi
  • 929
  • 1
  • 10
  • 24

1 Answers1

1

There are a few problems with the code:

  • The list *object-locations* is a literal. Literals should not be modified. You can't change the location of frog destructively. So you need to push a new location in front.
  • The list grows as you walk through the game.
  • STRING-APPEND creates a new string for each pickup action.

But

  • It is simple and sufficient for a book example.
  • The stack for object locations would make some kind of undo possible.
  • The push of a new association of item and location is a fast operation.
  • It leaves the opportunity for the reader of the book to make the code more efficient.

In Common Lisp that's easy to change:

(defvar *object-locations*
  (copy-tree
   '((whiskey-bottle living-room)
     (bucket living-room)
     (chain garden)
     (frog garden))))

(defun get-location (object)
  (second (assoc object *object-locations*)))

(defun set-location (object location)
  (setf (second (assoc object *object-locations*))
        location))

CL-USER > (get-location 'frog)
GARDEN

CL-USER > (set-location 'frog 'living-room)
LIVING-ROOM

CL-USER > (get-location 'frog)
LIVING-ROOM

CL-USER > *object-locations*
((WHISKEY-BOTTLE LIVING-ROOM)
 (BUCKET LIVING-ROOM)
 (CHAIN GARDEN)
 (FROG LIVING-ROOM))

See the book Common Lisp: A Gentle Introduction to Symbolic Computation by David S. Touretzky for a really basic introduction to Lisp.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
  • What would be to standard/good way of dealing with this situation in a functional language? In C#, JS, or AS I could simply create an Array or a List and the change the individual entries when necessary. In functional programming, however, it looks like one is supposed not to change anything whenever possible... I can see how it's possible when parsing some data or doing calculations, but I don't really see a way of efficiently applying this paradigm in other practical applications, such a game where one has to 1. keep track of various states and events 2. change things like the inventory etc. – Philip Seyfi Feb 05 '11 at 12:52
  • @Philip Seyfi: 'Lisp' (as in Common Lisp) is not a purely functional language with immutable data structures. It has mutable cons cells, mutable arrays, mutable objects, mutable hash-tables - use what you like. – Rainer Joswig Feb 05 '11 at 12:57
  • In my example, is there a way to mutate `(frog garden)` to `(frog body)`? – Philip Seyfi Feb 05 '11 at 13:22
  • @Philip Seyfi: see the expanded answer. – Rainer Joswig Feb 05 '11 at 13:33