Haskell:检查所有条件是否都为真。如果是,则返回true,否则返回false。

3
我今天下午写了一些Haskell代码,我有一个必须满足的条件列表。如果它们都为真,我想返回真,如果其中一个是假,则返回假。
我有一个方法可以工作,但我只是想知道是否有更好的实现方式以提高可读性/效率。
以下是我的代码:
checkMatch :: Person -> Person -> Bool
checkMatch seeker candidate
    |  gender candidate == preferedGender seeker 
    && gender seeker == preferedGender candidate
    && minAcceptableAge seeker <= age candidate 
    && maxAcceptableAge seeker >= age candidate
    && minAcceptableAge candidate <= age seeker
    && maxAcceptableAge candidate >= age seeker = True
    |  otherwise = False

性别的定义是:

data Gender = Male | Female (Eq)

我刚刚对&&和|进行了对齐,以使这看起来更好一些,但我感觉肯定有更好的方法,但在谷歌上搜索后无法得出任何结论。


顺便说一下,它是被优先选择的 - leftaroundabout
6个回答

7
您可以去掉“guards”并使用“and”来检查您的条件:
checkMatch :: Person -> Person -> Bool
checkMatch seeker candidate = and [ 
    gender candidate           == preferedGender seeker 
  , gender seeker              == preferedGender candidate
  , minAcceptableAge seeker    <= age candidate 
  , maxAcceptableAge seeker    >= age candidate
  , minAcceptableAge candidate <= age seeker
  , maxAcceptableAge candidate >= age seeker
  ]

太棒了,我知道可以使用所有内容,但不知道id的存在。谢谢! :) - visi0n
2
你不需要 id。使用 and :: [Bool] -> Bool,它也在 Prelude 中。 - leftaroundabout
@visi0n 你也可以使用 all (== True),但 all id 的效果是一样的。 - jtobin
2
@visi0n 你可能也会喜欢 inRange - Daniel Wagner

2
你可以滥用 maybe monad 作为语法糖,如下所示:
a |==| b = guard $ a == b
a |>=| b = guard $ a >= b
a |<=| b = guard $ a <= b
a |/=| b = guard $ a /= b

checkMatch :: Person -> Person -> Bool
checkMatch seeker candidate = Just () == do
  gender candidate |==| preferedGender seeker
  gender seeker |==| preferedGender candidate
  minAcceptableAge seeker |<=| age candidate 
  maxAcceptableAge seeker |>=| age candidate
  minAcceptableAge candidate |<=| age seeker
  maxAcceptableAge candidate |>=| age seeker

你的解决方案相当可爱。 - Justin L.
@JustinL。是的,我的想象力有些过度了。 - Jimmy Hoffa
1
你可能可以使用某种“assert”领域特定语言来解决问题。 - Justin L.

1

首先,您可以使用,来简单地保护条件。

接下来,我应该将minAccAge <= age && maxAccAge >= age模式重构为一个专用函数,比如说

acceptsAge :: Person -> Age -> Bool
judge `acceptsAge` age
   = age >= minAcceptableAge judge && age <= maxAcceptableAge judge

它仍然存在

checkMatch :: Person -> Person -> Bool
checkMatch seeker candidate
  | gender candidate == preferedGender seeker 
  , gender seeker == preferedGender candidate
  , seeker `acceptsAge` age candidate 
  , candidate `acceptsAge` age seeker         = True

我会把它留在这里,两个preferedGender检查无法减少太多。

我认为我更喜欢使用and而不是guards,但我喜欢acceptsAge函数。我将其放在where块中。谢谢! - visi0n

1
你所问的是一个风格问题,没有对错之分。不过,这里有几个建议。
首先,你正在编写等效于以下模式的代码:
isTrue value | value == True = True
             | otherwise     = False

这当然可以简化为:

isTrue value = value

其次,由于您正在检查需要全部为真的多个测试,您可以利用 "and" 函数并将您的测试作为列表元素传递。如果所有元素都为 True,则返回 True,否则它会短路并返回 False。
将这两个想法结合起来,我们得到:
checkMatch :: Person -> Person -> Bool
checkMatch seeker candidate
  = and [gender candidate == preferedGender seeker, 
         gender seeker == preferedGender candidate,
         minAcceptableAge seeker <= age candidate,
         maxAcceptableAge seeker >= age candidate,
         minAcceptableAge candidate <= age seeker,
         maxAcceptableAge candidate >= age seeker]

"这可能是我写的方式。"

1
我注意到你的代码存在一些重复,因为你在两个方向上都检查了所有内容。我建议定义一个名为checkAcceptable的辅助函数,只检查一个方向,然后调用该函数两次:
checkAcceptable :: Person -> Person -> Bool
checkAcceptable seeker candidate =
  gender candidate == preferedGender seeker &&
  minAcceptableAge seeker <= age candidate &&
  maxAccetableAge seeker >= age candidate

checkMatch :: Person -> Person -> Bool
checkMatch seeker candidate =
  checkAcceptable seeker candidate &&
  checkAcceptable candidate seeker

1
为了代码最小化,你可以这样写。
inRange x (a,b) = a <= x && x <= b

checkOneWay a b = gender b == preferredGender a
               && age b `inRange` (minAcceptableAge a, maxAcceptableAge a)

checkMatch candidate seeker = checkOneWay candidate seeker
                           && checkOneWay seeker candidate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接