Как передать «прикладную функцию» в качестве параметра в Common Lisp - PullRequest
4 голосов
/ 10 мая 2019

Я новичок в Common Lisp и пытаюсь реализовать repeatedly из Clojure. Например

(repeatedly 5 #(rand-int 11))

Это соберет 5 (rand-int 11) вызовов и вернет список: (10 1 3 0 2)

В настоящее время это то, что я делаю:

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

Что выглядит не так хорошо, я должен назвать это так: (repeatedly 5 #'random '(11)). Есть ли способ сделать функцию более интуитивно понятной, как в синтаксисе Clojure?

Код может стать довольно уродливым: (repeatedly 5 #'function (list (- x 1))).

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

Ответы [ 3 ]

6 голосов
/ 11 мая 2019

В то время как Дэниелс отвечает идеально, я хотел бы отметить классную вещь о CL.Вы можете реализовать такой же макрос для чтения, как в Clojure.Теперь, поскольку #(1 2 3) является литералом массива в CL, я реализую [...] с функциями [...] и #(...).

(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 #\)))

Я не потратил время на поиск %s и _, поэтому он не оптимален.Вот несколько примеров:

(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)
6 голосов
/ 11 мая 2019

Остальные аргументы

Можно также написать это как

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

Таким образом, нет необходимости создавать список аргументов самостоятельно.

Тогда его называют:

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

вместо (repeatedly 5 #'random (list (1- x)))

сокращенная запись через макросы

короткие лямбда-нотации также могут быть достигнуты с помощью макросов для некоторых целей:

> (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)

стиль

Часто также не нужно и не желательно писать языковые конструкции как макрос чтения . Расширение s-выражений лучше оставить ниже уровня языка программирования -> s-выражения в основном представляют собой синтаксис данных.

В большинстве кодов на Лиспе фактически можно найти lambda выражений, используемых без каких-либо сокращений. Обычный стиль Lisp - это использование символических имен, а не столько специальных символов или специального синтаксиса лексики / токена. Это делает текст немного длиннее, но имеет и другие преимущества. Например, можно увидеть одно и то же lambda в тексте и в прочитанном или даже запущенном коде ...

6 голосов
/ 11 мая 2019

Я не уверен, правильно ли я понимаю ваш вопрос, но, может быть, просто что-то вроде этого:

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

Поскольку #(…) это просто сокращение для лямбд в Clojure.

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

Но это даже немного короче:

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