Racket中“oneof”功能的功能变体

时间:2016-08-08 03:07:18

标签: functional-programming scheme racket xor

我编写了以下函数来查找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个变量的特定。如何才能做到这一点?感谢。

编辑:正如评论中指出的那样,代码是“重复的”而不是“命令性的”,尽管仍需要改进。

3 个答案:

答案 0 :(得分:5)

你的最后一点是准确的:听起来像xor是正确的功能,但这只是两个参数。如果xor采用任意数量的参数可能会更好,但鉴于它没有,我们可以自己实现。

也许最天真的方式就是计算真实值的数量并检查该数字是否正好1.我们可以使用countfor/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元素而不是总是返回布尔值。为此,我们可以使用foldlfoldrfor/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)))) 的双参数版本是一个函数,而不是像xorand这样的宏,因为它的任何一个参数都不能是惰性的。但是,实际上可以有多个参数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时扩展到过程版本。尽管如此,它仍然很有趣,并且它使其行为与xorand更加一致,所以我认为我会将其包含在内以保证完整性。

答案 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)))