2016-12-05 2 views
3

Я наткнулся на статью this, объясняющую комбинатор Y. Код находится в Scheme, но я пытаюсь работать с ним с помощью Common Lisp.Определение и использование функций в переменных в Common Lisp

Однако у меня возникли проблемы с переводом со Схемы на общий Лисп. Схема использует одно пространство имен для обеих функций и (другие) переменные, но Common Lisp использует разные пространства имен для функций и переменных. Как я могу решить эту разницу, чтобы получить рабочий код Common Lisp?

Схема Код

Вот некоторые схемы код из учебника.

В начале автор определяет функцию факториала:

(define (factorial n) 
    if (= n 0) 
    1 
    (* n (factorial (- n 1))))) 

и переводит его в это:

(define factorial 
    (lambda (n) 
    (if (= n 0) 
     1 
     (* n (factorial (- n 1)))))) 

Потому что (по мнению автора), что то, что делает схему:

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

Common Lisp

Я пытался переписать обе вышеуказанные фрагменты в Common Lisp имитировать этот переход от первой формы ко второй. Но в CL нет define, и у него нет единого пространства имен. Поэтому я попытался обмануть его.

Переписывая первое определение Scheme в Common Lisp было легко:

(defun factorial (n) 
    (if (= n 0) 
     1 
     (* n (factorial (- n 1))))) 

Но (для меня) перевод этого во второе определение было немного сложнее. Я перевел это так:

(setf (symbol-function 'factorial) 
    (lambda (n) 
    (if (= n 0) 
     1 
     (* n (factorial (- n 1)))))) 

Это плохой способ сделать это (или есть лучший способ)? Кажется, что это работает, но компилятор дает мне предупреждение о стиле: неопределенная функция: factorial.

ответ

3

В некотором смысле это больше похоже на это:

(setf (symbol-function 'factorial) 
     (labels ((factorial (n) 
       (if (= n 0) 
        1 
        (* n (factorial (- n 1)))))) 
     #'factorial)) 

LABELS определяет локальную функцию factorial. Внутри определения локальной функции factorial любые вызовы factorial относятся к этой функции. Затем мы возвращаем эту функцию из выражения меток.Таким образом, вы можете определить рекурсивные функции, где рекурсивный вызов не относится к неопределенной функции.

Если вы посмотрите на реализацию Common Lisp, вы увидите, что DEFUN часто расширяет его на непереносимые конструкции, например, именованные функции лямбда. Кроме того, DEFUN также имеет побочные эффекты во время компиляции.

2

Преобразование не имеет смысла в общем списке.

CL defun обычно делает намного больше, чем просто (setf fdefinition).

Вы можете видеть это, оценивая (macroexpand-1 '(defun foo (a b c) (bar c a b))).

Реальный вопрос: почему вы пытаетесь это сделать?

+0

Я просто научусь лучше вводить код сам, а не просто его читать. Возможно, в этом случае это не лучшая идея из-за различий между Scheme и Common Lisp. – Frank

+0

Мне не нравятся реологические вопросы типа «почему вы пытаетесь это сделать», особенно не потому, что Фрэнк начинает свою мотивацию, лучше понимая Y-комбинатор. Бьюсь об заклад, вы не столкнулись с проблемой, чтобы найти статью, на которую он ссылается. С уважением, Альберт –

1

Если я правильно понимаю, основная проблема вашего вопроса связана с переводом между "Lisp-1" and a "Lisp-2".

Схема - это «Lisp-1» - она ​​имеет одно пространство имен для обеих функций и переменных. Common Lisp, с другой стороны, является «Lisp-2» - он имеет отдельные пространства имен для функций и переменных.

В схеме, вы можете написать

(define foo (lambda (...) ...)) 

, а затем вызвать foo как:

(foo ...) 

Мы можем определить foo точно так же, как в Common Lisp, как хорошо, но если мы попытаемся позвонитеfoo, используя этот синтаксис, ваша программа выйдет из строя. Это связано с тем, что foo находится в переменной переменной пространства имен, а не в функции .

Мы можем обойти это с помощью funcall для вызова foo:

(funcall foo ...) 

Это краткое введение. На странице Common Lisp Cookbook на странице Functions представлена ​​дополнительная информация, которая может оказаться полезной.

+0

Да, проблема с разным названием пространства имен является проблемой. Я должен решить, хранить ли функцию в ячейке значения (и называть ее с помощью функции funcall) или использовать решение Райнера (что, я думаю, я попробую). Переход, показанный в OP, является только первым из длинной серии переходов, которые в конечном итоге будут давать '(определяют почти факториал (lambda (f) (lambda (n) (if (= n 0) ((лямбда (x) (xx)) (лямбда (x) (f (xx))))) (обозначение Y (лямбда (f) ) (определить факториал (Y почти-факториал)). – Frank

+0

@Frank, справа. Y-комбинатор включает передачу функции в качестве параметра и применение этой функции. Поэтому я думаю, вам понадобится «funcall», независимо от того, как вы туда попадете. –

Смежные вопросы