Racket中"oneof"功能的功能变体

rns*_*nso 2 scheme functional-programming xor racket

我编写了以下函数来查找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))  ))
Run Code Online (Sandbox Code Playgroud)

(xor只有2个参数)

但是,这是非常必要的,而不是功能性的.此外,我想写一个函数(oneof N),它将是通用的而不是5个变量的特定.如何才能做到这一点?谢谢.

编辑:正如评论中所指出的,代码是"重复的"而不是"命令性的",尽管仍需要改进.

Ale*_*ing 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))))
Run Code Online (Sandbox Code Playgroud)

这两个都有效,但它们不保留Racket的有用属性xor,它在成功时返回单个truthy元素,而不是总是返回一个布尔值.要做到这一点,我们可以使用折叠,使用foldl,foldrfor/fold.但是,鉴于我们希望尽快退出,使用#:final选项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))))
Run Code Online (Sandbox Code Playgroud)

然而,这实际上仍然不是最佳的.的两个参数的版本xor是一个函数,而不是像宏andor,因为它不能在任其参数懒惰.然而,xor实际上可以有很多论点.为了添加这种短路行为,我们可以写成xor一个宏:

(define-syntax xor
  (syntax-rules ()
    [(_) #f]
    [(_ x) x]
    [(_ x rest ...)
     (let ([v x])
       (if v
           (and (nor rest ...) v)
           (xor rest ...)))]))
Run Code Online (Sandbox Code Playgroud)

一般来说,这就像以下的功能版本一样xor:

> (xor #f #f #f #f #f)
#f
> (xor #f #f 1 #f #f)
1
> (xor #f #f 1 2 #f)
#f
Run Code Online (Sandbox Code Playgroud)

然而,喜欢andor它有时会"短路",如果结果不会影响,则不评估表达式:

> (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
Run Code Online (Sandbox Code Playgroud)

(请注意,hello!在最后一个示例中永远不会打印出来.)

这是一个好主意,这是一个坏主意......?我真的不知道.这种行为似乎不太可能非常有用,并且增加了很多复杂性.它还可以防止xor被高阶使用,但是syntax-id-rulesxor在表达式位置使用时,你可以使用并扩展到过程版本.尽管如此,它可能很有趣,并且它使其行为更加符合andor,所以我认为我将它包括在内以保证完整性.