2

EDIT: The solution is to replace the '(1) with (list 1) in the first (let...) form. This is because I was trying to modify literal data. Thanks for the help! (I would give upvotes, but apparently you need 15 reputation...)

This is my first post on this site.

I was solving some Project Euler problems today and I came across some unexpected list sorting behavior (well, at least to me) in Common Lisp:

I have a function that finds all of the proper divisors of a number x:

(defun divisors (x)
    "Finds all of the proper divisors of x."
    (let ((sq (sqrt x)) (divs '(1)))
        (when (integerp sq) (push sq divs))
        (loop for i from 2 to (1- (floor sq)) do
        (let ((div (/ x i)))
            (when (integerp div)
                (push i divs)
                (push div divs))))
    divs))

This function works great. For example:

(divisors 100)
==> (20 5 25 4 50 2 10 1)

The problem arises whenever I try to sort the resulting list:

(sort (divisors 100) #'<)
==> (1 2 4 5 10 20 25 50)

Well, that worked fine. But what happens when I call divisors again?

(divisors 100)
==> (20 5 25 4 50 2 10 1 2 4 5 10 20 25 50)

What? Maybe if I try a different number...

(divisors 33)
==> (11 3 1 2 4 5 10 20 25 50)

The divisors from previous queries are persistent after I sort the resulting list. If I recompile the function, I get no errors until I sort the resulting list again. I am assuming I have messed up somewhere in the function definition, but I am pretty new to Lisp and I cannot find the error. Could it be a problem with the nested (let...) forms?

Thanks in advance!

Mofi
  • 46,139
  • 17
  • 80
  • 143
John David Reaver
  • 1,238
  • 1
  • 10
  • 17

2 Answers2

9

Frequently asked.

You've modified literal data. You need to cons up new data. Use the function LIST or the function to make a copy.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
  • Thanks for the response! Could you be more explicit? I'm afraid I don't understand. – John David Reaver Apr 14 '12 at 17:40
  • 3
    @Lisper `'(1)` is a list literal. The consequences of using destructive operations on literals are undefined, and `sort` is a destructive operation. Use the `list` function to construct lists. – Matthias Benkard Apr 14 '12 at 17:47
3

Rainer is right; if it isn't obvious, the code can be fixed with this change:

(let ((sq (sqrt x)) (divs (list 1)))
Doug Currie
  • 40,708
  • 1
  • 95
  • 119