球拍中'oneof'功能的功能变体 [英] Functional variant of 'oneof' function in Racket
问题描述
我写了以下函数来查找5个变量中是否有一个是真的:
(define(one of vwxyz )
(或(和v(不是w)(不是x)(不是y)(不是z))
(和w(不是v)(不是x)(不是y) )
(和x(not v)(not w)(not y)(not z))
(and y(not v)(not w)(not x)(not z))
(和z(不是v)(不是w)(不是x)(不是y))))
(xor只有2个参数)
然而,这是非常必要的,并且不起作用。此外,我想写一个函数(N的一个),它将是通用的而不是针对5个变量的特定。如何才能做到这一点?编辑:正如评论中指出的那样,代码是'重复的',而不是'必要的',尽管它的改进仍然存在。
你的最后一点是准确的:听起来像 xor
是正确的函数, ,但它只需要两个参数。如果 xor
带有任意数量的参数,那么它可能会更好,但是假设它没有,我们可以自己实现它。
也许最天真的方法是计算真值的数量并检查数字是否恰好为1.我们可以用 count
或 for / sum
,具体取决于您的偏好:
;使用计数
(define(xor。args)
(= 1(count identity args)))
;使用for / sum
(define(xor。args)
(= 1(for / sum([x(in-list args)])
(if x 1 0))))
这两种方法都有效,但它们不保留Racket的 xor
,它在成功时返回单个truthy元素,而不是总是返回一个布尔值。要做到这一点,我们可以使用fold,使用 foldl
, foldr
或折
。但是,鉴于我们希望尽快退出,请使用的
非常方便:#:final
选项作为/ 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
的双参数版本是一个函数,而不是像和
和之类的宏。
,因为它的任何参数都不能懒惰。然而,实际上可以有许多参数 xor
。为了增加这种短路行为,我们可以把 xor
写成一个宏:
< code $(
$($ x $ b $休息...)
(let([vx])
(如果v
(并且(也不休息...)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
然而,像和
和或
,它有时会短路,不评估表达式,如果它们的结果不合适:
> (xor #f #f #f #f(begin(displaylnhello!)#f))
hello!
#f
> (xor #f #f 1 #f(begin(displaylnhello!)#f))
hello!
1
> (xor #f #f 1 2(begin(displaylnhello!)#f))
#f
(请注意,在上一个例子中 hello!
从不会打印出来。)
这是一个好主意,这是一个坏主意......?我不知道。这种行为看起来不太可能是超级有用的,并且增加了很多复杂性。它还可以防止 xor
使用更高阶的命令,但是可以通过 syntax-id-rules
来解决<当在表达式中使用 xor
时,扩展到过程版本。尽管如此,它还是有趣的,它使得它的行为与和
和或
更加一致,所以我想我会包括它的完整性。
I have written following function to find if one and only one of 5 variables is true:
(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 takes only 2 arguments)
However, it is very imperative and not functional. Moreover, I want to write a function (oneof N) which will be generic rather than specific for 5 variables. How can this be done? Thanks.
Edit: As pointed out in the comments, the code is 'repetitive' and not 'imperative', though the need for its improvement remains.
Your final note is accurate: it sounds like xor
is the right function but this, but it only takes two arguments. It would likely be better if xor
took any number of arguments, but given that it doesn’t, we can implement it ourselves.
Perhaps the most naïve way would just to be to count the number of truthy values and check if that number is precisely 1. We can do this with count
or for/sum
, depending on your preference:
; 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))))
Both of these work, but they don’t preserve a useful property of Racket’s xor
, which returns the single truthy element upon success rather than always returning a boolean. To do this, we could use a fold, using foldl
, foldr
, or for/fold
. However, given that we want to ideally exit as soon as possible, using the #:final
option of for/fold
is pretty convenient:
(define (xor . args)
(for/fold ([found #f])
([arg (in-list args)])
#:final (and found arg)
(if (and found arg) #f
(or found arg))))
However, this is actually still not really optimal. The two-argument version of xor
is a function, not a macro like and
and or
, because it cannot be lazy in either of its arguments. However, a many-argument xor
actually can be. In order to add this short-circuiting behavior, we can write xor
as a macro:
(define-syntax xor
(syntax-rules ()
[(_) #f]
[(_ x) x]
[(_ x rest ...)
(let ([v x])
(if v
(and (nor rest ...) v)
(xor rest ...)))]))
In general, this works just like the function versions of xor
:
> (xor #f #f #f #f #f)
#f
> (xor #f #f 1 #f #f)
1
> (xor #f #f 1 2 #f)
#f
However, like and
and or
, it sometimes "short-circuits", not evaluating expressions if their results will not mater:
> (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
(Note that hello!
is never printed in the last example.)
Is this a good idea, is it a bad idea…? I don’t really know. It does seem unlikely that this behavior will ever be super useful, and it adds a lot of complexity. It also prevents xor
from being used higher-order, but you could get around that with syntax-id-rules
and expanding to the procedure version when xor
is used in an expression position. Still, it’s potentially interesting, and it makes its behavior more consistent with and
and or
, so I figured I’d include it for completeness.
这篇关于球拍中'oneof'功能的功能变体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!