让我们一步一步地修复你的代码:
(define (split lst)
(cond ((null? lst) lst)
((null? (cdr lst)) lst)
((cons (cons (car lst) (split (cddr lst))) (cons (cadr lst) (split (cddr lst)))))))
我注意到的第一件事是在cond 的最后一种情况下缺少else。条件应该看起来像:
(cond (question-1 answer-1)
(question-2 answer-2)
...
(else else-answer))
插入else 后,您的代码如下所示:
(define (split lst)
(cond ((null? lst) lst)
((null? (cdr lst)) lst)
(else
(cons (cons (car lst) (split (cddr lst))) (cons (cadr lst) (split (cddr lst)))))))
接下来是第一个基本情况,或(null? lst) cond 问题的答案。在一个空列表上它应该返回什么?
似乎无论列表有多长,它都应该始终返回一个恰好包含两个内部列表的列表。因此,当lst 为空时,逻辑答案将是(list '() '())。
(define (split lst)
(cond ((null? lst)
(list '() '()))
((null? (cdr lst)) lst)
(else
(cons (cons (car lst) (split (cddr lst))) (cons (cadr lst) (split (cddr lst)))))))
接下来是第二个基本情况,即(null? (cdr lst)) cond 问题的答案。同样,它应该返回一个正好包含两个内部列表的列表:
(list ??? ???)
第一个索引应该放在第一个列表中,然后没有什么可以放在第二个列表中。
(list (list (car lst)) '())
在您的代码上下文中:
(define (split lst)
(cond ((null? lst)
(list '() '()))
((null? (cdr lst))
(list (list (car lst)) '()))
(else
(cons (cons (car lst) (split (cddr lst))) (cons (cadr lst) (split (cddr lst)))))))
现在,这个函数的行为是什么?
> (split '(1 2 3 4 5 6))
'((1 (3 (5 () ()) 6 () ()) 4 (5 () ()) 6 () ()) 2 (3 (5 () ()) 6 () ()) 4 (5 () ()) 6 () ())
仍然不是你想要的。那么最后一种情况,递归情况,应该怎么做呢?
考虑一下您“给予”了什么以及您需要“生产”什么。
给定:
-
(car lst)第一个元素
-
(cadr lst)第二个元素
-
(split (cddr lst)) 正好包含两个内部列表的列表
你应该产生:
第一个??? 孔包含第一个元素和两个内部列表中的第一个,第二个??? 孔包含第二个元素和两个内部列表中的第二个。
这建议这样的代码:
(list (cons (car lst) (first (split (cddr lst))))
(cons (cadr lst) (second (split (cddr lst)))))
或者,因为car 获得第一个而cadr 获得第二个:
(list (cons (car lst) (car (split (cddr lst))))
(cons (cadr lst) (cadr (split (cddr lst)))))
在您的代码上下文中:
(define (split lst)
(cond ((null? lst)
(list '() '()))
((null? (cdr lst))
(list (list (car lst)) '()))
(else
(list (cons (car lst) (car (split (cddr lst))))
(cons (cadr lst) (cadr (split (cddr lst))))))))
使用它产生你想要的:
> (split '(1 2 3 4 5 6))
'((1 3 5) (2 4 6))
> (split '(1 2 3 4 5 6 7))
'((1 3 5 7) (2 4 6))
> (split '("a" "little" "bit" "of" "that" "to" "spice" "things" "up"))
'(("a" "bit" "that" "spice" "up") ("little" "of" "to" "things"))
现在这和你以前的有什么区别?
你之前的代码:
(cons (cons (car lst) (split (cddr lst)))
(cons (cadr lst) (split (cddr lst))))
固定版本:
(list (cons (car lst) (car (split (cddr lst))))
(cons (cadr lst) (cadr (split (cddr lst)))))
第一个区别是你的原始版本在外面使用cons,而固定版本使用list。这是因为(list ??? ???) 总是返回正好包含两个元素的列表,而(cons ??? ???) 可以返回任何大小大于 1 的列表,它将第一个元素合并到现有的第二个列表中。 (list ??? ???) 是您想要的,因为您指定它应该返回正好包含两个内部列表的列表。
第二个区别在于你如何使用递归调用(split (cddr lst))。
这与您如何解释递归案例的“给定”部分有关。你假设第一次调用split 会给你第一个“内部”列表,第二次调用split 会给你第二个“内部”列表。事实上,它为您提供了这两次的 both 列表。所以对于第一个你必须得到它的“第一”或car,而对于第二个你必须得到它的“第二”或cadr。