Sorry, I forgot to CC.

---------- Forwarded message ----------
From: Gustavo <gugamilare@gmail.com>
Date: 2010/8/8
Subject: Re: [alexandria-devel] Fold operators on lists and trees in Alexandria?
To: Heka Treep <zena.treep@gmail.com>


Hello, Heka,

Just to clarify, I'm not a Alexandria developer, but I'll make some criticism.

I'm sorry but flatten/fold-right is not faster than flatten. It has a bug that prevents it from working correctly: fold-right version is destructive (it uses nreverse in the incoming list, it should be used in the result). Then, if you make a loop that flattens the same tree a lot of times, the original tree is modified so the running time is altered.

cl-user> (defvar tree '((1 2 3) (4 5 6)))
tree
cl-user> (flatten/fold-right tree)
(1 2 3 4 5 6)
cl-user> tree
((1))

It doesn't make sense flatten/fold-right to be faster than flatten because it calls append which needs to traverse the first argument, so it gets slower when the list to be flattened has large sublists. Also, it treats NIL as an atom while flatten treats it as the empty list.

The following version of flatten is the only one that I have noted to be faster than flatten (25% to 45% faster) because it doesn't have to call nreverse at the end:

(defun flatten/tail (tree)
š (let (list tail)

ššš (labels ((traverse (subtree)
šššššššššššššš (when subtree
šššššššššššššššš (cond
šššššššššššššššššš ((consp subtree)
ššššššššššššššššššš (progn
ššššššššššššššššššššš (traverse (car subtree))
ššššššššššššššššššššš (traverse (cdr subtree))))
šššššššššššššššššš (tail (setf (cdr tail) (cons subtree nil)
šššššššššššššššššššššššššššššš tail (cdr tail)))
šššššššššššššššššš (t (setf list (list subtree)
ššššššššššššššššššššššššššš tail list))))))
ššššš (traverse tree))
ššš list))

Don't worry, everyone commits mistakes. It took me some time to figure out the problem too.

Gustavo.

2010/8/7 Heka Treep <zena.treep@gmail.com>
Sorry, I made a small typo:


(defun flatten/fold-rigth (list)
š (fold-rigth #'(lambda (e rest)
ššššššššššššššššš (typecase e
ššššššššššššššššššš (atom (cons e rest))
ššššššššššššššššššš - (list (append (flatten e) rest))
ššššššššššššššššššš + (list (append (flatten/fold-rigth e) rest))))
ššššššššššššš list nil))

But this, of course, does not affect the benchmark
.

2010/8/8 Heka Treep <zena.treep@gmail.com>

Hi.

I was found that there is no effective fold operators on lists or trees in CL. I know that `reduce' can do this (as an article in Wikipedia sayz) but `reduce' is not very effective. As stated in the Graham Hutton's article ``fold is a standard operator that encapsulates a simple pattern of recursion for processing lists'', also catamorphism at some ADT plays the same role. So I thought that if we introduce an effective fold operators, it becomes possible to express many functions through its shortly and effectively (in fact, almost any function on that ADT).

Take for example the `flatten' function that is defined in Alexandria as follows:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ok, TCO recursion:
;;
(defun flatten (tree)
š (let (list)
ššš (labels ((traverse (subtree)
šššššššššššššš (when subtree
šššššššššššššššš (if (consp subtree)
šššššššššššššššššššš (progn
šššššššššššššššššššššš (traverse (car subtree))
šššššššššššššššššššššš (traverse (cdr subtree)))
šššššššššššššššššššš (push subtree list)))))
ššššš (traverse tree))
ššš (nreverse list)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

We need an ADT for the trees, but in the first approximation we can use nested lists.

When expressed in terms of reduce:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun flatten/reduce (list)
š (reduce #'(lambda (e rest)
ššššššššššššššššš (typecase e
ššššššššššššššššššš (atom (cons e rest))
ššššššššššššššššššš (list (append (flatten/reduce e) rest))))
ššššššššš list
ššššššššš :initial-value nil
ššššššššš :from-end t))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Now if we translate pure functional operator (here is `reduce') to the instructions for tagbody/go "state machine":

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; fold-left f '(a b c) i
;;;
;;;šššššš f
;;;ššššš /\
;;;šššš fš c
;;;ššš /\
;;;šš fš b
;;;š /\
;;; iš a
;;;
;;; foldl f z []šššš = z
;;; foldl f z (x:xs) = foldl f (f z x) xs

(defun fold-left (function list &optional initial-value)
š (let ((result initial-value))
ššš (tagbody
šššš :start
šššš (unless (endp list)
šššššš (setq result (funcall function result (car list)))
šššššš (setq list (cdr list))
šššššš (go :start)))
ššš result))

;;; fold-rigth f '(a b c) i
;;;
;;;šš f
;;;š /\
;;; aš f
;;;ššš /\
;;;šš bš f
;;;ššššš /\
;;;šššš cš i
;;;
;;; foldr f z []šššš = z
;;; foldr f z (x:xs) = f x (foldr f z xs)

(defun fold-rigth (function list &optional initial-value)
š (let ((result initial-value)
ššššššš (list (nreverse list)))
ššš (tagbody
šššš :start
šššš (unless (endp list)
šššššš (setq result (funcall function (car list) result))
šššššš (setq list (cdr list))
šššššš (go :start)))
ššš result))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Then `flatten' can be written as:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun flatten/fold-rigth (list)
š (fold-rigth #'(lambda (e rest)
ššššššššššššššššš (typecase e
ššššššššššššššššššš (atom (cons e rest))
ššššššššššššššššššš (list (append (flatten e) rest))))
ššššššššššššš list nil))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

I try to benchmarc (this three functions) and has the following results:

flatten/fold-rigth

X time
Y memory

alexandria:flatten

10 * X time
23 * Y memory

flatten/reduce

42 * X time
83 * Y memory

So, its look resonable to use folders there.



((sorry for my english - I just using google.translate :))


_______________________________________________
alexandria-devel mailing list
alexandria-devel@common-lisp.net
http://common-lisp.net/cgi-bin/mailman/listinfo/alexandria-devel