Pedro Queiroga
Pedro Queiroga

Reputation: 391

How to pass "applied function" as a parameter in Common Lisp

I am new to Common Lisp, and am trying to implement a repeatedly from Clojure. For example

(repeatedly 5 #(rand-int 11))

This will collect 5 (rand-int 11) calls, and return a list: (10 1 3 0 2)

Currently, this is what I'm doing:

(defun repeatedly (n f args)
  (loop for x from 1 to n
       collect (apply f args)))

Which doesn't look as nice, I have to call it like this: (repeatedly 5 #'random '(11)). Is there a way to make the function more intuitive, as in Clojure's syntax?

The code can then get pretty ugly: (repeatedly 5 #'function (list (- x 1))).

https://clojuredocs.org/clojure.core/repeatedly

Upvotes: 4

Views: 677

Answers (3)

Rainer Joswig
Rainer Joswig

Reputation: 139261

Rest args

One can write it also as

(defun repeatedly (n f &rest args)
  (loop repeat n collect (apply f args)))

Thus there is no need to create the argument list yourself.

Then one calls it:

> (repeatedly 5 #'random (1- x))
(7 2 3 1 4)

instead of (repeatedly 5 #'random (list (1- x)))

shorter notation via macros

short lambda notations can also be achieved via macros for some purposes:

> (defun repeatedly (n function)
    (loop repeat n collect (funcall function)))
REPEATEDLY

> (repeatedly 5 (lambda () (random 10)))
(1 7 1 7 8)

> (defmacro ⧨ (&body body) `(lambda () ,@body))
⧨

> (repeatedly 5 (⧨ (random 10)))
(9 3 0 7 0)

or alternatively:

> (defmacro ⧩ (&body body) `(lambda () ,body))
⧩

> (repeatedly 5 (⧩ random 10))
(9 7 7 7 5)

style

Often it is also not necessary nor desirable to write language constructs as reader macros. The extension of s-expressions is best left below the programming language level -> s-expressions are mostly a data syntax.

In most Lisp code one actually finds lambda expressions used without any abbreviation. The usual Lisp style is to use symbolic names and not so much special characters or special lexical/token syntax. It makes the text slightly longer, but has other advantages. For example one sees the same lambda in text and in the read or even running code...

Upvotes: 6

Sylwester
Sylwester

Reputation: 48745

While Daniels answer perfect, I'd like to point out a cool thing about CL. You can implement a similar reader macro as Clojure has. Now since #(1 2 3) is a array literal in CL I'll implement [...] with both [...] and #(...) features.

(set-macro-character #\[
  (lambda (stream char)
    (declare (ignore char))
    (let ((body (read-delimited-list #\] stream t)))
      `(lambda (&optional %1 %2 %3 %4 %5 &aux (_ %1)) ,body))))

(set-macro-character #\]
  (get-macro-character #\)))

I didn't take the time to search for the %s and _ so it isn't optimal. Here are some examples:

(mapcar [sqrt (+ (* %1 %1) (* %2 %2))] '(1 3 5) '(2 4 6))
; ==> (2.236068 5 7.81025)
(mapcar [* _ 2] '(2 4 6))    
; ==> (4 8 12)
(repeatedly 5 [rand-int 11]) 
; ==> (10 1 3 0 2)

Upvotes: 6

danlei
danlei

Reputation: 14291

I'm not sure if I understand your question correctly, but maybe just something like this:

(defun repeatedly (n function)
  (loop repeat n collect (funcall function)))

Since #(…) is just a shorthand for lambdas in Clojure.

CL-USER> (repeatedly 5 (lambda () (random 11)))
(0 8 3 6 2)

But this is even a bit shorter:

CL-USER> (loop repeat 5 collect (random 11))
(5 4 6 2 3)

Upvotes: 6

Related Questions