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
    ,是真还是假?因此,我们用作折叠内核的函数必须有一个布尔累加器,因为累加器是返回的

  • 折叠的累加器封装了核函数算法作出决策的整个状态。例如,如果函数使用包含可变变量的词法作用域,那就是欺骗

  • 该算法要求累加器中至少有三种状态。累加器必须处于某个初始S0,当看到
    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
    ,是真还是假?因此,我们用作折叠内核的函数必须有一个布尔累加器,因为累加器是返回的

  • 折叠的累加器封装了核函数算法作出决策的整个状态。例如,如果函数使用包含可变变量的词法作用域,那就是欺骗