2011-09-27 17 views

回答

13

是的,您可以使用ViewPatterns擴展名。

Prelude> :set -XViewPatterns 
Prelude> let f (last -> x) = x*2 
Prelude> f [1, 2, 3] 
6 

注意,這種模式將始終成功,雖然如此,你可能會想增加一個模式的情況下列表是空的,否則last會拋出異常。

Prelude> f [] 
*** Exception: Prelude.last: empty list 

另請注意,這只是語法糖。與正常模式匹配不同,這是O(n),因爲您仍在訪問單向鏈接列表的最後一個元素。如果您需要更高效的訪問權限,請考慮使用不同的數據結構,例如Data.Sequence,它提供對兩端的訪問權限O(1)

+0

last在空列表上拋出異常。我期待擺脫最後,頭,fst,snd。不能很好地處理空列表的函數。模式匹配,例如,讓(x:xs)=「abcdefg」是我在我的問題中提到的。希望有一個類似的方法來獲得最後一個元素,而不使用不安全的前奏功能。 –

+2

@MichaelLitchard:嗯,你可以例如定義一個'safeLast'函數,它返回一個'Maybe',然後你可以進行模式匹配,或者也許只是反轉列表,然後使用正常的模式匹配。 – hammar

+2

,因爲它總是成功,它根本不是什麼「模式匹配」。 'f =(* 2)。最後'更好。 – u0b34a0f6ae

11

您可以使用ViewPatterns做圖案在列表的末尾匹配,所以讓我們做

{-# LANGUAGE ViewPatterns #-} 

,並使用reverse作爲viewFunction,因爲它總是成功,因此,例如

printLast :: Show a => IO() 
printLast (reverse -> (x:_)) = print x 
printLast _ = putStrLn "Sorry, there wasn't a last element to print." 

這是安全的,只要你覆蓋所有的可能性,它不會拋出任何異常。 (你可以重寫它返回一個Maybe,例如。)

語法

mainFunction (viewFunction -> pattern) = resultExpression

是語法糖

mainFunction x = case viewFunction x of pattern -> resultExpression

這樣可以真正看到它反轉名單然後模式匹配,但它感覺更好。 viewFunction只是你喜歡的任何功能。 (一個擴展的目的之一是讓人們能夠乾淨,容易使用存取模式匹配功能 所以他們沒有使用他們的數據類型的基礎結構時 其上定義的功能。)

4

其他答案解釋了基於ViewPatterns的解決方案。如果你想使之更加的模式匹配狀,你可以包成PatternSynonym

tailLast :: [a] -> Maybe ([a], a) 
tailLast [email protected](_:_) = Just (init xs, last xs) 
tailLast _ = Nothing 

pattern Split x1 xs xn = x1 : (tailLast -> Just (xs, xn)) 

,然後寫你的功能例如

foo :: [a] -> (a, [a], a) 
foo (Split head mid last) = (head, mid, last) 
foo _ = error "foo: empty list" 
2

這是我的Haskell編程的第一天,我也遇到了同樣的問題,但我無法解析使用某種外部神器在以前的解決方案建議。

我對Haskell的感覺是,如果核心語言沒有針對您的問題的解決方案,那麼解決方案就是轉換問題直到它適用於該語言。

在這種情況下,轉換問題意味着將尾部問題轉換爲頭部問題,這似乎是模式匹配中唯一受支持的操作。它可以很容易地使用列表反轉來做到這一點,然後使用head元素在反轉列表上工作,就像在原始列表中使用tail元素一樣,最後,如果需要,將結果還原爲初始順序(例如if這是一個列表)。例如,給定一個整數列表(例如[1,2,3,4,5,6]),假設我們要構建這個列表,其中原始列表的每個第二個元素從結尾開始被其雙倍數(取自this excellent introduction to HaskellHomework1的鍛鍊)取代:[2,2,6,4,10,6]。

然後我們可以使用以下命令:

revert :: [Integer] -> [Integer] 
revert []  = [] 
revert (x:[]) = [x] 
revert (x:xs) = (revert xs) ++ [x] 

doubleSecond :: [Integer] -> [Integer] 
doubleSecond []  = [] 
doubleSecond (x:[]) = [x] 
doubleSecond (x:y:xs) = (x:2*y : (doubleSecond xs)) 

doubleBeforeLast :: [Integer] -> [Integer] 
doubleBeforeLast l = (revert (doubleSecond (revert l))) 

main = putStrLn (show (doubleBeforeLast [1,2,3,4,5,6,7,8,9])) 

這顯然比以前的解決方案更長的時間,但感覺更哈斯克爾十歲上下給我。

+1

歡迎來到StackOverflow,並祝賀這麼好的開始!反轉列表以訪問遠端實際上是一個非常Haskellish的解決方案(雖然效率相當低,但如果您需要多次執行,使用'last'可能會更糟......如果性能很關鍵並且需要訪問最後一個元素,那麼列表不是合適的類型)。 – leftaroundabout

+0

謝謝。在我的新手水平上,我確實更感興趣的是發現Haskell編程習慣用法,而不是對它們的性能影響進行測試。我同意,嘗試以相反順序使用簡單鏈接列表是一種更糟的情況,我想這實際上是Haskell首先不提供尾部模式匹配的原因。 (Haskell列表是使用簡單鏈接列表實現的,對嗎?) – OlivierD

+1

正確。 Haskell列表非常適合堆棧或無限流,但它們對於隨機訪問來說真的很糟糕,訪問遠端並不比訪問中間的任何元素更好。 – leftaroundabout

相關問題