stuck

Discussion of Common Lisp
nuntius
Posts: 538
Joined: Sat Aug 09, 2008 10:44 am
Location: Newton, MA

Re: stuck

Post by nuntius » Sun Aug 29, 2010 8:53 pm

burton wrote:(dolist (sum (lst)
(sum 0))
(if (t(oddp lst))
(setq sum (+ 1 sum)
(
)

im not sure..
Close, but there are still a few oddities in your code.

DOLIST doesn't take the same parameter list as DO. The function prototype is (DOLIST (var list [result]) body), where the result-form is optional. Use LET to create a counter outside the loop: (let ((count 0)) ...). Then iterate over each item in a list: (dolist (item list) ...).

I'm not sure what the first clause in your IF does. It should look something like (if (oddp item) (incf count)), or preferably use WHEN for IF statements with no "else" clause.


[OT]: Recursion is the "Schemey" way to write code. It is not so popular in common lisp.

s-imp
Posts: 11
Joined: Sat Aug 28, 2010 5:04 am

Re: stuck

Post by s-imp » Sun Aug 29, 2010 9:53 pm

;) Got'cha

findinglisp
Posts: 447
Joined: Sat Jun 28, 2008 7:49 am
Location: Austin, TX
Contact:

Re: stuck

Post by findinglisp » Mon Aug 30, 2010 3:18 pm

s-imp wrote:Yeah, I thought about 'length', but I knew to avoid creating cons cells for a new list as you do get a performance penalty for it.

Thank you both for the info on tail-recursion. So my function is not tail recursive (as it calls #'+ last). I traced it and then disassembled it to see what was going on. Very interesting.
It should also be said that Common Lisp makes no guarantees around tail recursion; implementations are not required to be properly tail recursive. In practice, some are, but that's really outside the dictum of the standard. That stands in strong contrast to Scheme where every conforming implementation to RxRS is required to be properly tail recursive. As a result, Scheme tends to rely much more heavily on tail recursion to implement loops, whereas CL has a stronger set of iteration constructs (e.g., DO, DO*, DOLIST, LOOP, etc.).
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Warren Wilkinson
Posts: 117
Joined: Tue Aug 10, 2010 11:24 pm
Location: Calgary, Alberta
Contact:

Re: stuck

Post by Warren Wilkinson » Mon Aug 30, 2010 3:21 pm

Here are 5 ways of doing it. I added timing for SBCL on my computer without tweaking any optimization stuff.

Code: Select all

;; using a reduction
(defun odd-count1 (list) (reduce #.(lambda (acc i) (if (oddp i) (1+ acc) acc)) list :initial-value 0))

;; using 'loop'
(defun odd-count2 (list) (loop for i in list counting (oddp i)))

;; Using recursion
(defun odd-count3 (lst)
  (if (null lst)
      0
      (+ (if (oddp (first lst)) 1 0)
	 (odd-count3 (rest lst)))))

;; using tail recursion
(defun odd-count4 (lst count) (if (null lst) count (odd-count4 (cdr lst) (if (oddp (car lst)) (1+ count) count))))

;; using destructive updates and jumps
(defun odd-count5 (lst)
  (let ((count (the fixnum 0)))
    (tagbody :start (when lst (when (oddp (car lst)) (incf count)) (setf lst (cdr lst)) (go :start)))
    count))

(defvar *data* (loop repeat 1024 collecting (random 256)))

(time (dotimes (i 1000)(odd-count1 *data*)))    ;; 0.038 seconds
(time (dotimes (i 1000)(odd-count2 *data*)))    ;; 0.023 seconds
(time (dotimes (i 1000)(odd-count3 *data*)))    ;; 0.048 seconds
(time (dotimes (i 1000)(odd-count4 *data* 0)))  ;; 0.031 seconds
(time (dotimes (i 1000)(odd-count5 *data*)))    ;; 0.023 seconds
Need an online wiki database? My Lisp startup http://www.formlis.com combines a wiki with forms and reports.

findinglisp
Posts: 447
Joined: Sat Jun 28, 2008 7:49 am
Location: Austin, TX
Contact:

Re: stuck

Post by findinglisp » Mon Aug 30, 2010 3:31 pm

Warren Wilkinson wrote:Here are 5 ways of doing it. I added timing for SBCL on my computer without tweaking any optimization stuff.

Code: Select all

(time (dotimes (i 1000)(odd-count1 *data*)))    ;; 0.038 seconds
(time (dotimes (i 1000)(odd-count2 *data*)))    ;; 0.023 seconds
(time (dotimes (i 1000)(odd-count3 *data*)))    ;; 0.048 seconds
(time (dotimes (i 1000)(odd-count4 *data* 0)))  ;; 0.031 seconds
(time (dotimes (i 1000)(odd-count5 *data*)))    ;; 0.023 seconds
The LOOP macro typically expands into a TAGBODY/GO under the hood, which explains why its performance is similar. If I remember right, SBCL also implements proper tail recursion, so ODD-COUNT4 also does well. Real recursion and REDUCE involve lots of true function calls, so there's higher overhead there.

IMO, LOOP is almost always a win and I tend to use it in preference to other styles, unless the problem screams for recursion (whether tail-recursion or not).

That said, find your style. Lisp allows a lot of different styles. Most code is not performance-critical.

-- Dave
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Post Reply