Вот мое решение:
(define (flatten-iter a-list)
(define (flat-do acc lst-interm lst)
(cond
((null? lst)
(reverse acc))
((and (list? lst-interm) (not (null? lst-interm)))
(flat-do acc (car lst-interm) (append (cdr lst-interm) lst)))
((not (list? lst-interm))
(flat-do (cons lst-interm acc) empty lst))
((list? (car lst))
(flat-do acc (car lst) (cdr lst)))
(else
(flat-do (cons (car lst) acc) empty (cdr lst)))))
(flat-do empty empty a-list))
(flatten-iter (list 1 (list 2 (list 3 4 (list 5 empty 6))) 7 8))
=> (1 2 3 4 5 6 7 8)
Хвост-рекурсивные функции требуют, чтобы они никогда не возвращались, и поэтому вы не можете использовать стек для хранения состояния вашей программы.Вместо этого вы используете аргументы функции для передачи состояния между вызовами функций.Поэтому нам нужно определить, как сохранить состояние.Поскольку результатом нашей функции является list?
, имеет смысл увеличить список empty
;мы используем acc
для этой цели.Вы можете увидеть, как это работает в ветке else
выше.Но мы должны иметь возможность обрабатывать вложенные списки.И пока мы углубляемся, мы должны сохранить остальные элементы вложенного списка для дальнейшей обработки.Пример списка: (list 1 (list 2 3) 4 5)
До (list 2 3)
мы уже добавили 1
в аккумулятор.Поскольку мы не можем использовать стек, нам нужно другое место для хранения остальных элементов списка.И это место является аргументом lst
, который содержит элементы исходного списка, которые должны быть сведены.Мы можем просто append
lst
к остальным элементам (cdr (list 2 3))
, которые (list 3)
, и продолжить с головой списка, на которую мы наткнулись при выравнивании, то есть (car (list 2 3)), которая является просто 2
,Теперь (and (list? lst-interm) (not (null? lst-interm)))
выполняется успешно, потому что flat-do
вызывается следующим образом:
(flat-do (list 1) (list 2 3) (list 4 5))
и условие вызывает этот код:
(flat-do (list 1) (car (list 2 3)) (append (cdr (list 2 3)) (list 4 5)))
flat-do
снова вызывается следующим образом: (flat-do (список 1) 2 (список 3 4 5))
Условие (not (list? 2))
теперь успешно выполняется и код (flat-do (cons 2 1) empty (list 3 4 5))
оценивается.
Остальная обработка выполняется с помощью else
ветвь, пока lst
не станет null?
и reverse
не будет выполнено acc
.Затем функция возвращает реверсированный аккумулятор.