球拍中'oneof'功能的功能变体 [英] Functional variant of 'oneof' function in Racket

查看:105
本文介绍了球拍中'oneof'功能的功能变体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了以下函数来查找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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆