2016-08-19 308 views
0

我對這個問題經歷了一篇文章,但我不明白。有人能解釋嗎?列表形式的列表中的每個第n個元素

問:從第n個元素本身開始以列表的形式查找列表中的每個第n個元素。

everyNth :: Int -> [t] -> [t] 
everyNth elt = map snd . filter (\(lst,y) -> (mod lst elt) == 0) . zip [1..] 

此外,請解釋如何使用模式匹配來解決此問題。即使用

[]->[] 
+1

我們可以在這裏解釋很多東西,也許你想對你不明白的東西更精確一點?另外,它有助於你開始解釋你的理解...... – Alec

+0

我理解第一行,對於第二行,我認爲它採用elt,如果mod結果等於零,則將它從尾部映射到頭部,然後它將特定元素拉到列表中。 – Arushi

+0

下面是一個密切相關的解決方案:'nths n = catMaybes.zipWith($)(tail.cycle $ Just:replicate(n-1)(const Nothing))''。隨意比較和對比 - 然後告訴我們缺少什麼。如果這是一項家庭作業,請隨意使用它,以防止自己的危險。我甚至會拋出另一種相關方法的高爾夫版本。玩的開心。 '\ n-> concat.zipWith($)(cycle $([2..n] >> [const []])++ [(:[])])' – MarLinn

回答

3

它易於使用模式匹配「中,選擇每n個元素」爲n個特殊情況:

every2nd (first:second:rest) = second : every2nd rest 
every2nd _ = [] 
-- >>> every2nd [1..12] 
-- [2,4,6,8,10,12] 

every3rd (first:second:third:rest) = third : every3rd rest 
every3rd _ = [] 
-- >>> every3rd [1..13] 
-- [3,6,9,12] 

every4th (first:second:third:fourth:rest) = fourth : every4th rest 
every4th _ = [] 
-- >>> every4th [1..12] 
-- [4,8,12] 

對於一般的情況下,雖然,我們的運氣了,至少用那種特殊的方法。上述模式需要一定的長度才能成爲明確的模式。將所組成的功能你提到從想法開始,我們知道如何尋找的[1..]每一個第n個成員,即如果它是n的倍數

multiple n m = m `mod` n == 0 
-- >>> filter (multiple 3) [1..12] 
-- [3,6,9,12] 

所以,你正試圖解決明白拉鍊[1..]與列表

index xs = zip [1..] xs 
-- >>> index [1..5] 
-- [(1,1),(2,2),(3,3),(4,4),(5,5)] 
-- >>> index "hello" 
-- [(1,'h'),(2,'e'),(3,'l'),(4,'l'),(5,'o')] 

然後過濾掉那些只是其對第一個元素是n的倍數

every_nth_with_index n xs = filter (\(m,a) -> multiple n m) (index xs) 
-- >>> every_nth_with_index 3 [1..12] 
-- [(3,3),(6,6),(9,9),(12,12)] 
-- >>> every_nth_with_index 3 "stackoverflow.com" 
-- [(3,'a'),(6,'o'),(9,'r'),(12,'o'),(15,'c')] 

然後擺脫的附屬建築,留給我們只是每對的第二個元素:

every_nth n xs = map snd (every_nth_with_index n xs) 
-- >>> every_nth 3 [1..12] 
-- [3,6,9,12] 
-- >>> every_nth 3 "stackoverflow.com" 
-- "aoroc" 

Retracinging我們的腳步,我們看到,這是一樣的

everyNth elt = map snd . filter (\(lst,y) -> (mod lst elt) == 0) . zip [1..] 
2

隨着一點點的欺騙,你可以使用定義everyNth模式匹配。真的,正如邁克爾的回答中指出的那樣,我們正在抽象出難以進行模式匹配的部分。

everyNth n lst = e (shorten lst) 
       where shorten = drop (n-1) -- here's the cheat 
         e [] = [] 
         e (first:rest) = first : e (shorten rest) 
+0

作爲獎勵,這種方式是有效的! – dfeuer

+0

一個抱怨:​​模式匹配在這裏永遠不會失敗。看到我的答案。 – dfeuer

+0

這改善了組合方法的效率,至少在我檢查的構圖中...'everyNth n = map snd。過濾器fst。 zip(circ n)where circ n = drop 1 $ cycle(True:replicate(n-1)False)' – Michael

2

臭名昭着的扇子再次襲擊。

everyNth n xs = foldr go (`seq` []) xs n where 
    go x r 0 = x : r (n - 1) 
    go _ r k = r (k - 1) 

這是非常相似的,但chepner's approach它集成滴入遞歸。重寫沒有摺疊,它是純粹的模式匹配:

everyNth n = go n where 
    go k [] = k `seq` [] 
    go 0 (x : xs) = x : go (n - 1) xs 
    go k (_ : xs) = go (k - 1) xs 
+0

第一場比賽是否等同於'go!k [] = []'? – chepner

+0

關閉主題(沒有模式匹配)我認爲以下內容可以很好地讀出'everyNth n =地圖頭。 takeWhile(非null)。 iterate(drop n)' – Cirdec

+0

@Cirdec,我不太在乎不必要的部分函數,​​而且我是列表融合的粉絲。我不知道是否有一種特別漂亮的方式來融合。如果我們把'takeWhileJust ::(a - > Maybe b) - > [a] - > [b]'with'takeWhileJust f(x:xs)|只要y < - f x = y:takeWhileJust f xs; takeWhileJust _ _ = []'。 – dfeuer

1

如果你以前從未見過Haskell,那麼這需要一點解釋。

everyNth :: Int -> [t] -> [t] 
everyNth elt = map snd . filter (\(lst,y) -> (mod lst elt) == 0) . zip [1..] 

首先,請注意該類型有兩個參數,但定義只有一個。這是因爲everyNth返回的值實際上是另一個函數。 elt是Int,並且第二行中的表達式創建了執行該作業的新函數。

二,注意「。」運營商。這是一個連接兩個功能的運算符。它的定義是這樣的:

(f . g) x = f (g x) 

這裏是第二個參數定義的等價版本作出了明確:

everyNth elt xs = map snd (filter (\(lst y) -> (mod lst elt) == 0) (zip xs)) 

當你看到通過鏈接鏈一堆的功能「」您需要從右向左閱讀它。在我的第二個版本中,請注意支架嵌套。 zip [1..] xs是最內在的表達,所以它首先得到評估。它將["foo", "bar"]這樣的列表變成[(1, "foo"),(2, "bar")]。然後對其進行過濾,以找到數量爲elt的倍數的條目。最後,map snd將數字取消以僅返回所需的條目。

相關問題