Каков стандартный способ определения глобальных замыканий в схеме?
Поэтому я хочу знать, есть ли стандартный способ иметь такой код:
(let ((x 10))
(define (add10 a)
(+ x a)))
Я знаю о:
(define add10 (let ((x 10))
(lambda (a) (+ x a))))
но это не будет работать, если я хочу определить несколько функций, мне нужно знать стандартный способ, чтобы я мог написать макрос, который будет определять функции. где вы можете вызвать макрос внутри let:
(let ((x 10))
(macro x))
и, например, макрос создаст список функций:
(let ((x 10))
(define (add1)
(+ x 1))
(define (add2)
(+ x 2))
(define (add3)
(+ x 3)))
Существует ли стандартный способ определения функций add1..add3? В схеме, которую я тестировал, функции будут локальными внутри let
и не доступны снаружи.
Если вы показываете код макроса, меня интересуют только макросы LISP с define-macro
а также quasiquote
, пожалуйста, нет define-syntax
потому что это в основном для использования в моем собственном lisp (на основе схемы), где у меня есть только макросы lisp.
Если схема не дает поддержки для чего-то подобного, разрешает ли такой диалект, как Common Lisp, что-то подобное?
3 ответа
(let ((x 10))
(somemacro x))
->
(let ((x 10))
(define (add1)
(+ x 1))
(define (add2)
(+ x 2))
(define (add3)
(+ x 3)))
В Common Lisp:
CL-USER 43 > (defmacro somemacro (var)
`(progn
(defun add1 () (+ ,var 1))
(defun add2 () (+ ,var 2))
(defun add3 () (+ ,var 3))))
SOMEMACRO
CL-USER 44 > (let ((x 10))
(somemacro x))
ADD3
CL-USER 45 > (add1)
11
CL-USER 46 > (add3)
13
Видно, что когда-нибудь. Обычно это немного плохой стиль в Common Lisp, потому что файловый компилятор не будет распознавать, что существуют глобальные объявления функций, потому что внутри LET
DEFUN
не на высшем уровне. Если функция определена в файле на верхнем уровне, то во время компиляции файловый компилятор увидит, что это функция, и может делать особые вещи, например, отмечать подпись в среде времени компиляции, вставляя ее в строку. и т.п.
Обратите внимание, что когда DEFINE
в Scheme определяет локальную функцию, которую все еще можно выполнять (в зависимости от того, что реализация делает дополнительно к стандарту):
(let ((x 10))
()
(define (add1) (+ x 1)))
Обратите внимание, что в Common Lisp defun
определяет глобальные функции и flet
/ labels
определить локальные функции.
В R7RS, последнем отчете схемы, мы имеем define-values
, Это может быть использовано следующим образом:
(define-values (add1 add2 add3)
(let ((x 10))
(values (lambda () (+ x 1))
(lambda () (+ x 2))
(lambda () (+ x 3)))))
Конечно, для больших блоков можно создать локальное определение и ссылаться на него в values
вместо.
В отчете R7RS вы найдете синтаксическое правило для define-values
это будет работать для R6RS и R5RS. Оно использует call-with-values
где значения передаются в list
а потом define
От этого. Могу поспорить, это также работает внутри lambdas
Успех, что реализация Схемы на самом деле может превратить это в letrec
так что, хотя он не очень элегантен, он делает грязную работу.
Я думаю, что нет решения, которое оборачивает связывание вокруг define
может работать вообще переносно или безопасно, как завернутый define
Она будет либо создавать локальные привязки (ведущие формы в теле), либо быть незаконной (не ведущие формы в теле), хотя я был бы рад, если бы человек, отвечающий стандартам Схемы, указал, где я ошибаюсь.
Вместо этого что-то вроде этого немного неприятного хака должно сработать, я думаю.
(begin
(define inc undefined)
(define dec undefined)
(let ((x 3))
(set! inc (lambda (y)
(set! x (+ x y))
x))
(set! dec (lambda (y)
(set! x (- x y))
x))))
Здесь я положился на константу под названием undefined
что означает "еще не определено правильно": Racket предоставляет это в racket/undefined
но в целом это может быть что угодно: вы могли бы просто где-то сказать
(define undefined 'undefined)
например.
Хитрость заключается в том, чтобы определить вещи, которые вы хотите на верхнем уровне, с помощью значений заполнителей, а затем назначить их внутри let
,
Я уверен, что можно определить макрос, который расширяется до чего-то вроде (вот почему у меня все это внутри begin
): Я не сделал этого, потому что это неудобно, и я использую Racket, поэтому я не могу легко написать в нем макросы Lisp старого стиля.
Обратите внимание, что очевидный подход в современной схеме заключается в использовании define-values
:
(define-values (x y) (let (...) (values ...)))
Делай что хочешь. Как уже упоминалось в другом ответе, вы можете реализовать define-values
как макрос, если у вас есть только несколько значений. Но если у вас совсем нет нескольких значений, вы можете использовать что-то, что определяет вещи на основе списка результатов:
(define-list (x y) (let (...) (list ...)))
Вот два грубых варианта этого макроса: первый использует родные макросы Racket:
(require racket/undefined)
(define-syntax define-list
(syntax-rules ()
[(define-list () expr)
(let ((results expr))
(unless (zero? (length results))
(error "need an empty list"))
(void))]
[(define-list (name ...) expr)
(begin
(define name undefined)
...
(let ([results expr])
(unless (= (length results)
(length '(name ...)))
(error "wrong number of values"))
(set! name (begin0
(car results)
(set! results (cdr results))))
...))]))
в то время как второй использует негигенные макросы в Racket:
(require compatibility/defmacro
racket/undefined)
(define-macro (define-list names expr)
`(begin
,@(let loop ([ntail names] [defs '()])
(if (null? ntail)
(reverse defs)
(loop (cdr ntail)
(cons `(define ,(car ntail) undefined) defs))))
(let ([results ,expr])
(unless (= (length results)
(length ',names))
(error "wrong number of values"))
,@(let loop ([ntail names] [i 0] [assignments '()])
(if (null? ntail)
(reverse assignments)
(loop (cdr ntail) (+ i 1)
(cons `(set! ,(car ntail) (list-ref results ,i))
assignments)))))))
Обратите внимание, что ни один из них не был проверен, и мне нужно потратить немного времени, чтобы убедить себя в том, что второе достаточно гигиенично.
Но с этим:
> (define-list (inc dec)
(let ([i 0])
(list
(lambda ()
(set! i (+ i 1))
i)
(lambda ()
(set! i (- i 1))
i))))
> inc
#<procedure>
> (inc)
1
> (dec)
0
> (dec)
-1
>