2012-02-22 63 views
8
檢測底值

我寫這樣會將名單xs(init xs, last xs)像這樣一個Haskell函數:在Haskell

split xs = split' [] xs 
    where 
     split' acc (x:[]) = (reverse acc, x) 
     split' acc (x:xs) = split' (x:acc) xs 

由於空列表不能以這種方式被分割,沒有匹配的空的清單。但是,我並不想簡單地將error ...的功能。因此,我定義如下:

split [] = ([], undefined) 

由於懶惰的評價因此,我可以定義一個安全init它簡單的返回空列表爲空列表:

init' = fst . split 

有沒有一些方法,我怎麼能檢測如果我試圖訪問它,這樣

last' xs 
    | isUndefined (snd xs) = ... 
    | otherwise   = ... 

我不知道MaybeEither,而且未定義那些表達我想要的是更好的選擇。然而,我想知道是否有辦法檢測未定義的實際值,即捕獲錯誤(如捕獲異常)。

+3

簡單的答案是NO。無法檢測到未定義的。 (更復雜的答案是,你可以在IO momad中捕獲異常,但這不是你想要在這裏做的。) – augustss 2012-02-22 10:42:35

回答

10

因爲底部涵括未結束,該函數isUndefined必須解決停機問題,因此不能存在。

但請注意,即使存在,您仍然無法確定元組的第二個元素中的未定義值是否已通過split函數放入,或者列表的最後一個元素是否已定義。

+1

如果存在解決暫停問題的函數,我們可以顯示False,因此我們可以顯示所有內容,包括「我們可以判斷元組的第二個元素中的未定義值是通過分割函數放在那裏的,還是名單的最後一個元素已經不明確。「 – 2014-01-21 21:05:06

8

,直到它被評爲error功能沒有做任何事情,所以你可以這樣做:

split [] = ([], error "split: empty list") 

last' = snd . split 
+0

在我看來,這裏的最佳答案是因爲它回答了下一個問題:我在「其他」條款中加入了什麼?「。 – 2012-02-22 18:07:45

+0

我知道這一點,正如我所說的「感謝懶惰的評價」 - 把錯誤放在那裏就像放入undefined一樣。我想探測這件事,但據我所知,這是不可能的,因爲底層的語義禁止它。 – scravy 2012-02-25 12:32:38

+0

+1爲使用錯誤信息提供信息。 OP選擇「我不想'錯誤...'」並且使用'undefined'大多會在調試時導致挫敗感:「是的,我打了'undefined';但是* undefined'的用法*」。 (GHC)Haskell尤其糟糕,因爲懶惰和積極的優化可以使編譯的程序以意外的順序執行。堆棧軌跡最近被添加到GHC中,但它們仍然是選擇性的,並且是一種幻覺,而不是二進制文件如何實際執行的真實記錄。只要告訴我們最初出了什麼問題就容易多了! – Warbo 2017-11-15 14:47:19

11

undefined並不比使用error好。事實上,在undefined前奏是defined作爲

undefined = error "Prelude.undefined" 


現在,不能導致error的功能被稱爲「全功能」,即,它是適用於所有的輸入值。

你目前實施的split函數簽名

split :: [a] -> ([a], a) 

這是一個問題,因爲該類型簽名承諾,結果總是包含一個列表和一個元素,這顯然是不可能提供泛型類型的空列表。

Haskell解決此問題的規範方法是更改​​類型簽名以表示有時我們沒有第二項的有效值。

split :: [a] -> ([a], Maybe a) 

現在,你可以寫一個適當的實施在那裏你會得到一個空列表

split [] = ([], Nothing) 
split xs = split' [] xs 
    where 
     split' acc (x:[]) = (reverse acc, Just x) 
     split' acc (x:xs) = split' (x:acc) xs 

現在,您可以檢測的模式匹配

let (init', last') = split xs 
in case last' of 
    Nothing -> ... -- do something if we don't have a value 
    Just x -> ... -- do something with value x 
+3

有點諷刺的是,undefined被定義了;) – 2012-02-22 14:32:49

+1

@DanBurton嗯,如果這是你的幽默,請注意,也可以像這樣定義'undefined':'undefined = undefined'。它的行爲不同(這個定義在強制時不終止),但在語義上它具有相同的值(底部)。 – 2012-02-23 02:46:08

4

從缺失值情況下的情況下Haskell 2010 Language Report > Introduction # Values and Types

Haskell中的錯誤在語義上等同於⊥(「bottom」)。從技術上講,它們與無終點無法區分,因此該語言不包含檢測或根據錯誤採取行動的機制。

爲了清楚起見,undefined旨在是插入⊥到你的程序的方式,並考慮到(如上指出的)undefined在的error定義的,有,因此,「用於檢測沒有機制或根據undefined採取行動「。

+1

雖然從Haskell 2010的角度來看這是正確的,但值得注意的是,在GHC Haskell中,包括'error'在內的一些⊥值被實現爲異常,這可能會在IO單元中被捕獲。但是,這樣做通常表示設計不好,因爲有更好的方法來處理純代碼中的錯誤。 (當運行時可以檢測到非終止時,甚至會出現'NonTermination'異常,儘管由於暫停問題當然不可靠)。 – hammar 2012-02-22 15:00:14

1

雖然語義上來說Ingo的答案是正確的,但如果您使用的是GHC,則有一種使用一些「不安全」功能的方法,雖然不是很完美,但如果您通過IOa類型的計算它會返回True的異常,起作用。這有點欺騙:)。

import Control.Exception 
import System.IO.Unsafe 
import Unsafe.Coerce 

isUndefined :: a -> Bool 
isUndefined x = unsafePerformIO $ catch ((unsafeCoerce x :: IO()) >> return False) (const $ return True :: SomeException -> IO Bool) 

我知道這是可怕的,但沒有比它更有效。儘管如此,它不會檢測到非終止;)

+0

這可以通過用「(\ e - > return $ show e ==」Prelude.undefined「)之類的東西替換」return False「來改善,在實踐中幾乎100%的準確性(除非有人創建了一個例外,的顯示返回「Prelude.undefined」,但這將是愚蠢的......) – 2013-07-15 12:04:30