Ошибка
(infix '(* 5 6))
; =
(list (infix (cadr '(* 5 6))) (car '(* 5 6)) (infix (caddr '(* 5 6))))
; =
(list (infix 5) '* (infix (caddr 6)))
; = ^^^^^^^^^
; |
; |
; v
(if ...
...
(if (null? (cdr 5)) ; <-- fails here
...
...))
Решение
Сначала необходимо определить структуру данных, которыми вы манипулируете:
; OpExp is one of:
; - Number
; - (cons Op [List-of OpExp])
; Op = '+ | '* | ...
В английском sh: это либо число, либо оператор, за которым следует список других выражений.
Определим несколько примеров:
(define ex1 7)
(define ex2 '(* 1 2))
(define ex3 `(+ ,ex2 ,ex1))
(define ex4 '(* 1 2 3 (+ 4 3 2) (+ 9 8 7)))
Теперь мы следуем структуре OpExp, чтобы создать «шаблон»:
(define (infix opexp)
(if (number? opexp)
...
(... (car opexp) ... (cdr opexp) ...)))
Два случая:
- Первый случай: что делать, когда мы просто получаем число?
- Второй случай: сначала извлечь компонент enet:
(car opexp)
является оператором (cdr opexp)
- список операндов типа OpExp
Уточнение шаблона:
(define (infix opexp)
(if (number? opexp)
opexp
(... (car opexp) ... (map infix (cdr opexp)) ...)))
Поскольку у нас есть список Операторы, нам нужно отобразить рекурсивный вызов для всех из них. Все, что нам нужно сделать, это сделать оператор инфиксом на верхнем уровне.
Мы используем помощника, который переплетает список с оператором:
; inserts `o` between every element in `l`
(define (insert-infix o l)
(cond ((or (null? l) (null? (cdr l))) l) ; no insertion for <= 1 elem lst
(else (cons (car l) (cons o (insert-infix o (cdr l)))))))
и, наконец, используем помощника для получения окончательной версии:
; converts OpExp into infix style
(define (infix opexp)
(if (number? opexp)
opexp
(insert-infix (car opexp) (map infix (cdr opexp)))))
Мы определяем соответствующие результаты для наших примеров:
(define res1 7)
(define res2 '(1 * 2))
(define res3 `(,res2 + ,res1))
(define res4 '(1 * 2 * 3 * (4 + 3 + 2) * (9 + 8 + 7)))
И вызов infix
на ex1 ... exN должен привести к res1 ... resN