如何短路我自己的 andmap 函数?

How can I short circuit my own andmap function?

我有:

(define (andmap1 pred lst)
  (cond
    [(empty? lst)true]
    [(pred (first lst)) (andmap1 pred (rest lst))]
    [else false]))

对于

(andmap1 positive? (list 1 1 2))
(andmap1 positive? (list 1 -1 2))

我答对了。

但是当我输入时:

(andmap1 positive? (list "Hi" 1))
(andmap1 positive? (list 'Hi 1))
(andmap1 positive? (list false 1))

我收到一个错误。 如果 pred 和列表中的元素不匹配,我该如何停止函数? (pred 可以是任何谓词)

你的 andmap1 没问题,已经短路了。

> (andmap1 positive? (list -1 "Hi"))
#f

这里short-circuit的意思是,如果x是列表中y之前的元素,并且(p x)求值为false,那么(p y)不会被评估。在我上面的示例中,(positive? "Hi") 未被计算,因为 (p -1) 已经计算为 false。

对于您的示例,(positive? "Hi") 被评估,因为 "Hi" 之前没有元素,当应用于 positive? 时,returns 为 false。并且因为 positive? 的合同是 number? -> number?,你得到了一个违反合同的错误(因为 "Hi" 不是一个数字)。

但是,如果您希望 (andmap1 positive? (list "Hi" 1)) 之类的东西在不引起错误的情况下工作怎么办?

一种可能是放宽positive?,这样就不会导致违反合同的错误。

;; my-positive? :: any/c -> boolean?
(define (my-positive? x)
  (and (number? x) (positive? x)))

> (my-positive? "Hi")
#f
> (my-positive? -1)
#f
> (my-positive? 1)
#t

然后:

> (andmap1 my-positive? (list "Hi" 1))
#f

另一种可能性是处理 andmap1:

中的错误
(define (andmap1 pred lst)
  (cond
    [(empty? lst) true]
    [else 
     (with-handlers ([exn:fail? (lambda (e) #f)])
       (cond
         [(pred (first lst)) (andmap1 pred (rest lst))]
         [else false]))]))

> (andmap1 positive? (list "Hi" 1))
#f

一般来说,您不能:处理错误会解决部分问题,但不是全部。你不能的原因很重要。为了能够在根据参数调用过程之前停止,您必须能够回答两个问题:

  • 使用这个参数,这个过程会引发某种异常吗?
  • 使用这个参数,这个过程会不会终止?

如您所说,程序可以是任何程序。如果第一个是真的,那么如果你能处理错误,你就能处理问题。但如果第二个是真的,这对你没有帮助。

这些问题,众所周知,can't be answered(这个 link 显然不是对此的原始证明,但它是一个可爱的描述)。

如果您已经知道有一些您的谓词不喜欢的参数,您最大的希望就是将它包装在某种防护中,这将导致它在这些参数上失败。您可以通过传递一个显式检查的不同谓词来做到这一点,或者您可以提供一个可选的 'guard' 谓词:

(define (andmap1 pred lst #:guard (guard #f))
  (define effective-pred
    (if guard
        (λ (x)
          (and (guard x) (pred x)))
        pred))
  (cond
    [(empty? lst) true]
    [(effective-pred (first lst))
     (andmap1 effective-pred (rest lst))]
    [else false]))

现在 (andmap1 positive? '(1 2 3 "x")) 会引发错误,但 (andmap1 positive '(1 2 3 "x") #:guard integer?) 只会 return #f.