Reputation: 391
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
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
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
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