Land of Lisp example redundency?
Asked Answered
P

2

10

I've read a lot of good things about Land of Lisp so I thought that I might go through it to see what there was to see.

(defun tweak-text (lst caps lit)
  (when lst
    (let ((item (car lst))
      (rest (cdr lst)))
      (cond 
       ; If item = space, then call recursively starting with ret
       ; Then, prepend the space on to the result.
       ((eq item #\space) (cons item (tweak-text rest caps lit)))
       ; if the item is an exclamation point.  Make sure that the
       ; next non-space is capitalized.
       ((member item '(#\! #\? #\.)) (cons item (tweak-text rest t lit)))
       ; if item = " then toggle whether we are in literal mode
       ((eq item #\") (tweak-text rest caps (not lit)))
       ; if literal mode, just add the item as is and continue
       (lit (cons item (tweak-text rest nil lit)))
       ; if either caps or literal mode = true capitalize it?
       ((or caps lit) (cons (char-upcase item) (tweak-text rest nil lit)))
       ; otherwise lower-case it.
       (t (cons (char-downcase item) (tweak-text rest nil nil)))))))

(the comments are mine)
(FYI -- the method signature is (list-of-symbols bool-whether-to-caps bool-whether-to-treat-literally) but the author shortened these to (lst caps lit).)

But anyway, here's the question:
This has (cond... (lit ...) ((or caps lit) ...)) in it. My understanding is that this would translate to if(lit){ ... } else if(caps || lit){...} in a C style syntax. Isn't the or statement redundant then? Is there ever a condition where the (or caps lit) condition will be called if caps is nil?

Packing answered 2/1, 2011 at 0:58 Comment(0)
H
10

Indeed, you are correct. See the errata for the book.

Page 97: The function tweak-text has two glitches in it, though it will run OK on most Lisp implementations. First of all, it uses the eq function to compare characters- Characters should always be checked with other functions such as eql or char-equal as per the ANSI spec. Also, there's an unnecessary check of (or caps lit) that can be simplified to caps.

Hooknosed answered 2/1, 2011 at 1:41 Comment(3)
Thanks. I was beginning to think I was going crazy (I'll remember to check errata first next time)Packing
Broken errata link?Wallache
@J.Mini seems to work fine for me, I still see the page up there. Either way the whole text is copied out here so we're ok if the link goes down.Hooknosed
R
8

I would write that as:

(defun tweak-text (list caps lit)
  (when list
    (destructuring-bind (item . rest) list
      (case item
        ((#\space)             (cons item (tweak-text rest caps lit)))
        ((#\! #\? #\.)         (cons item (tweak-text rest t    lit)))
        ((#\")                 (tweak-text rest caps (not lit)))
        (otherwise (cond (lit  (cons item (tweak-text rest nil  lit)))
                         (caps (cons (char-upcase item)
                                     (tweak-text rest nil lit)))
                         (t    (cons (char-downcase item)
                                     (tweak-text rest nil nil)))))))))

The CASE statement dispatches on the character. The COND statement then takes care of the other conditions. CASE compares with EQL. That means CASE works for also for characters and even can compare with multiple items. I'm also a fan of a code layout style that lines up corresponding expressions - this is only useful with monospaced fonts. This helps me to detect patterns visually in the code and helps detecting code that can be simplified.

DESTRUCTURING-BIND takes the list apart.

For fun, rewritten using LOOP:

(defun tweak-text (list)
  (loop with caps and lit

        for item in list

        when (eql item #\space)
        collect item

        else when (member item '(#\! #\? #\.))
        collect item and do (setf caps t)

        else when (eql item #\")
        do (setf lit (not lit))

        else when lit
        collect item and do (setf caps nil)

        else when caps
        collect (char-upcase item) and do (setf caps nil)

        else
        collect (char-downcase item) and
        do (setf caps nil lit nil)))
Rosario answered 2/1, 2011 at 10:4 Comment(1)
'((There are a few things in the book I have noted I would have done differently (he has a bit of recursion here where iteration is much cleaner and (in my opinion) easier to grasp))( but (I am following the examples (in-case (he has a better plan than I would have come up with))))) (Lisp does English).Packing

© 2022 - 2024 — McMap. All rights reserved.