2011-10-11 60 views
6

通過Comparing list length用箭頭

如果我想找到一個列表的列表最長的名單啓發比較列表長度,最簡單的方法可能是:

longestList :: [[a]] -> [a] 
longestList = maximumBy (comparing length) 

一個更有效的方法是以預先計算長度:

longest :: [[a]] -> [a] 
longest xss = snd $ maximumBy (comparing fst) [(length xs, xs) | xs <- xss] 

現在,我想進一步。對於正常情況可能效率不高,但是您能否使用箭頭解決這個問題?我的想法基本上是,同時列出所有列表,並繼續步進,直到超出除最長列表之外的每個列表的長度。

longest [[1],[1],[1..2^1000],[1],[1]] 

在前述(很做作)例如,您可能只需要採取通過每個列表中的兩個步驟,以確定該列表[1..2^1000]是最長的,從來沒有需要來確定所述列表的整個長度。我說得對,這可以用箭頭來完成嗎?如果是這樣,那麼怎麼樣?如果沒有,那麼爲什麼不呢,這種方法怎麼可能被實施呢?

+1

我沒有看到與箭頭的任何連接。 – luqui

+0

@luqui關於[使用箭頭](http://en.wikibooks.org/wiki/Haskell/Understanding_arrows#Using_arrows)上Haskell Wikibook的一部分似乎表示,箭頭對於以類似於我的方式進行解析很有用提出瞭解決這個問題的方法(查看每個列表的第一個元素,然後是第二個元素等)[Stephen's Arrow Tutorial](http://en.wikibooks。org/wiki/Haskell/StephensArrowTutorial)給了我相同的感覺:箭頭可以用來挖掘這些列表並存儲信息。 –

+0

我已經接受了一個答案,但是如果有人能用箭頭來回答答案,或者徹底解釋爲什麼箭頭不相關,那麼我肯定會接受這個答案。 –

回答

2

想法關於這一點,還有一個更簡單的解決方案,它具有相同的性能特徵。我們可以使用具有延遲長度比較功能的maximumBy

compareLength [] [] = EQ 
compareLength _ [] = GT 
compareLength [] _ = LT 
compareLength (_:xs) (_:ys) = compareLength xs ys 

longest = maximumBy compareLength 
+0

+1這確實很優雅。但是,如果我沒有弄錯,它會在每次比較2個列表時重新遍歷列表,以查看哪個更長。 –

+0

@丹伯頓:是的,但只是兩個列表中較短的一個。所以這只是一個問題,哪種方法有最高的常數因素,我必須承認我沒有測試過。 – hammar

+0

或者,您可以使用二進制數的代數數據類型來表示列表的長度。通過這種方式,您只能得到一個列表長度的對數因子,以比較兩個列表的長度,並且仍然至少有一定程度的懶惰。 –

4

OK,因爲我寫的問題,我恍然大悟實現這個簡單的方法(不箭頭,噓!)

longest [] = error "it's ambiguous" 
longest [xs] = xs 
longest xss = longest . filter (not . null) . map (drop 1) $ xss 

除了這有一個問題......它滴的第一部分的名單,並沒有恢復它!

> take 3 $ longest [[1],[1],[1..2^1000],[1]] 
[2,3,4] 

需要更多的簿記:P

longest xs = longest' $ map (\x -> (x,x)) xs 

longest' [] = error "it's ambiguous" 
longest' [xs] = fst xs 
longest' xss = longest . filter (not . null . snd) . map (sndMap (drop 1)) $ xss 

sndMap f (x,y) = (x, f y) 

現在,它的工作原理。

> take 3 $ longest [[1],[1],[1..2^1000],[1]] 
[1,2,3] 

但沒有箭頭。 :(如果可以用箭頭來完成,然後希望這個答案可以給你什麼地方開始。

+0

另外,'errror'不明確''是處理相同長度列表的一種非常糟糕的方式。咩。這個問題是專門爲你有很多短名單和一個很長的名單而設計的。 –

3

這是最簡單的實現,我能想到的,沒有箭頭參與,雖然。

我把名單第一個元素是原始列表,第二個元素是剩餘尾部,如果我們只剩下一個列表,我們就完成了,否則我們嘗試把所有剩下的列表尾部,過濾掉那些空的列表。如果仍然存在,繼續前進,否則它們的長度都是相同的,我們會隨意選擇第一個。

longest [] = error "longest: empty list" 
longest xss = go [(xs, xs) | xs <- xss] 
    where go [(xs, _)] = xs 
     go xss | null xss' = fst . head $ xss 
       | otherwise = go xss' 
       where xss' = [(xs, ys) | (xs, (_:ys)) <- xss] 
+0

我_could_改變第二行爲'最長xss =去$ map(id &&& id)xss',但我想這不是你想的那種箭頭用法:) – hammar