我有一个关系列表,我希望打印所有父亲的名字。由于没有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)
我不想在else
之后作任何发言。
6条答案
按热度按时间woobm2wo1#
很糟糕,所有的
if
都带有else
。但没关系,有一个杰出的什么都不做的IO
操作。有很多其他的方法来剥这只猫的皮。一个是模式匹配。
另一个特定于一元动作的方法是使用
when
。当然,这只是隐藏了
else
,它还是return ()
:fwzugrvs2#
Haskell解决此类问题的惯用方法是尽可能避免混合计算和I/O。
在这种情况下,不是“打印所有父亲的名字”,而是可以先“计算所有父亲的名字”(这里没有I/O),然后“打印计算出的名字”(这里是I/O)
不需要
if
,因为mapM_
为我们遍历列表,并且必须打印所有列表条目。unguejic3#
每个
if
必须有一个else
。如果你试图编译它,你会被告知有一个洞
所以你需要制造这种类型的东西。幸运的是,这很容易:
pure x
不执行任何操作,并返回x
。由于您尝试执行的操作非常常见,因此有两个专门为该任务设计的函数:
您可以在
Control.Monad
中找到这两个函数。vlju58qv4#
您可以编写一个函数,它 * 总是 * 写入名称,但要确保它只在包含
father
的值上被调用。filter
的定义隐藏了跳过列表中某些元素的逻辑。filter
的参数总是返回True
或False
,但filter
的结果只包含那些您要调用print
的元素。(这里,
sequence
只是将IO
值的列表转换为IO
值,通过“交换”IO
和[]
,main
必须是IO
值。您可以将其合并到printName
中,方法是将其定义为sequence . fmap printName . ...
,并将sequence . fmap foo
替换为traverse foo
。)请注意,
if foo then bar else baz
是完整case
表达式的语法糖但是,
case
表达式不必处理foo
参数的每个可能值。不过,看看当
r
* 与"father"
* 不匹配时会发生什么,这将是有启发性的。fd3cxomn5#
我觉得有必要解释一下为什么一个if在Haskell里必须有一个else。
Haskell是类型化lambda演算的一个实现,在lambda演算中我们有表达式和值没有其他东西。在它中,我们将表达式求值/约简为值或不能进一步约简的表达式。
在类型化lambda演算中,我们增加了类型和抽象,但是我们仍然要计算值和表达式,其中一个表达式是
if predicate then value else value
。这个if
表达式必须约简为一个值,因此if表达式的两个分支都必须约简为相同类型的值。如果我们有一个“if predicate then值”,这意味着我们有一个分支不会约简为一个值。您可以在此答案的上下文中交替使用 run、reduce 和 evaluate。
当我们运行Haskell代码时,我们将lambda项简化为无法进一步简化的值或表达式。编译器的存在是为了帮助我们编写有效的lambda项。
通过lambda演算,我们看到if语句在求值时必须化简为一个值(或者能够这样做),因为Haskell是实现类型化lambda演算的,Haskell中的if表达式如果没有else,就不可能 * 一直 * 求值为一个值。
TL;DR
“if... then... else”陈述式在评估时应该减少为value。只要if陈述式的两个分支评估为相同的型别,它就会正确评估。
如果任何分支没有计算出一个值,或者将要计算出不同类型的值,这不是一个有效的lambda项,代码将不会进行类型检查。
lskq00tm6#
我也在为这种风格和Haskell格式要求而挣扎。但我发现,与其强调必需的[else],不如使用[else do]来包括所有后续代码行,而无需在并发行上添加额外的缩进,例如..
证明代码可以采用更简单的形式