From: joswig on

Frank Buss wrote:
> joswig(a)corporate-world.lisp.de wrote:
>
> > For combinations of this kind of stuff I really prefer the functional
> > approach:
> > CL-USER 5 > (remove-if-not 'oddp '(1 2 3))
> > (1 3)
>
> I know it is possible, but I'm not sure aboout how to write the following
> with a functional approach:
>
> > (loop for i from 1 to 10 collect (loop for j from 1 to i collect j))
> ((1) (1 2) (1 2 3) (1 2 3 4) (1 2 3 4 5) (1 2 3 4 5 6) (1 2 3 4 5 6 7) (1 2
> 3 4 5 6 7 8) (1 2 3 4 5 6 7 8 9) (1 2 3 4 5 6 7 8 9 10))

This looks like a simple recursive problem. A typical task for
Functional
Programming.

Did you ever read SICP? After reading that book, writing that code in
a functional style would be easy...

In a simple style I would

a) generate a list of 1 to 10
b) map over that list and generate sublists that go from 1 to n

Often the function to generate integers from 0..n is called IOTA:

here is the definition from AIMA:

(defun iota (n &optional (start-at 0))
"Return a list of n consecutive integers, by default starting at 0."
(if (<= n 0) nil (cons start-at (iota (- n 1) (+ start-at 1)))))

CL-USER 3 > (iota 10 1)
(1 2 3 4 5 6 7 8 9 10)

Mapping over a list and returning a list of results is done with
MAPCAR:

CL-USER 4 > (mapcar (lambda (n) (iota n 1)) (iota 10 1))
((1) (1 2) (1 2 3) (1 2 3 4) (1 2 3 4 5) (1 2 3 4 5 6) (1 2 3 4 5 6 7)
(1 2 3 4 5 6 7 8) (1 2 3 4 5 6 7 8 9) (1 2 3 4 5 6 7 8 9 10))


>
> --
> Frank Buss, fb(a)frank-buss.de
> http://www.frank-buss.de, http://www.it4-systems.de

From: Frank Buss on
joswig(a)corporate-world.lisp.de wrote:

> In a simple style I would
>
> a) generate a list of 1 to 10
> b) map over that list and generate sublists that go from 1 to n
>
> Often the function to generate integers from 0..n is called IOTA:
>
> here is the definition from AIMA:
>
> (defun iota (n &optional (start-at 0))
> "Return a list of n consecutive integers, by default starting at 0."
> (if (<= n 0) nil (cons start-at (iota (- n 1) (+ start-at 1)))))

This should be written in Common Lisp like this:

(defun iota (n &optional (start-at 0))
"Return a list of n consecutive integers, by default starting at 0."
(when (> n 0) (cons start-at (iota (1- n) (1+ start-at)))))

> Mapping over a list and returning a list of results is done with
> MAPCAR:
>
> CL-USER 4 > (mapcar (lambda (n) (iota n 1)) (iota 10 1))
> ((1) (1 2) (1 2 3) (1 2 3 4) (1 2 3 4 5) (1 2 3 4 5 6) (1 2 3 4 5 6 7)
> (1 2 3 4 5 6 7 8) (1 2 3 4 5 6 7 8 9) (1 2 3 4 5 6 7 8 9 10))

Nice. Another solution with more higher order functions:

(defun iota ()
(lambda (n &optional (start-at 0))
(let ((end (+ n start-at)))
(labels ((next (n)
(when (< n end) (cons n (next (1+ n))))))
(next start-at)))))

; alternative:
(defun non-recursive-iota ()
(lambda (n &optional (start-at 0))
(loop for i from start-at below (+ start-at n) collect i)))

(defun iota-from-1 ()
(lambda (n)
(funcall (iota) n 1)))

(defun test ()
(mapcar (iota-from-1) (funcall (iota-from-1) 10)))

(no currying in Common Lisp without libraries like ARNESI, so there is no
way to write the first argument to mapcar much easier than with a lambda)

--
Frank Buss, fb(a)frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Rainer Joswig on
Am 26.06.2006 22:28 Uhr schrieb "Frank Buss" unter <fb(a)frank-buss.de> in
pvyi2g18y73m$.a94ixhwfcotg.dlg(a)40tude.net:

> joswig(a)corporate-world.lisp.de wrote:
>
>> In a simple style I would
>>
>> a) generate a list of 1 to 10
>> b) map over that list and generate sublists that go from 1 to n
>>
>> Often the function to generate integers from 0..n is called IOTA:
>>
>> here is the definition from AIMA:
>>
>> (defun iota (n &optional (start-at 0))
>> "Return a list of n consecutive integers, by default starting at 0."
>> (if (<= n 0) nil (cons start-at (iota (- n 1) (+ start-at 1)))))
>
> This should be written in Common Lisp like this:
>
> (defun iota (n &optional (start-at 0))
> "Return a list of n consecutive integers, by default starting at 0."
> (when (> n 0) (cons start-at (iota (1- n) (1+ start-at)))))

Not really. It is no improvement to use WHEN over IF ... NIL.
It is also no improvement to use -1 of (- ... 1). These are
basically useless improvements. The IF version makes clear that
the NIL result is really a wanted result. Additionally
in most self-respecting Lisps 1+ will not make any difference
to (+ 1 ...) .

>
>> Mapping over a list and returning a list of results is done with
>> MAPCAR:
>>
>> CL-USER 4 > (mapcar (lambda (n) (iota n 1)) (iota 10 1))
>> ((1) (1 2) (1 2 3) (1 2 3 4) (1 2 3 4 5) (1 2 3 4 5 6) (1 2 3 4 5 6 7)
>> (1 2 3 4 5 6 7 8) (1 2 3 4 5 6 7 8 9) (1 2 3 4 5 6 7 8 9 10))
>
> Nice. Another solution with more higher order functions:
>
> (defun iota ()
> (lambda (n &optional (start-at 0))
> (let ((end (+ n start-at)))
> (labels ((next (n)
> (when (< n end) (cons n (next (1+ n))))))
> (next start-at)))))

Well, IOTA is usually defined as a function that returns a vector (or LIST
in case of Lisp). And not as a function that returns a function. The
introduction of yet another LAMBDA is also useless. See below.

(defun iota (n &optional (start-at 0))
(let ((end (+ n start-at)))
(labels ((next (n)
(when (< n end) (cons n (next (1+ n))))))
(next start-at))))

> ; alternative:
> (defun non-recursive-iota ()
> (lambda (n &optional (start-at 0))
> (loop for i from start-at below (+ start-at n) collect i)))
>
> (defun iota-from-1 ()
> (lambda (n)
> (funcall (iota) n 1)))

(defun iota-from-1 (n)
(iota n 1))

>
> (defun test ()
> (mapcar (iota-from-1) (funcall (iota-from-1) 10)))

This version here is simpler. The function is directly used.

(defun test ()
(mapcar #'iota-from-1 (iota-from-1 10)))

If you wanted you can use arbitrary deep functions. Unless there is a reason
for it, and I don't see it here, I would stay away from that. Higher
Order does not mean cooler. ;-) In this case it is easy to detect.
Your toplevel function just returns another function without closing over
a variable. That's useless mostly. You could have used the toplevel function
directly.

What you may have wanted is a IOTA function generator:

(defun make-iota-function (&optional (start-at 0))
(lambda (n)
(let ((end (+ n start-at)))
(labels ((next (n)
(when (< n end) (cons n (next (1+ n))))))
(next start-at)))))


(setf (symbol-function 'iota-from-1)
(make-iota-function 1))

(defun test ()
(mapcar #'iota-from-1 (iota-from-1 10)))

Or

(defun test ()
(let ((iota-from-1-function (make-iota-function 1)))
(mapcar iota-from-1-function
(funcall iota-from-1-function 10))))


> (no currying in Common Lisp without libraries like ARNESI, so there is no
> way to write the first argument to mapcar much easier than with a lambda)

?

From: Rob Warnock on
Frank Buss <fb(a)frank-buss.de> wrote:
+---------------
| joswig(a)corporate-world.lisp.de wrote:
| > Often the function to generate integers from 0..n is called IOTA:
| > here is the definition from AIMA:
| > (defun iota (n &optional (start-at 0))
| > "Return a list of n consecutive integers, by default starting at 0."
| > (if (<= n 0) nil (cons start-at (iota (- n 1) (+ start-at 1)))))
|
| This should be written in Common Lisp like this:
| (defun iota (n &optional (start-at 0))
| "Return a list of n consecutive integers, by default starting at 0."
| (when (> n 0) (cons start-at (iota (1- n) (1+ start-at)))))
+---------------

Personally, I prefer this version:

(defun iota (count &optional (start 0) (step 1))
(loop repeat count for i upfrom start by step collect i))

Besides the obvious uses:

> (iota 5)

(0 1 2 3 4)
> (iota 5 3)

(3 4 5 6 7)
> (iota 5 3 3)

(3 6 9 12 15)
>

it can be used for other things as well:

> (iota 5 3 0)

(3 3 3 3 3)
> (iota 5 5 -1)

(5 4 3 2 1)
>

Though note that these latter cases technically violate the CLHS
requirement that LOOP's BY prepositions's value be positive:

6.1.2.1.1 The for-as-arithmetic subclause
...
BY The loop keyword BY marks the increment or decrement
supplied by FORM3. The value of FORM3 can be any
positive number. The default value is 1.

Both cases work in CMUCL & CLISP. Is there a CL implementation
that does *not* accept zero and/or negative BY values?


-Rob

-----
Rob Warnock <rpw3(a)rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607

From: Pascal Bourguignon on
rpw3(a)rpw3.org (Rob Warnock) writes:
> Personally, I prefer this version:
>
> (defun iota (count &optional (start 0) (step 1))
> (loop repeat count for i upfrom start by step collect i))
> [...]
> Though note that these latter cases technically violate the CLHS
> requirement that LOOP's BY prepositions's value be positive:
>
> 6.1.2.1.1 The for-as-arithmetic subclause
> ...
> BY The loop keyword BY marks the increment or decrement
> supplied by FORM3. The value of FORM3 can be any
> positive number. The default value is 1.
>
> Both cases work in CMUCL & CLISP. Is there a CL implementation
> that does *not* accept zero and/or negative BY values?

You can easily make it conformant:

(defun iota (count &optional (start 0) (step 1))
(cond
((minusp step)
(loop :repeat count :for i :downfrom start :by (- step) :collect i))
((plusp step)
(loop :repeat count :for i :upfrom start :by step :collect i))
(t
(loop :repeat count :collect start))))

(mapcar (lambda (step) (iota 5 10 step)) '(-1 0 1))
--> ((10 9 8 7 6) (10 10 10 10 10) (10 11 12 13 14))

--
__Pascal Bourguignon__ http://www.informatimago.com/

Pour moi, la grande question n'a jamais ?t?: ?Qui suis-je? O? vais-je??
comme l'a formul? si adroitement notre ami Pascal, mais plut?t:
?Comment vais-je m'en tirer?? -- Jean Yanne