Search code examples
common-lispclisp

Why Terminal stop responding to Enter command and what I write?


I have a list of lists "straight":

(setq straight '(
        ("Arad"             366)
        ("Bucharest"          0)
        ("Craiova"          160)
        ("Dobreta"          242)
        ("Eforie"           161)
        ("Fagaras"          176)
        ("Giurgiu"           77)
        ("Hirsova"          151)    
        ("Iasi"             226)
        ("Lugoj"            244)
        ("Mehadia"          241)
        ("Neamt"            234)
        ("Oradea"           380)
        ("Pitesti"          100)
        ("Rimnicu Vilcea"   193)
        ("Sibiu"            253)
        ("Timisoara"        329)
        ("Urziceni"          80)
        ("Vaslui"           199)
        ("Zerind"           374)))

And I have a list ("Pitesti" 101). I'm trying to search through "straight" and find value corresponding to "Pitesti". However when I run my function, terminal stops responding to enter command and doesn't respond to anything I write. Here's the function:

(defun her (node)
  (setq s straight)
  (setq c '())
  (loop while (not (eq (car node)
                       (caar s)))
        do (setq s (cdr s)))
  (setq c (append node (car (cdar s)))))

Solution

  • First, your immediate problem: eq means object identity. I guess that you give a singleton list of the city name to this function, so when you compare (eq (car node) (caar s)), you compare two strings. As long as those two strings are not the same object (this might happen if they are both read as a literal from the same compilation—which seems quite unlikely here), this will be false. Instead, you should use string=.

    Since your comparisons never return true, that loop will keep on setting s to the tails of straight, but note that the cdr of nil is nil again: this will never terminate.

    So, replacing eq with string= might solve your immediate problem. However, there are more issues.

    What happens if the string you pass doesn't actually exist in straight? Then again, it will never terminate. A better way to do this loop:

    (loop :for pair :in straight
          :when (string= (car node) (car pair))
            :do (return pair))
    

    The for-in construct will terminate at the end of the list, so you will get back nil if the string is missing. Loop has another construct to find something: thereis:

    (loop :for pair :in straight
          :thereis (when (string= (car node) (car pair)) pair))
    

    Speaking of finding, why not use find instead of loop:

    (find (car node) straight
          :test #'string=
          :key #'first)
    

    Since using a list of lists for a simple associative structure is rather common (“association list” or “alist”), there is a specialized form for this:

    (assoc (car node) straight
           :test #'string=)
    

    Finally, a glaring general issue: you are setqing free (most likely unbound) variables throughout. Don't do that. Strive to write referentially transparent functions. Establish local bindings with let etc. When you have a result, simply return it, don't set some global variable to it.