Search code examples
loopscommon-lisphashtablenested-loops

How can I do two or more instructions in a loop in common-lisp?


I would like to know whats wrong with this code.Assume *corpus is a list of words ("at" "the" ...) and this code tries to keep them in a hash-table (word times-repeated-word)

(defparameter h (make-hash-table))
(defparameter ex 0)
(loop for x in *corpus
      do ((setf ex 0)
          (loop for y being the hash-keys of h
                if (equal x y) do ((incf (gethash y h)) (setf ex 1)))
                if (eql ex 0)
                do (setf (gethash x h) 1)))

If the word is in the hash-table just increase 1, else add a new pair.


Solution

  • You want to iterate over a corpus of words; for each word w, if w is mapped to an integer n in some hash, you want to increment that number so that w is mapped to n+1; otherwise, you want to map that word to 1.

    Basically, you want to do this:

    (defun increment-corpus (corpus hash)
      (map nil
           (lambda (word)
             (incf (gethash word hash 0)))
           corpus))
    
    • I am using MAP so that I can iterate over any sequence of words, not just lists.

    • The result-type of MAP is NIL, because I don't care about the result, I just want to make side-effects.

    • The function that is applied simply increments the current value bound to word. Note that GETHASH provides a default form to be evaluated in case no value is bound to the given key. Here, I just need to put zero so that the increment works in all cases. I didn't read it at first, but this comment from Terje D. already said it.

    Example

    (defparameter *hash* (make-hash-table :test #'equal))
    
    (defun test (&rest words)
      (increment-corpus words *hash*)
      (maphash (lambda (&rest entry) (print entry)) *hash*))
    

    The hash is initially empty.

    > (test "a" "b" "c" "d")
    
    ("a" 1) 
    ("b" 1) 
    ("c" 1) 
    ("d" 1)
    
    > (test "a")
    
    ("a" 2) 
    ("b" 1) 
    ("c" 1) 
    ("d" 1)
    
    > (test "a" "b" "c" "x")
    
    ("a" 3) 
    ("b" 2) 
    ("c" 2) 
    ("d" 1) 
    ("x" 1)