2016-08-01 3 views
-1

Я использую следующий код модифицированный из Solving Infix Arithmatic in LISP с определением атома из Check if an argument is a list or an atom иметь инфиксную арифметический решатель:Eval не работает в Ракетке

(define atom? (or/c number? symbol? boolean? string?)) 

(define (solve expression) 
    (define templist '()) 
    (println expression) 
    (if (atom? expression) 
     expression 
     (begin 
     (set! templist (list (second expression) 
          (solve (first expression)) 
          (solve (third expression)) )) 
     (println templist) 
     (eval templist)))) 

(solve '(3 + 2)) 

Выход выглядит следующим образом:

'(3 + 2) 
3 
2 
'(+ 3 2) 
+: unbound identifier; 
also, no #%app syntax transformer is bound in: + 

Следовательно , templist создается справа (+ 3 2), но на уровне eval есть ошибка. Почему «+» рассматривается как «несвязанный идентификатор»? Функция Eval иначе работает хорошо в командной строке в DrRacket:

> (eval '(+ 3 2)) 
5 

Где проблема и как она может быть решена? Благодарю.

ответ

3

Вы не изучали ответы на свои предыдущие вопросы? Не используйте set!, это не очень хороший стиль схемы/ракеты. Это особенно вопиющее здесь, потому что оно абсолютно ничего не делает; вытащите его и используйте let (или local, или block с внутренними определениями, если хотите).

В стороне, eval одинаково злой, и вам здесь не нужно. Функции являются первоклассными; создать сопоставление между символами и функциями, а затем просто использовать сопоставление для извлечения соответствующей функции для вызова.

(define (operator->procedure op) 
    (case op 
    [(+) +] 
    [(-) -] 
    [(*) *] 
    [(/) /] 
    [else (error 'operator->procedure "unknown operator ~v" op)])) 

(define (solve expression) 
    (if (atom? expression) 
     expression 
     ((operator->procedure (second expression)) 
     (solve (first expression)) 
     (solve (third expression))))) 

Чтобы быть еще более ясным, вы можете использовать форму ракетки в шаблоне сопоставления, match:

(define (solve expression) 
    (match expression 
    [(? atom) expression] 
    [(list a op b) 
    ((operator->procedure op) (solve a) (solve b))])) 
+0

Я использовал набор! и templist только для отладки кода, в противном случае требуется только «eval (list (второе выражение) ...». Почему он установлен на языке, если он вообще не используется? Спасибо за хорошо объясненный ответ. – rnso

+1

@ rnso Возможно, но ваш 'set!' вообще ничего не делает. Даже если ваш исходный код вы могли бы использовать только локальную привязку, я бы сказал, что пока вы не будете очень комфортно с Scheme/Racket, * никогда не используйте 'set!', даже не для отладки. Представьте, что этого не существует. –

+0

@rnso 'set!' полезно для реализации некоторых низкоуровневых конструкций в Scheme, но это не то, что полезно в большинстве пользовательских кодов. Обычно вы можете избежать мутации вообще, и если вы действительно хотите что-то изменить, вы можете использовать поля вместо этого (но я бы тоже избежал их, если вы на 100% не уверены, что они вам нужны). –