2016-10-13 3 views
1

В SBCL, как можно положить лямбда-выражение в слот структуры [например, (setf (struct-slot1 struct1) '(lambda (x) (* x x)))], чтобы его можно было вызвать с помощью funcall или apply? Компилятор SBCL жалуется на: wanted one of (FUNCTION SYMBOL). Есть ли способ установить такой лямбда, не компилируя его или не давая ему явное имя? Почему выражение лямбда не является функцией? Я хотел бы сделать что-то вроде: (funcall (function (struct-slot1 struct1)) 3). Спасибо за любые идеи. (ps: обычно я компилирую лямбда перед запуском, но во время отладки мне нужно видеть внутренности лямбда.)Выражения SBCL и Lambda

+1

Ваш прецедент не поясняет, читаете ли вы откуда-то или что-то. Обычно вы просто делаете '(setf (struct-slot1 struct1) (lambda (x) (* x x))). Тогда вы можете '(funcall (struct-slot1 struct1) 2) => 4' –

+0

Извините за путаницу (но я был в замешательстве!) Я пытался сделать что-то вроде предложения с помощью setf, но мне нужно было построить выражение лямбда , поэтому он будет выглядеть примерно так: (setf (struct-slot1 struct1) '(lambda (x) (* x, (+ 2 2)))), который устанавливает список, а не функцию. Но, как замечает Райнер, вы можете превратить список в функцию с coerce [а затем вы можете получить доступ к выражению лямбда с помощью (function-lambda-list (struct-slot1 struct1)]. (Только для записи ввода записей, я обрабатывая файл спецификации пользователя с помощью некоторых макросов, которые строят эти лямбда-выражения.) – davypough

+0

Я не уверен, что понимаю этот прецедент. Почему бы просто не просто (setf ... (let ((y (+ 2 2))) (lambda (x) (* xy)))) '? –

ответ

1

Это не спецификация SBCL, но в соответствии со стандартом ANSI Common Lisp.

Преобразование лямбда-выражения в виде списка в функции

Это ваши варианты:

CL-USER 168 > (funcall (coerce '(lambda (x) (* x x)) 
           'function) 
         4) 
16 

CL-USER 169 > (funcall (compile nil '(lambda (x) (* x x))) 
         4) 
16 

CL-USER 170 > (funcall (eval '(lambda (x) (* x x))) ; because LAMBDA is a macro, too 
         4) 
16 

CL-USER 171 > (funcall (eval '(function (lambda (x) (* x x)))) 
        4) 
16 

Заметим, однако, что лямбда-выражение ссылается на нулевую лексическую среду. Таким образом, он не имеет доступа к каким-либо лексическим переменным из окружающего кода.

лямбда-выражения не функционируют объекты

Почему лямбда-выражение не является функцией?

Потому что это всего лишь список, а не код. Чтобы сделать лямбда-выражение в коде, вы должны превратить его в объект функции.

Некоторые другие (часто более старые) Lisps позволяют использовать лямбда-выражения в качестве кода, но не Common Lisp. Это определяется стандартом.

Вы не можете сделать (funcall '(lambda (x) (+ x x)) 3). Сначала вы должны преобразовать лямбда-выражение в объект функции.

ФУНКЦИЯ является специальным оператором -> синтаксис + семантика

(FUNCALL (функция (структура-slot1 Struct1)) 3)

Это ошибка синтаксиса, уже. FUNCTION является специальным оператором и ожидает, что имя функции или лямбда-выражение. (struct-slot1 struct1) нет. (struct-slot1 struct1) - это код, который извлекает значение из структуры. Но это не имя функции , которое было бы символом или списком (setf <some-symbol>). Это также не выражение лямбда, которое было бы чем-то вроде (lambda (...) ...).

+0

Спасибо, Райнер.Я забыл о принуждении - это его решает. Также спасибо за ваше ясное объяснение, которое по моим лампам лучше, чем в Практических общих Lisp> Функции> Анонимные функции. – davypough

+0

Нужно ли «принуждение» в первом примере? Я вижу, что 'type-of (лямбда (x) (* x x))' уже 'функция'. – agam

+1

@agam: пример состоял в том, как получить из кода в виде списка данных допустимую функцию -> from '(quote (lambda() ...))' функции. '(lambda() ...)' уже является функцией, потому что она некорректна. –

1

(ps: обычно я скомбинирую лямбду перед запуском, но во время отладки мне нужно видеть внутренности лямбда.)

Попробуйте это:

(proclaim '(optimize (debug 3))) 

В SBCL, вы также можете сделать это:

(sb-ext:restrict-compiler-policy 'debug 3) 

... который устанавливает самую низкую допустимую политику для отладки. Документация говорит:

См. Также: ПОЛИТИКА в СО-КОМПИЛЯЦИИ.

Теперь определим простой хэш-таблицу для испытаний:

CL-USER> (defparameter *hash* (make-hash-table :test #'eq)) 

Вставьте анонимную функцию:

CL-USER> (setf (gethash :fun *hash*) (lambda (u) (* 10 u))) 
#<FUNCTION (LAMBDA (U)) {1005140E2B}> 

Называйте это плохо:

CL-USER> (funcall (gethash :fun *hash*) "oops") 

отладчик показывает вверх :

Argument X is not a NUMBER: "oops" 
    [Condition of type SIMPLE-TYPE-ERROR] 

Restarts: 
0: [RETRY] Retry SLIME REPL evaluation request. 
1: [*ABORT] Return to SLIME's top level. 
2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {10041C8033}>) 

Backtrace: 
    0: (SB-KERNEL:TWO-ARG-* "oops" 10) 
    1: ((LAMBDA (U)) "oops") 
    2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (FUNCALL (GETHASH :FUN *HASH*) "oops") #<NULL-LEXENV>) 
    3: (EVAL (FUNCALL (GETHASH :FUN *HASH*) "oops")) 

Перейти к кадру # 1, ((LAMBDA (U)) "oops") и нажмите v (А.К.А. sldb-show-source). Новый буфер выскакивает со следующим содержанием:

(LAMBDA() 
    (PROGN 
    (LET* ((#:HASHTABLE *HASH*) (#:NEW1 (LAMBDA (U) (#:***HERE*** (* 10 U))))) 
    (SB-KERNEL:%PUTHASH :FUN #:HASHTABLE #:NEW1)))) 

Благодаря политике отладчика является достаточно высоким, исходный код анонимной функции доступен, даже если оно было напечатано в REPL. Если ваша анонимная функция будет скомпилирована из исходного файла, то нажатие v перейдет к соответствующей точке в этом файле (что соответствует форме, завернутой в вышеописанный термин ***HERE***).


Если вы хотите, вы можете также хранить код как данные, прежде чем скомпилировать:

(defun wrap-and-compile (code) 
    (let ((function (compile nil code))) 
    (compile nil `(lambda (&rest args) 
        (restart-case (apply ,function args) 
         (DEBUG() :report ,(format nil "~S" code) 
         ',code)))))) 

(setf (gethash :wrapped *hash*) 
     (wrap-and-compile '(lambda (x) (* 10 x)))) 

(funcall (gethash :wrapped *hash*) "Boo") 

И отладчик показывает:

Argument X is not a NUMBER: "Boo" 
    [Condition of type SIMPLE-TYPE-ERROR] 

Restarts: 
0: [DEBUG] (LAMBDA (X) (* 10 X)) 
1: [RETRY] Retry SLIME interactive evaluation request. 
2: [*ABORT] Return to SLIME's top level. 
3: [ABORT] abort thread (#<THREAD "worker" RUNNING {100513A403}>) 

Это немного Hacky, но он работает. Перезапуск DEBUG возвращает исходные данные, из которых была скомпилирована функция.

+0

Интересно. Тем не менее, мои анонимные функции сконструированы [и, возможно, скомпилированы с (скомпилировать nil )] во время загрузки, поэтому для них нет источника. Ваши комментарии заверяют меня, что там, где есть воля, есть способ! – davypough

+0

@ davypough Просто, чтобы убедиться, что я попытался с помощью функции, подобной этой: '(compile nil (list 'lambda (list' x) (list '*' x 10)))' и это все еще работает. Но в любом случае вы можете создать свой собственный объект, который хранит функции-данные перед компиляцией. См. Править. – coredump

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