может ли быть правильной функцией, а не специальной формой - PullRequest
14 голосов
/ 11 февраля 2010

Я наконец начал изучать функциональные языки (emacs lisp), и это делает явное различие между функциями и специальными формами, такими как управление потоком, например, если.

Существует ли фундаментальная / теоретическая причина, по которой специальные формы отличаются от функций? какие-нибудь языки предоставляют функционал if?

Спасибо

Ответы [ 7 ]

12 голосов
/ 11 февраля 2010

При нетерпеливой оценке требуется различие, языки с ленивой оценкой (т.е. Haskell), если et al. могут быть функциями.

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

Ленивая оценка: аргументы функции вычисляются тогда и только тогда, когда к ним обращаются.

8 голосов
/ 11 февраля 2010

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

Вместо этого, с помощью if, вы хотите оценить только одну из , затем форму и , иначе форму , а не обе. Чтобы подавить оценку того или другого, вам нужен макрос или специальная форма .

3 голосов
/ 11 февраля 2010

В таких языках, как Emacs Lisp и Common Lisp, специальные формы являются встроенными языковыми конструкциями. У них разные правила оценки, которые вызывают обычные функции. Для обычных вызовов функций все аргументы оцениваются. Таким образом, вы не можете написать IF как нормальную функцию - условие определяет, какое предложение будет оценено. Также обычно вы не можете написать свои собственные специальные формы - в Common Lisp нет языковой конструкции для определения специальной формы (хотя отдельные реализации должны как-то реализовывать существующие. Это приводит к макросам. С помощью макросов вы можете написать синтаксическое преобразование который преобразует одно выражение в другое. Чтобы иметь возможность писать IF как макрос, вам нужна другая условная форма, которую вы можете использовать для преобразованного кода. Lisp предоставляет условные выражения в качестве базовых конструкций. Предположим, что COND является такой базовой конструкцией , тогда вы можете расширить IF до использования COND.

MY-IF как макрос в Common Lisp:

(defmacro my-if (condition true-clause false-clause)
   `(cond (,condition ,true-clause)
          (t ,false-clause)))

So

(my-if (foo-p) 'one 'two)

расширяется до

(cond ((foo-p) 'one)
      (t 'two))
1 голос
/ 11 февраля 2010

Для полноты: на языке Pico , например, нет специальных форм, а if является примитивной функцией , тогда как Pico вдохновлен Scheme и по умолчанию готов к оценке .

В схеме вы могли бы написать

(define (true t f)
  (t))

(define (false t f)
  (f))

(define (function_if c t e)
  (c t e))

, а затем

(function_if true (lambda () 'true) (lambda () 'false))
==> true

Что делает это управляемым в Pico, так это то, что вы можете определить функциональные параметры , которые принимают функциональные аргументы, которые "автоматически" задерживаются. Это означает, что вам не нужно делать обертывание внутри лямбды самостоятельно. Таким образом, Пико оценивает с энтузиазмом, но лениво оценивает по требованию, обходя необходимость в специальных формах.

Итак, в синтаксисе Scheme с функциональными параметрами вы можете кодировать логические значения как:

(define (true (t) (f))
  (t))

(define (false (t) (f))
  (f))

Тогда функция if становится:

(define (function_if c (t) (e))
  (c (t) (e)))

и

(function_if true 'true 'false)
==> true

В качестве другого примера, определение функции and равно (define (and p (q)) (p (q) false)).

Аналогичным образом вы можете определить or, not, while, for, ... как функции, используя вышеуказанную кодировку логических значений.

1 голос
/ 11 февраля 2010

Краткий ответ: Нет.

Длинный (er) ответ: (если ...) требует, чтобы вы управляли порядком оценки аргументов. Лисп, будучи нетерпеливым языком, не может сделать это в функции.

Обходной путь: сделать это в макросе:

(defmacro _if (cnd true false)
    (let (  (gcond (gensym))
            (gresp (gensym)))
        `(let ( (,gcond ,cnd) ;`#quotes
                (,gresp nil))        
            (and ,gcond       (setf ,gresp (multiple-value-list ,true)))
            (and (not ,gcond) (setf ,gresp (multiple-value-list ,false)))
            (values-list ,gresp))))

Например:

[dsm@localhost:~]$ clisp -q
[1]> (defmacro _if (cnd true false)
    (let (  (gcond (gensym))
            (gresp (gensym)))
        `(let ( (,gcond ,cnd) ;`#quotes
                (,gresp nil))        
            (and ,gcond       (setf ,gresp (multiple-value-list ,true)))
            (and (not ,gcond) (setf ,gresp (multiple-value-list ,false)))
            (values-list ,gresp))))
_IF
[2]> (_if (= 1 1) (+ 2 3) "bar")
5
[3]> (_if (= 1 2) (+ 2 3) "bar")
"bar"
[4]> 
0 голосов
/ 15 марта 2010

IF может быть функцией на функциональном языке с семантикой вызова по имени (ленивая оценка), как в Lambda Calculus или Algol. На самом деле это, я думаю, лежит в основе отношений между машинами Тьюринга и лямбда-исчислением как эквивалентными основами для вычислений. Тем не менее, в языках, имеющих побочные эффекты (например, присваивания переменных), это не очень полезно, потому что , когда вещи происходят, важно.

0 голосов
/ 11 февраля 2010

В Scala можно моделировать if с правильной оценкой побочных эффектов с использованием аргументов по имени.

def If[A](cond : Boolean, truePart : => A, falsePart : => A) = if (cond) truePart else falsePart

Эта функция также может использоваться для моделирования множества новых управляющих структур .

...