我已经了解了如何使用foldr
和lambda
查找列表中 1 的数量。但是如何使用if
条件或任何其他方法来验证列表是否只有一个 1。
(define (exactlyone L)
(foldr (lambda (elem count) (if (equal? elem 1) (+ count 1) count))
0 L)
)
count
如果可能,如何在 if 条件下使用该值?
1
在遍历所有列表之前,您无法确定 s 的数量,因此foldr
必须消耗所有项目。之后,只需测试返回的值是否count
为1
:
(define (exactlyOne L)
(= 1
(foldr (lambda (elem count)
(if (equal? elem 1)
(+ count 1)
count))
0
L)))
当然,最简单的方法是使用现有程序(例如count
),而不是重新发明轮子。这将在球拍中工作:
(define (exactlyOne lst)
(= 1
(count (curry equal? 1) lst)))
例如:
(exactlyOne '(1 2 3))
=> #t
(exactlyOne '(1 2 3 1))
=> #f
如果您允许折叠的内核函数捕获包含可变变量的词法范围,则可以解决此问题。然后你可以保持累加器类型为布尔值,但有足够的状态来进行计算。
伪代码:
(foldl (let ([s 0])
(lambda (accum item)
;; if (equal? item 1) and s is not already 2
;; increment s
;; return (equal? s 1)
)
#f list)
您无法使用不捕获任何环境的函数来解决此问题;但为什么要限制于此?根据定义,函数是代码加上词法环境。
在上面,累加器基本上是一个假人;我们甚至不看它,因为状态s
代表了我们需要的一切。我们可以使用一个布尔值s
,使得状态是累加器参数和s
. 我们可以在布尔累加器和布尔值之间划分状态s
(以便它们一起形成代表必要的三个状态的两位计数器)。
这是一个非正式的证明,它不能在没有可变环境的情况下仅使用布尔返回函数来解决:
观察结果必须是布尔值:是否只有一个1
,真或假?所以我们用作折叠内核的函数必须有一个布尔累加器,因为累加器是返回的。
折叠的累加器封装了核函数算法做出决定的整个状态。例如,如果该函数使用包含可变变量的词法范围,那将是作弊。
该算法需要累加器中的至少三个状态。累加器必须处于某个初始S0中,当看到 a 时,它从 S0 转换到S1,当看到另一个时1
,它从 S2 转换到S21
。然后这个累加器必须在折叠之外被解释为S0
并S2
表示假和S1
真。
虽然理论上我们可以改变访问项目之间的累加器类型,但我们没有这方面的信息;我们不知道哪个元素是最后一个。如果我们知道我们正在查看最后一个元素,我们可以将我们的三态累加器转换为布尔值并返回它。
这就是为什么 Sylwester 的答案的第二部分使用延续:那么算法,而不是转换到S2可以直接逃出折叠并产生错误;然后累加器可以是布尔值。(一个更简单的非本地退出机制就足以代替完整的延续,例如从词法块返回)。
最简单的方法是进行自己的递归:
(define (only-one predicate lst)
(let loop ((lst lst) (seen #f))
(cond ((null? lst) seen)
((not (predicate (car lst))) (loop (cdr lst) seen))
;; from here on we know predicate is true
(seen #f) ; at least two
(else (loop (cdr lst) #t))))) ; recurse with seen as #t
如果你想用折叠解决它,你可以:
(define (only-one predicate lst)
(call/cc
(lambda (return)
(foldl (lambda (e seen)
(cond ((not (predicate e)) seen) ; keep seen (whatever it is)
(seen (return #f)) ; short circuit at second match
(else #t))) ; change seen to #t
#f
lst))))
这用于call/cc
获取退出过程,以防我们在处理所有元素之前知道结果。#f
如果没有或不止一个匹配,它将是,如果只有一次,则为#t。
两者都这样工作:
(only-one (lambda (x) (equal? x 1)) '(0 1 2 3 4 5)) ; ==> #t
(only-one (lambda (x) (equal? x 1)) '(2 3 4 5)) ; ==> #f
(only-one (lambda (x) (equal? x 1)) '(0 1 2 1 3 4 5)) ; ==> #f