如何短路我自己的 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
.
我有:
(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
.