Scheme 如何确定列表中的原子数是偶数还是奇数
tScheme新手问题: 我需要使用递归确定一个列表是否包含偶数或奇数个原子。我知道最简单的方法是获取列表长度并确定它是偶数还是奇数,但我想看看如何使用递归来完成它Scheme 如何确定列表中的原子数是偶数还是奇数,scheme,Scheme,tScheme新手问题: 我需要使用递归确定一个列表是否包含偶数或奇数个原子。我知道最简单的方法是获取列表长度并确定它是偶数还是奇数,但我想看看如何使用递归来完成它 (oddatom (LIST 'x 'y 'v 'd 'r 'h 'y)) 应返回#t,而 (oddatom '((n m) (f p) l (u k p))) 应返回#f 感谢您的帮助。我愿意: (define (oddatom lst) (cond ((null? lst) #f)
(oddatom
(LIST 'x 'y 'v 'd 'r 'h 'y))
应返回#t
,而
(oddatom
'((n m) (f p) l (u k p)))
应返回#f
感谢您的帮助。我愿意:
(define (oddatom lst)
(cond
((null? lst) #f)
((not (pair? lst)) #t)
(else (not (eq? (oddatom (car lst)) (oddatom (cdr lst)))))))
这意味着:
car
或cdr
中可能只有一个是奇数(异或)(require rackunit)
(check-equal? (oddatom (list 'x 'y 'v 'd 'r 'h 'y)) #t)
(check-equal? (oddatom '((n m) (f p) l (u k p))) #f)
(check-equal? (oddatom '(a (b) c)) #t)
(check-equal? (oddatom (cons 1 2)) #f)
(check-equal? (oddatom 1) #t)
(check-equal? (oddatom '(1 (2 . 3))) #t)
以下是我的解决方案版本:
(define (oddatom? lst)
(let recur ((odd #f)
(x lst))
(cond ((null? x) odd)
((pair? x) (recur (recur odd (car x)) (cdr x)))
(else (not odd)))))
我喜欢,但我认为值得提供一个尾部递归版本来维护自己的堆栈。请注意,这是将非尾部递归转换为尾部递归的练习,这是Scheme中某些算法的重要技术。不过,在这种情况下,这可能并不那么重要,克里斯·杰斯特·杨(Chris Jester Young)答案中的代码确实让人感觉更自然。因此,将此视为一种练习,不一定是一种重大改进
这里的想法是,内部函数,odd?
,获取一个事物列表,以及一个值,该值指示到目前为止是否已看到奇数个原子(空列表除外)
(定义(oddaom?东西)
(让单数?((事物(列表事物))
(结果#f))
(续)
我们看不到东西了。我们看到的东西是奇数吗?
((空?事物)
结果)
我们的事物列表的形式是((x.y)…),所以我们继续循环
;;;(x y…),结果为*相同*值,因为我们没有
“看到”x或y了,我们只是“打开”了它们。
((配对?(汽车用品))
(奇数?(cons(caar事物)(cons(cdar事物)(cdr事物))结果)
我们的事物列表的形式是(()…),所以我们继续循环
;;(…),结果为*相同*值,因为我们没有“看到”任何结果
额外的原子。
((空?(汽车用品))
(奇数?(cdr事物)结果)
我们的事物列表的形式是(…),所以我们在(…)上递归,
但结果是一个翻转的值,因为我们又看到了一个原子。
(其他
(奇数?(cdr事物)(非结果‘‘‘‘‘‘)
最后两种情况可以组合在一起,使第二个递归参数基于(null?(car things))
的值,如下所示:
(定义(oddaom?东西)
(让单数?((事物(列表事物))
(结果#f))
(续)
((空?事物)
结果)
((配对?(汽车用品))
(奇数?(cons(caar事物)(cons(cdar事物)(cdr事物))结果)
(其他
(奇数?(cdr物品)(如果(空)(汽车物品))
结果
(不是结果‘‘‘‘‘‘)
这里有一个:
(define (odd-atom? obj)
(and (not (null? obj))
(or (not (pair? obj))
(let ((this? (odd-atom? (car obj)))
(rest? (odd-atom? (cdr obj))))
(or (and (not this?) rest?)
(and (not rest?) this?))))))
或者,从@uselpa学习来简化“或”这个?上面的rest?逻辑,另一个:
(define (odd-atom? obj)
(and (not (null? obj))
(or (not (pair? obj))
(not (eq? (odd-atom? (car obj))
(odd-atom? (cdr obj)))))))
如果'()
是一个原子(就像在CommonLisp中'()
也是T
),那么它应该是(奇数原子?)(()())
是\T
:
我不确定这是对这个问题的正确解释。你的答案不是计算亚名单中的原子,你可能是对的。我会更新我的答案。克里斯的答案实际上是平面列表的尾部递归。我记得我检查过类似版本的
count atoms
和类似于您的代码的性能,我很惊讶在我的测试中,较短的代码战胜了更聪明的算法,除了cdr
中所有原子的最坏情况。对于平面列表,差异可以忽略不计,但是我的会分配更多的堆空间。即便如此,Chris的版本对递归函数进行了两次调用,其中只有一次处于尾部位置。Olin Shivers在SRFI 1的评论中指出,以使算法尾部递归的名义使用基于堆的“堆栈”通常是不值得的。阅读“关于递归和迭代/反转的注释”。摘录:对这些算法进行编码的自然而有效的方法是递归的。这将中间临时列表结构转换为中间临时堆栈结构。在基于堆栈的系统中,这可以改进缓存位置并减轻GC系统的负载。不要站在你的头上重复!我同意克里斯杰斯特·杨的说法。不过,学习如何在这两种形式之间进行转换是一种很好的做法,因此我希望尽可能将这两种形式都包括在内(例如,如中所示)。不过,我会更新答案,以表明这不一定会有更高的性能,即使是这样,它也会以一些可读性为代价?应该是,但每个人都回来了。。。除了我的#2答案之外,每个人都是。@GoZoner根据《小阴谋家》(第10页,另见)的说法,“()不是原子。见上文。在方案中,“()不是原子,因此(奇数原子?)(())
应该返回#f。
(define (odd-atom? obj)
(and (not (null? obj))
(or (not (pair? obj))
(let ((this? (or (null? (car obj))
(odd-atom? (car obj))))
(rest? (odd-atom? (cdr obj))))
(not (eq? this? rest?))))))
> (odd-atom? '())
#f
> (odd-atom? '(()))
#t
> (odd-atom? '(() () ()))
#t
> (odd-atom? '(() ()))
#f
> (odd-atom? '(() (a)))
#f
> (odd-atom? '(() (a b)))
#t
> (odd-atom? '((a) (a b)))
#t
> (odd-atom? '((a b) (a b)))
#f
>