如何在Haskell中的函数内使用全局变量

时间:2014-05-17 09:13:15

标签: haskell global-variables shadowing

真实世界haskell chapter 3部分与变量错误匹配的部分中,有一个示例如下:

-- file: ch03/BogusPattern.hs
data Fruit = Apple | Orange

apple = "apple"

orange = "orange"        

whichFruit :: String -> Fruit

whichFruit f = case f of
                 apple  -> Apple
                 orange -> Orange

解释说明在case f of部分,appleorange不被视为函数声明之前定义的全局变量。它们是局部变量。我想如果没有局部变量拥有与全局变量相同的名称,则不会隐藏全局变量。

3 个答案:

答案 0 :(得分:4)

这里的主要内容是模式匹配中的变量总是引入新变量而不是引用现有变量。您的问题与全局变量和局部变量无关。

如果要在模式中将f的值与某个变量(如apple的值)匹配,则需要使用模式保护和相等性测试。 E.g。

whichFruit f
    | f == apple  = Apple
    | f == orange = Orange

答案 1 :(得分:1)

右。但是这里有一个局部变量apple拥有与全局变量apple相同的名称,即apple(duh)。

关于模式的关键之处在于它们并没有像寻找特定的区别特征那样比较变量,同时将所有其他信息重新打包成新变量。 “区别特征”是构造函数匹配(总是大写 1 ),它们不会出现在case f of { apple -> ... }中,所以只需 all 就可以在变量apple

更惯用的例子

data Vegetable = Tomato | Potato

data Edible = Fruit Fruit | Vegetable Vegetable

如果您希望函数采用Edible参数,则解构类型通常很有用。这可能是这样的:

canJuice :: Edible -> Bool
canJuice (Fruit Apple) = False
canJuice (Fruit Orange) = True
canJuice (Vegetable Tomato) = True
canJuice (Vegetable Potato) = False

现在,对于更复杂的数据,编写这么多canJuice子句很尴尬。另一种方法是首先只匹配最外面的构造函数,然后将其他工作委托给别处:

canJuice :: Edible -> Bool
canJuice (Fruit fruit) = fruitJuicy fruit
canJuice (Vegetable veg) = vegetableJuicy veg

vegetableJuicy :: Vegetable -> Bool
vegetableJuicy Tomato = True
vegetableJuicy Potato = False

为了实现这一点,我们需要Haskell的特性,即将模式中出现的任何小写名称视为一个新变量,它接受模式匹配的“洞”中的值。


1 还有中缀构造函数,最着名的是list-cons (:)(它们都以冒号开头,就像所有命名的构造函数都以大写字母开头一样)。

答案 2 :(得分:1)

你已经遇到了irrefutable patterns。正如本书所提到的,普通变量名称和通配符_是无可辩驳模式的示例。另一个将更清楚地展示irrefutable patterns的例子:

data Fruit = Apple | Orange deriving (Show)

patternMatch f = case f of
  something -> Apple

现在上面的程序有一个警告。在ghci:

ghci> patternMatch 2
Apple
ghci> patternMatch "hi"
Apple

所以基本上变量something是一个与任何东西匹配的无可辩驳的模式。

现在,回到你的例子:

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

此处变量appleorange是无可辩驳的模式。它们不引用您已创建的全局函数。实际上,您可以删除appleorange的全局定义并编译它们以获得一个想法。无论你给出什么输入,你都会得到Apple作为上述代码的答案(因为它是一个无可辩驳的模式):

ghci > whichFruit "apple"
Apple
ghci > whichFruit "orange"
Apple
ghci > whichFruit "pineApple"
Apple
  

如何在Haskell中的函数中使用全局变量?

实际上这很容易。只需在函数定义中使用它们即可。

data Fruit = Apple | Orange deriving (Show)

apple = "apple"
orange = "orange"

giveFruit :: Fruit -> String
giveFruit Apple = apple
giveFruit Orange = orange

在ghci:

ghci> giveFruit Apple
"apple"
ghci> giveFruit Orange
"orange"

在函数定义中使用变量是直截了当的。


  

如果我希望变量引用全局,应该怎么做   拥有相同名称的变量?

一种方法是使用整个模块名称来引用它。例如:

module Fruit where

data Fruit = Apple | Orange deriving (Show)

apple = "apple"
orange = "orange"

giveFruit2 :: Fruit -> String
giveFruit2 apple = Fruit.apple