Чтение ваш другой вопрос Я понимаю, почему вы пишете выражения полужирным шрифтом -
…
(cond (<b>(= (cdr (last-element-on-list (list-ref list i)) '+)</b>)
(set! plus-list
(cons (list-ref list i) plus-list)))
(<b>(= (cdr (last-element-on-list (list-ref list i)) '-)</b>)
(set! minus-list
(cons (list-ref list i) minus-list))))
…
Показан ваш входной список -
(define lst
'((n 25 f +)
(s 25 m +)
(ll 20 no -)))
Ваш split
проверяет содержимое каждого элемента l
. split
превысил свои границы, и теперь он only работает для списков, содержащих элементы этой конкретной структуры. Наряду с set!
отсутствие else
в cond
обычно указывает на то, что вы делаете что-то не так. Вы также звоните (cdr (last-element-of-list ...))
. Если last-element-of-list
возвращает атом, cdr
выдаст здесь ошибку.
Рассмотрим проектирование split
в более общем виде -
(define (split proc l)
(define (loop l true false)
(cond ((null? l)
(cons true false))
((proc (car l))
(loop (cdr l)
(cons (car l) true)
false))
(else
(loop (cdr l)
true
(cons (car l) false)))))
(loop l '() '()))
(split (lambda (x) (> x 5))
'(1 5 3 9 7 0 8 3 2 6 4))
;; '((6 8 7 9) 4 2 3 0 3 5 1)
Если наш список содержит разные элементы, мы все равно можем использовать ту же самую процедуру split
-
(split (lambda (x) (eq? '+ (cadr x)))
'((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +)))
;; '(((4 +) (3 +) (2 +) (1 +)) (3 -) (2 -) (1 -))
Я думаю, никогда не рано начинать изучение стиля прохождения продолжения. Ниже return
представляет наше продолжение и по умолчанию cons
, ту же процедуру, которую мы использовали для возврата окончательного результата в нашей первоначальной реализации. Интуитивно понятно, что продолжение представляет «следующий шаг» вычисления -
(define (split proc l (return cons)) ;; `return` is our continuation
(if (null? l)
;; base case: list is empty, return empty result
(return '() '())
;; inductive case: at least one `x`
(let* ((x (car l))
(bool (proc x)))
(split proc ;; tail recur with our proc
(cdr l) ;; ... a smaller list
(lambda (t f) ;; ... and "the next step"
(if bool ;; if `(proc x)` returned true
(return (cons x t) ;; ... cons the `x` onto the `t` result
f) ;; ... and leave the `f` the same
(return t ;; otherwise leave `t` the same
(cons x f)))))))) ;; ... and cons the `x` onto the `f` result
Если мы запустим нашу split
процедуру, вы заметите, что мы получаем точно такой же вывод, как и выше. На первый взгляд кажется, что мы напортачили с хорошей программой, однако есть одно явное преимущество этой реализации. Поскольку продолжение настраивается пользователем, вместо cons
мы могли бы решить совершенно другую судьбу для наших двух списков, t
и f
-
(split (lambda (x) (eq? '+ (cadr x)))
'((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +))
(lambda (plus minus)
(printf "plus: ~a, minus: ~a\n" plus minus)))
;; plus: ((1 +) (2 +) (3 +) (4 +)), minus: ((1 -) (2 -) (3 -))
Обратите внимание, как plus
и minus
дали соответствующие результаты. Нам не нужно было выделять промежуточный результат cons
. Более интуитивно, мы хотим, чтобы printf
был «следующим шагом» , но нам нужно только указать первый аргумент -
(split (lambda (x) (eq? '+ (cadr x)))
'((1 +) (1 -) (2 +) (3 +) (2 -) (3 -) (4 +))
(curry printf "plus: ~a, minus: ~a\n"))
;; plus: ((1 +) (2 +) (3 +) (4 +)), minus: ((1 -) (2 -) (3 -))
Теперь мы поцарапали поверхность функционального стиля: D