Many thanks to tcr in #lisp for convincing me to improve this code to
handle multiple arguments. I disagreed with his idea that it should
take n as its first argument. he thought it was confusing, but I did
not see why:
(defun ncall (f n &rest data)
"FUNCTION f -> INTEGER n -> d1 .. dn -> (f (f ... (apply f
data) ... ))"
(if (= 1 n)
(apply f data)
(funcall f (ncall f (1- n) data))))
I wrote this because I had a function which prepended a single unit
axis onto an array. However I needed to append n axes onto the array
but didnt feel like modifyig the original function:
(defun prepend-unit-axis (n)
"NOUN n -> NOUN n with prepended unit axis"
(if (arrayp n)
(let ((dims (array-dimensions n)))
(make-array (cons 1 dims) :displaced-to (array->vector n)))
(vector n)))
(ncall #'prepend-unit-axis 3 (vector 12 34))
On Dec 25, 12:16 am, "livingcosmos.org" <········@gmail.com> wrote:
> Many thanks to tcr in #lisp for convincing me to improve this code to
> handle multiple arguments. I disagreed with his idea that it should
> take n as its first argument. he thought it was confusing, but I did
> not see why:
>
> (defun ncall (f n &rest data)
> "FUNCTION f -> INTEGER n -> d1 .. dn -> (f (f ... (apply f
> data) ... ))"
> (if (= 1 n)
> (apply f data)
> (funcall f (ncall f (1- n) data))))
The documentation for the function is confusing.
You say:
"FUNCTION f -> INTEGER n -> d1 .. dn -> (f (f ... (apply f
data) ... ))"
But you don't apply f to data. You apply f n times to data that is n-1
times listified.
? (ncall #'print 4 1)
(((1)))
(((1)))
(((1)))
(((1)))
(((1)))
? (ncall #'print 4 1 2 3)
(((1 2 3)))
(((1 2 3)))
(((1 2 3)))
(((1 2 3)))
(((1 2 3)))
? (ncall #'list 4 1 2 3)
(((((((1 2 3)))))))
Your documentation would indicate something like this:
? (defun ncall (f n &rest data)
"FUNCTION f -> INTEGER n -> d1 .. dn -> (f (f ... (apply f
data) ... ))"
(if (= 1 n)
(apply f data)
(funcall f (apply #'ncall f (1- n) data))))
NCALL
? (ncall #'list 4 1 2 3)
((((1 2 3))))
? (ncall #'print 4 1)
1
1
1
1
1
I also don't think that style of documentation is best, since Common
Lisp does not
support implicit currying. Arglists are as specified and omitting an
argument does not
create a curried function.
>
> I wrote this because I had a function which prepended a single unit
> axis onto an array. However I needed to append n axes onto the array
> but didnt feel like modifyig the original function:
>
> (defun prepend-unit-axis (n)
> "NOUN n -> NOUN n with prepended unit axis"
> (if (arrayp n)
> (let ((dims (array-dimensions n)))
> (make-array (cons 1 dims) :displaced-to (array->vector n)))
> (vector n)))
>
> (ncall #'prepend-unit-axis 3 (vector 12 34))
On Dec 24, 3:16 pm, "livingcosmos.org" <········@gmail.com> wrote:
> Many thanks to tcr in #lisp for convincing me to improve this code to
> handle multiple arguments. I disagreed with his idea that it should
> take n as its first argument. he thought it was confusing, but I did
> not see why:
>
> (defun ncall (f n &rest data)
> "FUNCTION f -> INTEGER n -> d1 .. dn -> (f (f ... (apply f
> data) ... ))"
> (if (= 1 n)
> (apply f data)
> (funcall f (ncall f (1- n) data))))
Surely, don't you mean:
(funcall f (apply #'ncall f (1- n) data))
You want to bounce the arguments down into the recursive call, not a
list of them as one argument, right?
Is this what you are looking for:
(defun ncall (f n &rest data)
(loop repeat n
for result = (apply f data) then (funcall f result)
finally (return result)))
Handles large N without tail call optimization; returns NIL if N is
nonpositive.