2011-08-18 34 views
11

我有這個功能在Haskell:爲什麼非詳盡的衛兵造成無可辯駁的模式匹配失敗?

test :: (Eq a) => a -> a -> Maybe a 
test a b 
    | a == b = Just a 
test _ _ = Nothing 

這是我當我試圖用不同的輸入功能:

ghci>test 3 4 
Nothing 
ghci>test 3 3 
Just 3 

根據真實世界哈斯克爾,第一模式是無可辯駁的。但似乎test 3 4不會失敗的第一種模式,並匹配第二種。我預計會出現某種錯誤 - 也許是「非窮舉的守衛」。那麼這裏究竟發生了什麼,有沒有辦法在發生這種意外情況時啓用編譯器警告?

回答

11

第一個模式確實是一個「顛撲不破模式」,但是這並不意味着它將始終選擇相應的功能右側。它仍然受制於可能會失敗的守衛,就像你在例子中那樣。

爲了確保涵蓋所有的情況,通常使用otherwise來建立一個總是成功的最終後衛。

test :: (Eq a) => a -> a -> Maybe a 
test a b 
    | a == b = Just a 
    | otherwise = Nothing 

請注意,關於otherwise沒有什麼魔術。它在前奏中定義爲otherwise = True。然而,在最後一種情況下使用otherwise是慣用的。

在一般情況下,編譯器會警告有關非顯式警衛是不可能的,因爲它會涉及解決暫停問題,但是存在諸如Catch之類的工具,它們試圖比編譯器在確定是否全部工作方面做得更好案件被覆蓋或不在一般情況下。

+1

所以,如果它是一個無可辯駁的模式,它如何不匹配?匹配是否取決於守衛的成功?它是否匹配,然後在守衛失敗後不匹配? –

+4

@Matt:模式確實匹配,並且任何由它綁定的變量都會被提供給後衛,然後可能會失敗。發生這種情況時,剩下的守衛將按順序嘗試。如果全部失敗,則嘗試下一個模式。如果沒有更多模式可供嘗試,則會發生非窮舉模式匹配錯誤。 – hammar

+4

在GHC'否則'* *是特殊的。如果您嘗試自己定義它,最終會得到非詳盡的匹配編譯時警告(當然,只要您啓用這些警告即可)。 – Rotsor

1

衛兵不是無可辯駁的。但是,是很常見的(和好)的做法,以增加最後一個後衛是趕上其他情況,所以你的函數變爲:

test :: (Eq a) => a -> a -> Maybe a 
test a b 
    | a == b = Just a 
    | True = Nothing 
6

如果你忽略了第二個子句,也就是說,如果你的最後一個匹配有一組守衛,其中最後一個不是平凡真實的,那麼編譯器應該警告你。

一般來說,測試警衛的完整性顯然是不可能的,因爲它會像解決暫停問題一樣困難。

答案馬特的評論:

看一下例子:

foo a b 
    | a <= b = True 
    | a > b = False 

一個人可以看到,無論後衛之一必須是真實的。但編譯器不知道a <= ba > b

現在看看另一個例子:

fermat a b c n 
    | a^n + b^n /= c^n = .... 
    | n < 0 = undefined 
    | n < 3 = .... 

爲了證明這組警衛齊全,編譯器必須證明費爾馬大定理。在編譯器中做這件事是不可能的。請記住,警衛的數量和複雜程度不受限制。編譯器必須是解決數學問題的一般解決方案,這是Haskell本身所陳述的問題。

更正式地,以最簡單的情況:

f x | p x = y 

編譯器必須證明,如果p x是不是底部,然後p xTrue所有可能的X。換句話說,無論x是或者評估爲True,它都必須證明p x是底部(不停止)。

+0

你能解釋一下爲什麼它不可能嗎?我熟悉暫停問題,但不明白爲什麼這顯然不可能。 –

+0

@Matt我已編輯我的帖子來回答你的問題。 – Ingo

+0

我喜歡使用[Fermat's Last Theorem](http://en.wikipedia.org/wiki/Fermat%27s_Last_Theorem)的例子,這對於外行來說是數學中最着名的問題之一,直到1995年才被證明(儘管Fermat聲稱有一個太大而不符合保證金的證據)。 – hammar