2014-09-01 50 views
1
-- 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 

在這段代碼爲什麼我需要爲什麼我需要在我的Haskell代碼中定義這兩個變量?

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匹配到兩個字符串中,並返回果實類型的基礎?

謝謝。

+0

你打電話給'whichFruit'? – 2014-09-01 04:40:54

回答

5

你不需要蘋果和橙色變量。哪個水果需要一個字符串。在您第一次嘗試時,您可以撥打whichFruit "orange",它將匹配第二種情況(「橙色」)。在你的第二個例子中,你需要使用whichFruit "f2"來完成同樣的事情。

您也可以定義x = "orange"並致電whichFruit x以獲得橙色。

0

拉斐爾的回答是完全正確的,但我想添加一些我認爲相關的觀點。

首先,更概念的東西。調用orangebanana變量並不完全正確。相反,它們是常量函數或僅僅是String類型的常量。這是因爲你實際上不能改變他們的價值觀(因此,我們稱之爲不變性)。關於你的需求問題,我不確定,但可能這個例子的想法很簡單,就是定義兩個不同的「成果」,這樣人們就可以在GHCi(或其他任何地方)通過applewhichFruit以及orange而不是字符串文字「apple」和「orange」。這就是說,儘管是一個簡單的例子來說明非常基本的功能,但值得一提的是,不建議(函數)定義函數whichFruit的方式。此功能僅適用於兩種不同的輸入:「蘋果」和「橙色」。除此之外的任何內容都會導致whichFruit意外失敗。由於whichFruit僅爲其域的子集(String)定義,因此稱爲部分函數。換句話說,類型簽名告訴我們whichFruit是一個函數,期望String並返回一個Fruit,但是當它被稱爲「香蕉」時,它會給出錯誤。想象一下,有人導入你的模塊(它定義了whichFruit)並開始使用它。在瀏覽GHCi的功能時,他發現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 

雖然使用一種類型的同義詞肯定會增加代碼的可讀性,它肯定不能解決問題,一旦沒有任何東西阻止調用者傳遞whichFruit傳遞類似「foo」的東西。正因爲如此,在Haskell中,我們努力編寫總計(爲其域的所有輸入定義)的功能,從而產生更健壯的代碼。通過簡單的設置標誌-Wall在GHCI和加載模塊,我們可以看到,即使編譯器警告我們有關的缺陷:

λ: :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)。作爲一種替代解決方案,人們可​​以以(MIS)添加一個新的價值構造的Fruit數據類型來表示一個無效的水果和返回,在默認情況下(_):

data Fruit = Apple | Orange | InvalidFruit 
    deriving (Show) 

該解決方案是不是好原因有很多。從語義上講,我們預計值構造函數可以構建與相應的類型構造函數的類型相匹配的值。在我們的例子中,我們預計AppleOrangeFruit s(這很有意義),但InvalidFruit應該表示什麼意思?此外,事實證明,在Haskell中,我們有更好的方式來表示失敗的可能性,也就是說,爲了將失敗的概念嵌入到whichFruit'中,我們可以簡單地使用例如Maybe類型來重寫它,如下所示:

whichFruit'' :: FruitName -> Maybe Fruit 
whichFruit'' f = case f of 
      "apple" -> Just Apple 
      "orange" -> Just Orange 
      _  -> Nothing 

這個解決方案看起來好多了,在我看來,它是最優的。唯一的缺點是它增加了「額外的開銷」並使調用者函數有點複雜化,調用者函數必須處理帶有可能失敗的附加上下文(Maybe Fruit)而不是僅值(Fruit)的值。作爲最後一點,我告訴你,使用這樣的解決方案(或相關的解決方案,即:Either String Fruit)是完全值得的,而且隨着你對Haskell更有經驗,處理這些「更復雜」的類型變得非常自然,你不會甚至通知。

相關問題