2014-09-11 74 views
6

我正在解決99個Haskell探測器。我已經成功解決了第21號問題,並且當我打開solution page時,提出了以下解決方案:一個有趣的模式

將給定位置的元素插入列表中。

insertAt :: a -> [a] -> Int -> [a] 
insertAt x xs (n+1) = let (ys,zs) = split xs n in ys++x:zs 

我找到的模式(n + 1)有趣,因爲它似乎是對的insertAt基於1的參數轉換成基於0的split參數(從前面練習它的功能,本質上是相同splitAt一種優雅的方式)。問題是,GHC並沒有發現這種模式是優雅的,其實它說:圖案

解析錯誤:N + 1

我不認爲這是誰寫的答案的傢伙是愚蠢的,我想知道這種模式在Haskell中是否合法,如果是的話,如何解決這個問題。

+0

儘管我認爲'n + k'-patterns確實具有一定的吸引力,但是在基於1和基於零的表示之間_converting_會使IMO從未成爲合適的用例。它們旨在遞歸地「解構」數字。 - FWIW,'insertAt'無論如何都應該是零,不是嗎? – leftaroundabout 2014-09-11 15:05:28

+0

@leftaroundabout,'insertAt'根據給定的例子應該是1:'insertAt'X'「abcd」2' - >'「aXbcd」'。 – Mark 2014-09-11 15:27:47

+0

是的,我的意思是,它應該被指定爲0,因爲所有的Haskell標準列表函數都是[[rightly](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831的.html))。 – leftaroundabout 2014-09-11 15:40:52

回答

12

我認爲這已經是removed from the language了,所以當99個Haskell Problems的作者寫了這個解決方案時可能會出現這種情況,但Haskell中已經不再存在這個問題了。

1

請注意,您現在可以use view patterns instead of n+1

例如:

{-# LANGUAGE ViewPatterns #-} 
module Temp where 
import Data.List (splitAt) 

split :: [a] -> Int -> ([a], [a]) 
split = flip splitAt 

insertAt :: a -> [a] -> Int -> [a] 
insertAt x xs (subtract 1 -> n) = let (ys,zs) = split xs n in ys++x:zs 
6

n+k模式的問題可以追溯到在Haskell一個設計決策,在模式中通過他們的名字的第一個字符的構造函數和變量之間的區別。如果你回到ML,常見的函數定義可能看起來像(使用Haskell語法)

map f nil = nil 
map f (x:xn) = f x : map f xn 

正如你所看到的,語法有一個在第一線的LHS fnil沒有什麼區別,但是他們有不同的角色; f是一個變量,需要綁定到map的第一個參數,而nil是需要與第二個參數進行匹配的構造函數。現在,ML通過查看周圍範圍中的每個變量來進行區分,並假設名稱在查找失敗時是變量。因此nil在查找失敗時被識別爲構造函數。但是,考慮當有一個在模式的錯字會發生什麼:

map f niil = nil 

(二i S IN niil)。 niil不是範圍內的構造函數名稱,因此它被視爲變量,並且定義被不正確地解釋爲默默解釋。

Haskell解決此問題的方法是要求構造函數名以大寫字母開頭,變量名以小寫字母開頭。而且,對於中綴運算符/構造函數,構造函數名稱必須以:開頭,而運算符名稱不得以:開頭。這也有助於解構綁定區分:

x:xn = ... 

顯然是一個解構綁定,因爲你不能定義一個名爲:功能,同時

n - m = ... 

顯然是一個函數的定義,因爲-可以」 t是構造函數名稱。

但允許n+k模式,如n+1,意味着+既是一個有效的函數名稱,也有一些像模式中的構造函數一樣工作。現在

n + 1 = ... 

再次含糊不清;它可能是名爲(+)的函數定義的一部分,或者它可能是解構模式匹配定義n。在Haskell 98,這種模糊性是通過聲明

n + 1 = ... 

函數定義,以及

(n + 1) = ... 

一個解構結合解決。但這顯然不是一個令人滿意的解決方案。

相關問題