Молчаливое программирование на Лиспе

Возможно ли использовать / внедритьмолчаливое программирование (также известный как бессмысленное программирование) в Лиспе? И если ответ - да, было ли это сделано?

Ответы на вопрос(4)

ДА, это возможно, и @danlei уже объяснил очень хорошо. Я собираюсь добавить некоторые примеры из книги ANSI Common Lisp Пола Грэма, глава 6.6, о сборщиках функций:

Вы можете определить построитель функций следующим образом:

(defun compose (&rest fns)
  (destructuring-bind (fn1 . rest) (reverse fns)
    #'(lambda (&rest args)
        (reduce #'(lambda (v f) (funcall f v))
                rest
                :initial-value (apply fn1 args)))))

(defun curry (fn &rest args)
  #'(lambda (&rest args2)
      (apply fn (append args args2))))

и использовать это так

(mapcar (compose #'list #'round #'sqrt)
        '(4 9 16 25))

возвращается

((2) (3) (4) (5))

compose вызов функции:

(compose #'a #'b #'c)

эквивалентно

#'(lambda (&rest args) (a (b (apply #'c args))))

Это означает, что compose может принимать любое количество аргументов, да.

Создайте функцию, которая добавляет 3 к аргументу:

(curry #'+ 3)

Смотрите больше в книге.

 18 июн. 2012 г., 00:33
но это не имеет особого смысла. Это приводит к плохому коду. Сложнее читать и отлаживать. Плюс - почти каждый компилятор CL будет генерировать медленный код для этого (списки аргументов и т. Д.).
 18 июн. 2012 г., 11:29
Вашcompose Пример работает и с моей версией. Я только что получил небольшую копию & amp; вставить ошибку или опечатку вcomp (неправильный порядок вызовов функций вstep, рабочая версия в REPL), которая сейчас исправлена. Кроме того, я думаю, что Райнер прав: несмотря на то, что это приятно делать в CL, это не очень идиоматично. Haskell гораздо больше подходит для этого стиля программирования. (Кстати, стиль Грэма, по мнению многих программистов CL, довольно своеобразен).
 18 июн. 2012 г., 02:01
@RainerJoswig Вы правы. Это может показать, насколько гибкий CL и какое закрытие может помочь нам ... o_O

Да, это возможно в общем случае с правильными функциями. Например, вот пример реализации Racketsum со страницы Википедии:

#lang racket
(define sum (curry foldr + 0))

Поскольку процедуры не карри по умолчанию, это помогает использоватьcurry или напишите свои функции в явном стиле карри. Вы можете абстрагироваться от этого с новымdefine макрос, который использует карри.

 paldepind20 июн. 2012 г., 10:34
Ну, на самом деле вопрос был о Лиспе в целом. Я добавил CL в качестве тега, так как это диалект Lisp, с которым я наиболее знаком, но этот ответ с использованием Scheme также полезен.
 17 июн. 2012 г., 22:43
Вопрос о Common Lisp, этот ответ правильный для Scheme, но не для CL

Вы можете использовать что-то вроде (это немного больше, чем-> в Clojure):

(defmacro -> (obj &rest forms)
  "Similar to the -> macro from clojure, but with a tweak: if there is
  a $ symbol somewhere in the form, the object is not added as the
  first argument to the form, but instead replaces the $ symbol."
  (if forms
      (if (consp (car forms))
          (let* ((first-form (first forms))
                 (other-forms (rest forms))
                 (pos (position '$ first-form)))
            (if pos
                `(-> ,(append (subseq first-form 0 pos)
                              (list obj)
                              (subseq first-form (1+ pos)))
                     ,@other-forms)
                `(-> ,(list* (first first-form) obj (rest first-form))
                     ,@other-forms)))
          `(-> ,(list (car forms) obj)
               ,@(cdr forms)))
      obj))

(вы должны быть осторожны, чтобы экспортировать символ$ из пакета в который вы размещаете-> - давайте назовем этот пакетtacit - и положи tacit вuse пункт любого пакета, где вы планируете использовать->, так-> а также$ наследуются)

Примеры использования:

(-> "TEST"
    string-downcase
    reverse)

(-> "TEST"
    reverse
    (elt $ 1))

Это больше похоже на F #|> (и оболочка трубы), чем у Haskell., но они в значительной степени то же самое (я предпочитаю|>, но это вопрос личного вкуса).

Чтобы увидеть, что-> делает, просто макроэкспонировать последний пример три раза (в SLIME это достигается путем установки курсора на первый( в примере и набравC-c RET три раза).

Решение Вопроса

Этот стиль программирования возможен в CL в принципе, но, будучи Lisp-2, нужно добавить несколько#'с иfuncalls. Кроме того, в отличие от Haskell, например, функции не каррируются в CL, и не существует неявного частичного применения. В общем, я думаю, что такой стиль не был бы очень идиоматическим CL.

Например, вы можете определить частичное приложение и композицию следующим образом:

(defun partial (function &rest args)
  (lambda (&rest args2) (apply function (append args args2))))

(defun comp (&rest functions)
  (flet ((step (f g) (lambda (x) (funcall f (funcall g x)))))
    (reduce #'step functions :initial-value #'identity)))

(Это просто быстрые примеры, которые я собрал, и # x2013; они на самом деле не проверены и не продуманы для различных вариантов использования.)

С теми, что-то вродеmap ((*2) . (+1)) xs в Хаскеле становится:

CL-USER> (mapcar (comp (partial #'* 2) #'1+) '(1 2 3))
(4 6 8)

sum пример:

CL-USER> (defparameter *sum* (partial #'reduce #'+))
*SUM*
CL-USER> (funcall *sum* '(1 2 3))
6

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

Кстати, в Emacs Lisp частичное приложение встроено какapply-partially.

В Qi / Shen функции каррируются, и поддерживается неявное частичное применение (когда функции вызываются с одним аргументом):

(41-) (define comp F G -> (/. X (F (G X))))
comp

(42-) ((comp (* 2) (+ 1)) 1)
4

(43-) (map (comp (* 2) (+ 1)) [1 2 3])
[4 6 8]

В Clojure также есть синтаксическая нить для потока, которая дает аналогичное ощущение «конвейерной обработки»:

user=> (-> 0 inc (* 2))
2

Ваш ответ на вопрос