lisp -how to check if all the list is numbers

2019-09-17 10:42发布

问题:

I build this function to check if all the "var" on the list are numbers. This what i tried to do

(defun check6 (list) 
(if  (null list) 'TRUE)
(if (not (numberp(first list))) nil)
(check6 (rest list)))

But always i get stack overflow.

Why please?

回答1:

The stack overflow is due to the fact that you have several unrelated if, so that they produce a value which is not consumed and continue to execute the rest of the body of the function. This means that check6 is never terminated and causes the overflow.

If you paste your code in a proper editor, which automatically align the lines of code, you could discover that the editor produces this alignment:

(defun check6 (list) 
  (if (null list) 
      'TRUE)             ; only one branch, no else branch, continue to the next form
  (if (not (numberp(first list))) 
      nil)               ; again only one branch, continue to the next form
  (check6 (rest list)))  ; infinite loop

If you want to use the if special operator, you should remember that it has two cases, for when the condition is true and when it is false, and should nest the forms in this way (again with the proper alignment):

(defun check6 (list) 
  (if (null list)
      t
      (if (not (numberp (first list)))
          nil
          (check6 (rest list)))))

But Common Lisp has the much more convenient syntax for concatenating conditions, cond:

(defun check6 (list) 
  (cond ((null list) t)
        ((not (numberp (first list))) nil)
        (t (check6 (rest list)))))

Finally, note that there are other ways to solve your problem, either by using iteration:

(defun check6 (list)
  (loop for element in list always (numberp element)))

or with high-level functions, in a still more concise way:

(defun check6 (list)
  (every #'numberp list))


回答2:

You should definitely take a close look at the different ways that Renzo has written the function, but your code can be patched up by returning from the function early:

(defun check6 (list) 
  (if (null list) (return-from check6 'TRUE))
  (if (not (numberp(first list))) (return-from check6 nil))
  (check6 (rest list)))


回答3:

You get a stack overflow because the function never terminates.

The result of a function in Lisp is the value of the last expression in the function's body.
In your case that is (check6 (rest list)).

I suspect that you're thinking of some other language, where this might be written something like (in a completely fictitious language):

bool check6(List list)
{
    if (list.empty())
        return true;
    if (!isNumber(list.head()))
        return false;
    return check6(list.tail());
}

but your conditionals don't return their results from the function; their results are just discarded since you're not doing anything with them.

In the fictitious language above, your function would be

bool check6(List list)
{
    if (list.empty())
        true;
    if (!isNumber(list.head()))
        false;
    return check6(list.tail());
}

where you can probably see what's going wrong.

I personally find multi-pronged conditionals hard to follow compared to a logical expression.

If you write down the conditions for when a list is all numbers:

  • the list is empty, or
    • its head is a number and
    • its tail is all numbers

it's straightforward to translate into Lisp:

(defun check6 (list)
  (or (null list)
      (and (numberp (first list))
           (check6 (rest list)))))

In the fictitious language,

bool check6(List list)
{
    return list.empty() || (isNumber(list.head()) && check6(list.tail()));
}

But once you get acquainted with higher-order functions, you would probably write

(defun check6 (list)
  (every #'numberp list))

which might be the most Lisp-y solution.