Можно ли использовать лямбду с лямбда-списком на лету (без макросов)? - PullRequest
0 голосов
/ 14 сентября 2018

Я пытаюсь создать функцию для возврата функций с произвольными лямбда-списками, сгенерированными на лету. Я могу сделать это с помощью макросов, но я пытаюсь де-макро-ифи, что я уже получил:

(defmacro make-canned-format-macro (template field-names)
  `(function (lambda ,field-names
               (apply #'format `(nil ,,template ,,@field-names)))))

Я могу использовать его следующим образом:

* (make-canned-format-macro "~A-powered ~A" (fuel device))

#<FUNCTION (LAMBDA (FUEL DEVICE)) {10067D975B}>
* (setf (fdefinition 'zoom-zoom) (make-canned-format-macro "~A-powered ~A" (fuel device)))

#<FUNCTION (LAMBDA (FUEL DEVICE)) {1006835A5B}>
* (zoom-zoom "nuclear" "pogo stick")

"nuclear-powered pogo stick"

Это именно то поведение, которое я хочу. Он возвращает функцию, чей лямбда-список был предоставлен на лету (в данном случае (fuel device).) Я пытаюсь выполнить правильную процедуру рефакторинга Lisp и покончить с макросами, которые не обязательно должны быть макросами. Однако я застрял, пытаясь превратить произвольный лямбда-список в lambda, который выполняется в функции:

* (defun make-canned-format (template field-names)
    #'(lambda field-names (apply #'format `(nil ,template ,@field-names))))
; in: DEFUN MAKE-CANNED-FORMAT
;     #'(LAMBDA FIELD-NAMES (APPLY #'FORMAT `(NIL ,TEMPLATE ,@FIELD-NAMES)))
; 
; caught ERROR:
;   The lambda expression has a missing or non-list lambda list:
;     (LAMBDA FIELD-NAMES (APPLY #'FORMAT `(NIL ,TEMPLATE ,@FIELD-NAMES)))

;     (SB-INT:NAMED-LAMBDA MAKE-CANNED-FORMAT
;         (TEMPLATE FIELD-NAMES)
;       (BLOCK MAKE-CANNED-FORMAT
;         #'(LAMBDA FIELD-NAMES (APPLY #'FORMAT `(NIL ,TEMPLATE ,@FIELD-NAMES)))))
; 
; caught STYLE-WARNING:
;   The variable TEMPLATE is defined but never used.
; 
; caught STYLE-WARNING:
;   The variable FIELD-NAMES is defined but never used.
; 
; compilation unit finished
;   caught 1 ERROR condition
;   caught 2 STYLE-WARNING conditions

MAKE-CANNED-FORMAT

Возможно ли то, что я пытаюсь сделать? (За исключением какого-то отвратительного eval хака, который был бы менее читабельным, чем макрос, я имею в виду.)

Ответы [ 3 ]

0 голосов
/ 15 сентября 2018

Во-первых, ваш макрос слишком сложный, вам не нужно отправлять вызов для применения и создавать промежуточный список аргументов, если вы заранее знаете, сколько аргументов будет использовано.Вот еще одна версия:

(defmacro lambda-format ((&rest args) template)
  `(lambda ,args (format nil ,template ,@args)))

Вы можете избавиться от макросов, используя функции с переменными числами и APPLY, но это означает, что, проверяя только сгенерированную функцию (используя, например, проверять или описывать), вы не можете знать,заранее, сколько аргументов необходимо:

(defun curry-format (template)
  (lambda (&rest args)
    (apply #'format nil template args)))

В случае формата вы можете использовать макрос FORMATTER, который может анализировать формат шаблона и предупреждатьвы до вы даете ему аргументы во время выполнения:

(defmacro template ((&rest args) template)
  (let ((format-fn (gensym))
        (template-fn (copy-symbol :template)))
    `(let ((,format-fn (formatter ,template)))
       (flet ((,template-fn ,args (funcall ,format-fn nil ,@args)))
         (function ,template-fn)))))

Здесь я использую FLET, чтобы сгенерированная функция имела понятное имя, но вы также можете использовать лямбду.

(template (a b) "~x ~b")
#<FUNCTION (FLET "TEMPLATE") {1002B93D0B}>

Если вы назовите его, то можете увидеть, что подпись является точной:

Lambda-list: (A B)

Это не относится к варианту с переменным числом.

Макрос ожидает строковые литералы и может проверять, содержит ли он допустимый формат во время макроразложения:

(template (a b) "~x ~!")
 ;; error in FORMAT: Unknown directive (character: EXCLAMATION_MARK)
 ;;  ~x ~!

Учитывая, как указано FORMATTER, вы не будете предупреждены, если число фактических аргументов отличается от числаожидаемых аргументов.Вы получите ошибку во время выполнения, если задано слишком мало заданных аргументов, и список неиспользуемых аргументов в качестве возвращаемого значения, если задано слишком много заданных аргументов (этот список также можно проверить, чтобы выдать ошибку).

0 голосов
/ 15 сентября 2018
(defun make-canned-format (template field-names)
   #'(lambda field-names (apply #'format `(nil ,template ,@field-names))))

Это невозможно. В лямбда-выражении список параметров является списком, а не произвольным символом, который он затем оценивает. Common Lisp ожидает фиксированный список параметров, а не переменную. Этот список не оценивается:

(lambda (a b c)    ; (a b c) this is a list of parameters.
                   ;  This list is not evaluated.
  ...)

(lambda foo    ; foo is not allowed syntax. Common Lisp expects a list.
  ...)

LAMBDA использует обычные лямбда-листы .

0 голосов
/ 14 сентября 2018

Чтобы превратить make-canned-format в функцию, необходимо заменить function на compile или (coerce (lambda ...) 'function).

Однако ваш рефакторинг ошибочен.make-canned-format должен быть макросом - таким образом он создаст замыкание в current compilation environment .Однако эта функция приведет к закрытию в global environment .

...