如何在Haskell中使用if-then-else语句而没有其他条件?

Swa*_*rma 4 haskell

我有一份关系清单,我希望打印所有父亲的名字.由于没有else条件,以下代码不起作用:

relations = [("father", "arushi", "anandan"), ("mother", "arushi", "abigale"), ("father", "anandan", "ayuta"), ("mother", "anandan", "akanksha")]

father ((r, c, f):xs) = if r == "father" then print(f)

main = do
    father (relations)
Run Code Online (Sandbox Code Playgroud)

我不想在之后发表任何声明else.

Dan*_*ner 21

太糟糕了,所有人都if来了else.但是没关系,有一个非凡的无所事事IO行动.

father ((r, c, f):xs) = if r == "father" then print f else return ()
Run Code Online (Sandbox Code Playgroud)

还有很多其他方法可以给这只猫留下皮肤.一种是模式匹配.

father (("father", c, f):xs) = print f
father ((r, c, f):xs) = return ()
Run Code Online (Sandbox Code Playgroud)

另一个特定于monadic动作的是使用when.

father ((r, c, f):xs) = when (r == "father") (print f)
Run Code Online (Sandbox Code Playgroud)

当然,那只是隐藏了else,这又是return ():

when p act = if p then act else pure () -- okay, it's actually pure, not return
Run Code Online (Sandbox Code Playgroud)

  • @SwapnilSharma如果经过你自己的一些尝试后,你仍然遇到麻烦,一些关于你尝试过的细节以及它出错的地方都会提出一个额外的问题.但这是一个*单独*问题. (4认同)

chi*_*chi 11

解决此类问题的惯用Haskell方法是尽可能避免混合计算和I/O.

在这种情况下,您可以先"计算所有父亲的名字"(这里没有I/O),然后"打印计算名称"(这里是I/O),而不是"打印所有父亲的名字".

relations = 
   [ ("father", "arushi", "anandan")
   , ("mother", "arushi", "abigale")
   , ("father", "anandan", "ayuta")
   , ("mother", "anandan", "akanksha")
   ]

-- compute only the fathers
fathers = [ f | ("father", _, f) <- relations ]

-- print them
main :: IO ()
main = mapM_ putStrLn fathers
Run Code Online (Sandbox Code Playgroud)

不需要if,因为我们mapM_遍历列表,并且必须打印所有列表条目.


dfe*_*uer 6

每个人都if必须拥有else.

father ((r, c, f):xs) =
  if r == "father"
    then print f
    else _what
Run Code Online (Sandbox Code Playgroud)

如果你试图编译它,你会被告知有一个漏洞

_what :: IO ()
Run Code Online (Sandbox Code Playgroud)

所以你需要制造那种类型的东西.幸运的是,这很简单:

father ((r, c, f):xs) =
  if r == "father"
    then print f
    else pure ()
Run Code Online (Sandbox Code Playgroud)

pure x没有做任何回报x.

由于您要做的事情很常见,因此有两个专门为此任务设计的功能:

when :: Applicative f => Bool -> f () -> f ()
when b m = if b then m else pure ()

unless :: Applicative f => Bool -> f () -> f ()
unless = when . not
Run Code Online (Sandbox Code Playgroud)

您可以在中找到这两个功能Control.Monad.

father ((r, c, f):xs) =
  when (r == "father") $ print f
Run Code Online (Sandbox Code Playgroud)


che*_*ner 6

您可以编写一个始终写入名称的函数,但确保只在包含的值上调用它father.

relations :: [(String,String,String)]
relations = [("father", "arushi", "anandan")
            ,("mother", "arushi", "abigale")
            ,("father", "anandan", "ayuta")
            ,("mother", "anandan", "akanksha")
            ]

printName :: (String,String,String) -> IO ()
printName (_, _, name) = print name

printFathers :: [(String,String,String)] -> [IO ()]
printFathers = fmap printName . filter (\(f, _, _) -> f == "father")

main = sequence (printFathers relations)
Run Code Online (Sandbox Code Playgroud)

定义filter隐藏了跳过列表中某些元素的逻辑.filter始终返回True或的参数False,但结果filter只包含您要调用的元素print.

(sequence在这里,刚刚转列表IO值转换成单一的IOmain必须是"交换" IO[]你可以纳入到这个printName定义它sequence . fmap printName . ...,并更换sequence . fmap footraverse foo.)


请注意,这if foo then bar else baz是完整case表达式的语法糖

case foo of
  True -> foo
  False -> baz
Run Code Online (Sandbox Code Playgroud)

但是,case表达式不必处理foo参数的每个可能值.你可以

father ((r, c, f):xs) = (case r of "father" -> print f) : father xs
Run Code Online (Sandbox Code Playgroud)

但是,看看r 匹配时发生什么,这将是有益的"father".