簡體   English   中英

如果沒有我們在數據類型上定義Eq,Haskell如何進行模式匹配?

[英]How does Haskell do pattern matching without us defining an Eq on our data types?

我已經定義了一個二叉樹:

data Tree = Null | Node Tree Int Tree

並實現了一個函數,它將產生所有節點的值的總和:

sumOfValues :: Tree -> Int
sumOfValues Null = 0
sumOfValues (Node Null v Null) = v
sumOfValues (Node Null v t2) = v + (sumOfValues t2)
sumOfValues (Node t1 v Null) = v + (sumOfValues t1)
sumOfValues (Node t1 v t2) = v + (sumOfValues t1) + (sumOfValues t2)

它按預期工作。 我有想法也嘗試使用警衛來實現它:

sumOfValues2 :: Tree -> Int
sumOfValues2 Null = 0
sumOfValues2 (Node t1 v t2)
    | t1 == Null && t2 == Null  = v
    | t1 == Null            = v + (sumOfValues2 t2)
    |               t2 == Null  = v + (sumOfValues2 t1)
    |   otherwise       = v + (sumOfValues2 t1) + (sumOfValues2 t2)

但是這個沒有用,因為我沒有實現Eq ,我相信:

 No instance for (Eq Tree) arising from a use of `==' at zzz3.hs:13:3-12 Possible fix: add an instance declaration for (Eq Tree) In the first argument of `(&&)', namely `t1 == Null' In the expression: t1 == Null && t2 == Null In a stmt of a pattern guard for the definition of `sumOfValues2': t1 == Null && t2 == Null 

那么,必須要做的問題是Haskell如何在不知道何時傳遞的參數匹配的情況下進行模式匹配,而不依賴於Eq

編輯

你的論證似乎圍繞着這樣一個事實:Haskell確實沒有比較函數的參數,而是在“形式”和簽名類型上知道要匹配哪個子函數。 但是這個怎么樣?

f :: Int -> Int -> Int
f 1 _ = 666
f 2 _ = 777
f _ 1 = 888
f _ _ = 999

運行f 2 9 ,是否必須使用Eq才能知道哪一個子功能是正確的? 所有這些都是相同的(與我的Tree / Node / Null時的初始Tree示例相反)。 或者是Int的實際定義

data Int = -2^32 | -109212 ... | 0 | ... +2^32 

對於模式匹配,Haskell使用值的結構和使用的構造函數。 例如,如果您評估

sumOfValues (Node Null 5 (Node Null 10 Null))

它從上到下檢查模式:

  • 第一個, Null ,不匹配,因為它有不同的結構

  • 第二個(Node Null v Null)不匹配,因為最后一個組件Null具有與(Node Null 10 Null)不同的結構(模式匹配遞歸地進行)

  • 第三個匹配v綁定到5和t2綁定到(Node Null 10 Null)

Eq和它定義的==運算符是一個不相關的機制; 使Tree成為Eq的實例不會改變模式匹配的工作方式。

我認為你的想法有點落后:模式匹配是在Haskell中使用值的最基本方式; 除了像Int類的一些基本類型, Eq是使用模式匹配實現的,而不是相反。

模式匹配和數字

事實證明,數字文字是一種特殊情況。 根據Haskell報告( 鏈接 ):

如果v == k ,則將數字,字符或字符串文字模式k與值v匹配成功,其中==基於模式的類型重載。

Haskell知道使用什么類型的構造函數來構造特定的實例,這就是成功模式匹配所需的全部內容。

你缺少的是你假設Null是一些像C或Java那樣的常量值。 它不是 - 它是Tree類型的構造函數。

模式匹配反向構建。 您不是向構造函數提供兩個值,而是為您提供適當類型的值,而是為構造函數提供類型的值,並為您提供構成該類型的組成值。 語言知道區分聯合的哪個分支用於構造值,因此它可以匹配正確的模式。 因此,不需要相等,因為它只是構造函數和變量 - 沒有實際值進行比較(或者甚至必須進行評估)。

關於你對Ints的編輯:

是的,Int基本上是一個類型,在-2 | -1 | 0 | 1 | 2 | 3 | 4 …的行上有大量的構造函數 -2 | -1 | 0 | 1 | 2 | 3 | 4 … -2 | -1 | 0 | 1 | 2 | 3 | 4 … 它有點手工,但它在實踐中有效。

某些時候,當您不想使用Eq時,您需要模式匹配。 你可以定義

isEmpty :: Tree -> Bool
isEmpty Null = True
isEmpty _ = False

sumOfValues2 :: Tree -> Int
sumOfValues2 Null = 0
sumOfValues2 (Node t1 v t2)
    | isEmpty t1 && isEmpty t2 = v
    | isEmpty t1 = v + (sumOfValues2 t2)
    | isEmpty t2 = v + (sumOfValues2 t1)
    | otherwise = v + (sumOfValues2 t1) + (sumOfValues2 t2)

順便說一下,你不需要所有這些情況,只需這樣:

sumOfValues :: Tree -> Int
sumOfValues Null = 0
sumOfValues (Node t1 v t2) = v + (sumOfValues t1) + (sumOfValues t2)

您可以將數據構造函數視為標記。 要對Tree的值進行模式匹配,編譯后的代碼將提取標記,並且只知道要分派到哪個分支。 它並不關心真正的價值,所以你不需要Eq

模式匹配取決於語法。 例如,如果你編寫一個涉及構造函數的模式,那么構造函數的模式匹配就是你得到的。 如果你編寫一個涉及litteral表達式的模式(並且litteral浮點值或整數就是這樣),你可以使用你的類型可能提供的(==)運算符的任何重載定義來獲得一個相等的測試(來自Eq類)。

因此,如果你重載你的Fred數據類型,它是類Num並且有一個構造函數,也稱為Fred你可以通過檢查給定的Integer是否來定義與Integers的相等性。現在在那個奇怪的場景中,除非我忽略了某些東西,以下應該工作:

getFred :: Fred -- get a Fred
getFred = Fred -- invoke the Fred constructor

testFred :: Fred -> Bool -- tests if Fred's equality operator considers 1 equal to Fred
testFred 1 = True -- overloaded (==) tests against 1, so should be true
testFred _ = False -- shouldn't happen, then...

isFredOne = testFred $ getFred -- returns True

我通過說模式匹配是Haskell的核心來證明這種類型的東西--Eq類型類不是。

因此雖然存在類似於需要Eq的模式匹配的特征,但它們不是模式匹配並且可以在模式匹配之上實現。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM