我有一份关系清单,我希望打印所有父亲的名字.由于没有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)
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_遍历列表,并且必须打印所有列表条目.
每个人都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)
您可以编写一个始终写入名称的函数,但确保只在包含的值上调用它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值转换成单一的IO值main必须是"交换" IO和[]你可以纳入到这个printName定义它sequence . fmap printName . ...,并更换sequence . fmap foo同traverse 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".