我编写了以下函数来查找5个变量中只有一个是否为真:
(define (oneof v w x y z)
(or (and v (not w) (not x) (not y) (not z))
(and w (not v) (not x) (not y) (not z))
(and x (not v) (not w) (not y) (not z))
(and y (not v) (not w) (not x) (not z))
(and z (not v) (not w) (not x) (not y)) ))
(xor只有2个参数)
然而,这是非常必要的,而不是功能性的。此外,我想写一个函数(oneof N),它将是通用的而不是5个变量的特定。如何才能做到这一点?感谢。
编辑:正如评论中指出的那样,代码是“重复的”而不是“命令性的”,尽管仍需要改进。
答案 0 :(得分:5)
你的最后一点是准确的:听起来像xor
是正确的功能,但这只是两个参数。如果xor
采用任意数量的参数可能会更好,但鉴于它没有,我们可以自己实现。
也许最天真的方式就是计算真实值的数量并检查该数字是否正好1.我们可以使用count
或for/sum
执行此操作,具体取决于您的偏好:
; using count
(define (xor . args)
(= 1 (count identity args)))
; using for/sum
(define (xor . args)
(= 1 (for/sum ([x (in-list args)])
(if x 1 0))))
这两个都有效,但它们不保留Racket的xor
的有用属性,它在成功时返回单个truthy元素而不是总是返回布尔值。为此,我们可以使用foldl
,foldr
或for/fold
进行折叠。但是,鉴于我们希望尽快退出,使用#:final
的{{1}}选项非常方便:
for/fold
然而,这实际上仍然不是最佳。 (define (xor . args)
(for/fold ([found #f])
([arg (in-list args)])
#:final (and found arg)
(if (and found arg) #f
(or found arg))))
的双参数版本是一个函数,而不是像xor
和and
这样的宏,因为它的任何一个参数都不能是惰性的。但是,实际上可以有多个参数or
。为了添加这种短路行为,我们可以将xor
写为宏:
xor
一般来说,这就像(define-syntax xor
(syntax-rules ()
[(_) #f]
[(_ x) x]
[(_ x rest ...)
(let ([v x])
(if v
(and (nor rest ...) v)
(xor rest ...)))]))
的函数版本一样:
xor
然而,像> (xor #f #f #f #f #f)
#f
> (xor #f #f 1 #f #f)
1
> (xor #f #f 1 2 #f)
#f
和and
一样,它有时会“短路”,如果结果不会影响表达式,则不会评估表达式:
or
(请注意,在最后一个示例中永远不会打印> (xor #f #f #f #f (begin (displayln "hello!") #f))
hello!
#f
> (xor #f #f 1 #f (begin (displayln "hello!") #f))
hello!
1
> (xor #f #f 1 2 (begin (displayln "hello!") #f))
#f
。)
这是一个好主意,这是一个坏主意......?我真的不知道。这种行为似乎不太可能非常有用,并且增加了很多复杂性。它还可以防止hello!
被高阶使用,但您可以使用xor
来解决这个问题,并在表达式位置使用syntax-id-rules
时扩展到过程版本。尽管如此,它仍然很有趣,并且它使其行为与xor
和and
更加一致,所以我认为我会将其包含在内以保证完整性。
答案 1 :(得分:3)
你可以在任意长度列表中计算你有多少true
个值,如果这个数字是1
那么我们就是好的(请记住在Scheme中)任何非false
值都被视为true
)。另请注意如何使用点表示法创建具有可变数量参数的过程:
(define (oneof . vars)
(= 1
(count (lambda (v) (not (false? v)))
vars)))
例如:
(oneof #f #f #f #f #t)
=> #t
(oneof #f #f #f #f #f)
=> #f
(oneof #t #t #t #t #t)
=> #f
答案 2 :(得分:3)
另一个更简洁的解决方案如下:
(define (oneof . vars)
(= 1 (count identity vars)))