Scheme 使用racket在列表中只搜索一个“;1“;
我已经了解了如何使用Scheme 使用racket在列表中只搜索一个“;1“;,scheme,lisp,racket,fold,foldleft,Scheme,Lisp,Racket,Fold,Foldleft,我已经了解了如何使用foldr和lambda来查找列表中的1数。但是如何使用if条件或任何其他方法来验证列表是否只有一个1 (define (exactlyone L) (foldr (lambda (elem count) (if (equal? elem 1) (+ count 1) count)) 0 L) ) 如果可能的话,如何在if条件下使用count值?在遍历所有列表之前,您无法确定1s的数量,因此必须foldr必须消耗所有项。之后,只需测试count的返
foldr
和lambda
来查找列表中的1数。但是如何使用if
条件或任何其他方法来验证列表是否只有一个1
(define (exactlyone L)
(foldr (lambda (elem count) (if (equal? elem 1) (+ count 1) count))
0 L)
)
如果可能的话,如何在if条件下使用
count
值?在遍历所有列表之前,您无法确定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
在遍历所有列表之前,您无法确定
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
最简单的方法是执行自己的递归:
(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
最简单的方法是执行自己的递归:
(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
如果允许fold的内核函数捕获包含可变变量的词法作用域,则可以解决此问题。然后,您可以保持累加器类型为布尔型,但仍有足够的状态来进行计算 伪代码:
(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
,是真还是假?因此,我们用作折叠内核的函数必须有一个布尔累加器,因为累加器是返回的1
时,累加器从该S0过渡到S1,当看到另一个1
时,累加器从该S0过渡到S2。然后,该累加器必须在折叠之外解释为S0
和S2
表示假,以及S1
真这就是为什么西尔维斯特的答案的第二部分使用了延续:然后算法,而不是转换到S2,可以直接逃出折叠并产生错误;累加器可以是布尔型的。(一个简单得多的非局部退出机制足以取代完整的连续体,例如从词法块返回)。如果允许fold的内核函数捕获包含可变变量的词法作用域,则可以解决此问题。然后,您可以保持累加器类型为布尔型,但仍有足够的状态来进行计算 伪代码:
(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
,是真还是假?因此,我们用作折叠内核的函数必须有一个布尔累加器,因为累加器是返回的