【问题标题】:How to use a global variable within a function in Haskell如何在 Haskell 的函数中使用全局变量
【发布时间】:2014-05-17 09:13:14
【问题描述】:

real world haskellchapter 3Incorrectly matching a variable部分,有一个例子如下:

-- 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 不被视为函数声明之前定义的全局变量。它们是局部变量。我认为如果没有与全局变量同名的局部变量,则全局变量不会隐藏。

【问题讨论】:

  • appleorange 不被视为 String 类型”这是不正确的,RWH 也没有声称它。全局和本地apple 都有String 类型,只是不是相同的字符串。

标签: haskell global-variables shadowing


【解决方案1】:

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

如果您想将f 的值与某个模式中的某个变量(如apple)的值相匹配,您需要使用模式保护和相等测试。例如

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

【讨论】:

    【解决方案2】:

    你找到了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
    

    【讨论】:

    • 您的意思是每当一个新的普通变量名出现在函数中时,就会创建一个新变量?如果我希望变量引用拥有相同名称的全局变量,应该怎么做?
    • @user811416:不不不。每当变量名出现以模式 时,就会创建一个新变量。 (事实上​​,模式是唯一可以出现新变量的地方。)
    • @user811416 更新了答案。 @leftaroundbot我认为OP的意思是每当一个新变量出现在函数定义中=的左侧时。但我不确定。
    • 我也不是,但我认为 OP 不太清楚模式代码和表达式代码之间的区别。使用合格的模块名称当然不是“解决这个问题”的合适方法。
    • @leftaroundabout 我同意命名可以做得更清楚。但是在上面的示例中(当两者具有相同的名称时)我看不到任何其他解决此问题的方法。
    【解决方案3】:

    没错。但是这里有一个局部变量apple与全局变量apple同名,即apple(呵呵)。

    关于模式的关键在于,它们不像寻找特定的区别特征那样比较变量,同时将所有其他信息重新打包到新变量中。 “显着特征”是构造函数匹配(始终为大写1),不会出现在case f of { apple -> ... } 中,因此all 信息在变量@ 中传递987654325@.

    一个更惯用的例子

    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 (:)(它们都以冒号开头,就像所有命名构造函数都以大写开头一样)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-16
      相关资源
      最近更新 更多