Как сделать "определить", который принимает строку в первом параметре в схеме SISC?
Давайте назовем эту функцию "динамически определяемой".
В основном я хочу написать макрос или лямбду, которая работает так:
$ (dynamic-define "variable" 123)
$ variable
$ => 123
Я попробовал это:
(define-syntax dynamic-define
(syntax-rules ()
((_ string <body>)
(eval `(define ,(string->symbol string) <body>)))))
и это работает, как ожидалось, но не кажется хорошим решением.
Я пытался использовать без Eval, как это:
(define-syntax dynamic-define
(syntax-rules ()
((_ string <body>)
(define (string->symbol string) <body>))))
Но когда я пытаюсь использовать, я получаю эту ошибку:
Error: invalid syntax (define (string->symbol "variable") 123)
Что я должен делать?
(ПОЖАЛУЙСТА: отредактируйте этот вопрос, чтобы он соответствовал родному английскому, и удалите эту строку, пожалуйста)
2 ответа
Вы сталкиваетесь с фундаментальной проблемой: вы не можете сделать реальное динамическое определение "чистым" способом, поэтому вы пытаетесь использовать eval
, Обратите внимание, что оба вышеупомянутых решения не являются динамическими - все, что они дают вам, это возможность писать "foo"
вместо foo
, Как я уже говорил в другом месте, используя eval
обычно плохо, и в этом случае это хуже, так как это eval
это используется из макроса, что делает его очень странным. Это в дополнение к неявной квази-кавычке в вашем макросе, которая делает вещи еще более запутанными. С таким же успехом вы можете определить все это как простую функцию:
(define (dynamic-define string <body>)
(eval `(define ,(string->symbol string) ,<body>)))
Если вам действительно нужно, чтобы это работало, то лучше сделать шаг назад и подумать, что именно вам нужно. С одной стороны, вышеприведенные решения не являются динамическими, поскольку они требуют использования макроса со строковым синтаксисом
(dynamic-define "foo" 123) ; instead of (define foo 123)
но как будет выглядеть реальное динамическое определение, которое принимает строку? Вы, вероятно, ожидаете, что это определит b
:
(define a "b")
(dynamic-define a 123)
но это становится еще более запутанным, когда вы рассматриваете, как оно взаимодействует с другими привязками. Например:
(define y 123)
(define (foo s)
(define x 456)
(dynamic-define s 789)
(+ x y))
Теперь, предполагая, что dynamic-define
делает то, что вы хотите, подумайте о том, что вы получите с (foo "x")
а также (foo "y")
, Хуже считать (foo "s")
а также (foo "+")
, И что еще хуже
(foo (random-element '("x" "y" "s" "+" "define")))
? Понятно, если это dynamic-define
действительно дает некоторое динамическое определение, тогда нет смысла, что вы можете сделать из этого кода, не зная заранее, какое имя foo
будет вызван с. Не имея смысла в этом, компиляция уходит в окно, но гораздо важнее то, что правильность (или вообще смысл вашего кода) умирает.
По моему опыту, этот тип шаблона намного лучше обрабатывается с помощью хеш-таблиц и подобных устройств.
с помощьюdefine-macro
#> (define-macro (dynamic-define varstr val)
`(define ,(string->symbol varstr) ,val))
#> (dynamic-define "variable" 123)
#> variable
123
с помощьюsyntax-case
#> (define-syntax dynamic-define
(lambda (stx)
(syntax-case stx ()
((k varstr val)
(with-syntax ([var (datum->syntax-object
#'k
(string->symbol (syntax-object->datum #'varstr)))])
#'(define var val))))))
#> (dynamic-define "variable" 123)
#> variable
123