4

I've just started learning Lisp, and I'm working through some Project Euler problems. I'm stuck on the one where you sum the even Fibonacci numbers below a max number. The things I've tried follow. I've read this post but I'm still not sure why neither of my ways works!

CL-USER> (defun sum-even-fibs (max)
           (do ((curr 0 next)
                (next 1 (+ curr next))
                (sum  0 (if (evenp curr) (+ sum curr))))
               ((> max curr) sum)))
SUM-EVEN-FIBS
CL-USER> (sum-even-fibs 10)
0


CL-USER> (defun sum-even-fibs (max)
           (let ((sum 0))
             (do ((curr 0 next)
                  (next 1 (+ curr next)))
                 ((> max curr))
               (if (evenp curr) 
                   (setq sum (+ sum curr))))
             (format t "~d" sum)))
SUM-EVEN-FIBS
CL-USER> (sum-even-fibs 10)
0
NIL
Community
  • 1
  • 1
lightlike
  • 937
  • 1
  • 8
  • 12

3 Answers3

5

Slightly 'better' version of the answer of arbautjc:

(loop for a = 0 then b and b = 1 then (+ a b) 
      while (< a 4000000) 
      when (evenp a) sum a)
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
  • Thanks. Do you gain a better intuition for which looping structure to use in different situations (do, loop, dolist, dotimes, etc.) with more practice? – lightlike May 02 '13 at 19:47
  • @lightlike - Yes. (Though for what it's worth, I find the answer is typically `loop`, sometimes `mapcar` and very occasionally `dolist`. YMMV, of course, but I haven't used `dotimes` or `do` in a very long time). – Inaimathi Jun 10 '14 at 14:40
3

You're terminating the do loop as soon as max is greater than sum, which happens at the very first iteration.

After you switch that > to a <, though, you'll get an arithmetic error because you'll eventually bind sum to nil (when you update to (if (evenp curr) (+ sum curr)) which is nil when (evenp curr) is false. You need to provide the else-side as well; it should be sum.

More importantly, do binds its values in parallel, not in sequence, which means that for the second iteration when you update sum to (if (evenp curr) (+ sum curr) sum), you're using the curr and sum from the first iteration. If that's not what you're intending, you should consider using do*.

Update

As per the request in the comments, here's a complete working version of the code. Note that it is almost identical to the code in the question; it only swaps the order of arguments to > so that termination is when the current Fibonacci number is greater than the maximum value, and adds the else case to the if expression, so that when (evenp curr) is false, the value is sum is preserved.

(defun sum-even-fibs (max)
  (do ((curr 0 next)
       (next 1 (+ curr next))
       (sum 0 (if (evenp curr) (+ sum curr) sum)))
      ((> curr max) sum)))
Community
  • 1
  • 1
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
  • Thank you! I knew there would be an issue with evaluation sequence in the first iteration but I wanted to make sure the function would actually do something first. – lightlike May 02 '13 at 03:27
  • As a lisp n00b, I'd love to see the correct code for this. The problem I'm running into is: I can do a loop, I can do the if, but I'm getting stuck on how to (a) initialize sum to 0 and (b) display the result. Thanks! – Olie Jul 08 '13 at 20:55
  • @Olie I've posted the modified code. It's almost identical to the original, but with the changes that I mentioned. The comment about `do/do*` was only cautionary; for this problem `do` was right. – Joshua Taylor Jul 09 '13 at 12:25
1

The loop instruction has many nice features:

(loop with a = 0 and b = 1 
      while (< a 4000000) 
      when (evenp a)
      sum a
      do (psetf a b b (+ a b)))

See reference in Common Lisp HyperSpec