为什么我需要在Haskell代码中定义这两个变量?

时间:2014-09-01 04:26:08

标签: haskell

-- file: ch03/BogusPattern.hs
data Fruit = Apple | Orange
    deriving (Show)

apple = "apple"
orange = "orange"

whichFruit :: String -> Fruit
whichFruit f = case f of
                "apple" -> Apple
                "orange" -> Orange

在这段代码中我为什么需要

apple = "apple"
orange = "orange"

我也尝试过这个:

-- file: ch03/BogusPattern.hs
data Fruit = Apple | Orange
    deriving (Show)

apple = "f1"
orange = "f2"

whichFruit :: String -> Fruit
whichFruit f = case f of
                "f1" -> Apple
                "f2" -> Orange

它并没有奏效。

Ghci说f1f2不在范围内。

whichFruit函数是否应该尝试将f匹配到两个字符串中,并根据案例返回水果类型?

谢谢。

2 个答案:

答案 0 :(得分:5)

您不需要苹果和橙色变量。 whichFruit采取一串。在您第一次尝试时,您只需拨打whichFruit "orange",它就会匹配第二种情况(“橙色”)。在第二个示例中,您需要使用whichFruit "f2"来完成同样的事情。

您还可以定义x = "orange"并致电whichFruit x以获得橙色。

答案 1 :(得分:0)

拉斐尔的回答是完全正确的,但我想补充一些我认为相关的观点。

首先,更具概念性的东西。调用orangebanana变量并不完全正确。相反,它们是常量函数或类型String的常量。之所以如此,是因为你无法真正改变它们的价值(因此,我们称之为不变性)。关于你关于他们需要的问题,我不确定,但可能这个例子的想法只是定义两个不同的"水果",以便一个人可以和whichFruit一起玩GHCi(或其他任何地方)传递appleorange而不是字符串文字“apple”和“orange”。

尽管如此,尽管是一个说明非常基本功能的简单示例,但值得一提的是,不建议定义函数whichFruit的方式(根本没有)。此功能仅适用于两个不同的输入:“apple”和“orange”。除此之外的任何内容都会导致whichFruit意外失败。由于whichFruit仅针对其域(String)的子集定义,因此称为部分函数。换句话说,类型签名告诉我们whichFruit是一个期望String并返回Fruit的函数,但是当它被调用时,比如说“banana”,它会给出错误。想象一下有人导入你的模块(定义{{​​1}})并开始玩它。在GHCi上浏览函数时,他找到whichFruit并使用whichFruit检查其类型签名。他很可能希望:t whichFruit能够使用任何字符串,但是当他调用whichFruit时:

whichFruit “banana”

这真的很糟糕,对吗?通过面对这一点,人们可能会认为问题只与更明确地将函数期望作为参数并尝试将代码更改为:

相关。
*** Exception: BogusPattern.hs:(11,16)-(13,34): Non-exhaustive patterns in case

尽管使用类型同义词肯定会增加代码的可读性,但是一旦没有任何东西阻止调用者调用type FruitName = String whichFruit :: FruitName -> Fruit whichFruit f = case f of "apple" -> Apple "orange" -> Orange 传递类似" foo"之类的内容,它肯定无法解决问题。因此,在Haskell中,我们努力编写 total (为其域的所有输入定义)的函数,从而产生更强大的代码。只需在GHCi中设置标志whichFruit并加载模块,我们就可以看到编译器甚至警告我们这个缺陷:

-Wall

好的......那我们该如何解决这个问题呢?更简单的方法是添加默认情况:

λ: :set -Wall
λ: :l BogusPattern
[1 of 1] Compiling BogusPattern    ( BogusPattern.hs, interpreted )
...

BogusPattern.hs:11:16: Warning:
    Pattern match(es) are non-exhaustive
    In a case alternative:
        Patterns not matched:
            []
            (GHC.Types.C# #x) : _ with #x `notElem` ['a', 'o']
            [GHC.Types.C# 'a']
            (GHC.Types.C# 'a') : ((GHC.Types.C# #x) : _)
            with
            #x `notElem` ['p']
            ...
Ok, modules loaded: BogusPattern.

虽然它有效但结果可能不合适(因为我们当然不希望whichFruit' :: FruitName -> Fruit whichFruit' f = case f of "apple" -> Apple _ -> Orange 返回whichFruit' "banana")。作为替代解决方案,可以向Orange数据类型添加新的值构造函数,以便(错误地)表示无效的水果并在默认情况下返回该水果(Fruit):

_

由于多种原因,此解决方案并不好。从语义上讲,我们希望值构造函数构建与相应类型构造函数的类型匹配的值。在我们的例子中,我们希望data Fruit = Apple | Orange | InvalidFruit deriving (Show) AppleOrange s(这是有道理的),但Fruit应该是什么意思?而且,事实证明,在Haskell中我们有更好的方法来表示失败的可能性,也就是说,为了将失败的概念嵌入InvalidFruit,我们可以简单地使用例如{{}来重写它。 1}}类型,如:

whichFruit'

这个解决方案看起来好多了,在我看来,这是最好的解决方案。可能发现的唯一缺点是它增加了额外的开销和#34;并使调用函数复杂化,该函数必须处理具有可能失败的上下文(Maybe)的值,而不仅仅是值whichFruit'' :: FruitName -> Maybe Fruit whichFruit'' f = case f of "apple" -> Just Apple "orange" -> Just Orange _ -> Nothing )。作为最后一点,我告诉你使用这样的解决方案(或相关的解决方案,即:Maybe Fruit)是完全值得的,随着你对Haskell的更多经验,处理那些更复杂的"类型变得如此自然,以至于你甚至都不会注意到。