2017-09-13 76 views
1

給定列表[1,2,31,23,22,1,43]想要使用高階函數來對元素[[1,2,31], [23,22,1], [43]]進行分組。增加/減少列表,其中元素最初是元素遞增順序,然後是遞減順序。Haskell:高階函數用於分組遞增遞減列表

groupIncDec :: Ord a => [a] -> [[a]] 

在輸出列表中的每個元素[[a]]應當是所有增加或在該示例中所有減小,像上面,第一元件[1,2,31]是所有增加,第二[23,22,1]所有減小,並且因爲有左單個元件和以前的狀態都在下降,[43]可以歸類爲全部增加。

想不通/如何使用groupBy。任何提示將不勝感激。

謝謝。

[編輯:增加了一些更多的描述,相信能進一步澄清問題]

+1

這是什麼功能做什麼呢?我無法單單從這個例子中看出來。 – AJFarmar

+0

好像''groupIncDec''可以包裝一對共同遞歸'([[a]],[a]) - >([[a]],[a])'函數,當'x'爲朝着相同的方向,當'x'朝相反的方向時互相調用,並且當'xs'是'[]'時基本出來。 –

+0

你不能使用'groupBy',因爲它沒有內存,它一次只能看兩個相鄰的元素。 –

回答

4

這裏的另一種方法。目前,假設列表中沒有像您的示例那樣的相鄰元素。如果列表lst與其自身的尾巴相比,元件逐元素:

> let lst = [1,2,31,23,22,1,43] 
> let dir = zipWith compare lst (tail lst) 
> dir 
[LT,LT,GT,GT,GT,LT] 

然後dir代表相鄰元件是否增加或減少。如果我們重複這個列表的頭:

> let dir' = head dir : dir 
> dir' 
[LT,LT,LT,GT,GT,GT,LT] 

然後dir'對應如何名單[1,2,31,23,22,1,43]應進行分組。通過與lst荏苒dir'並通過dir'元素分組,我們得到:

> import Data.Function -- for `on` 
> let groups = groupBy ((==) `on` fst) (zip dir' lst) 
> groups 
[[(LT,1),(LT,2),(LT,31)],[(GT,23),(GT,22),(GT,1)],[(LT,43)]] 

,我們可以過濾:

​​

因此,把所有這些組合起來,我們得到了一個更高階的解決方案:

groupIncDec' :: Ord a => [a] -> [[a]] 
groupIncDec' lst = 
    let dir = zipWith compare lst (tail lst) 
    in map (map snd) 
     $ groupBy ((==) `on` fst) 
     $ zip (head dir : dir) lst 

這並不與相鄰的重複元素列表正常工作,但我們可以採取這樣的名單:

lst' = [1,2,31,31,23,22,22,1,43] 

和組它:

group lst' = [[1],[2],[31,31],[23],[22,22],[1],[43]] 

,然後選擇「取消組合」它再次得到最終結果之前有效地應用上述算法。這看起來是這樣的:

groupIncDec :: Ord a => [a] -> [[a]] 
groupIncDec xs = 
    let ys = group xs 
     dir = zipWith (comparing head) ys (tail ys) 
    in map (concatMap snd) 
     $ groupBy ((==) `on` fst) 
     $ zip (head dir : dir) ys 

其工作原理是這樣:

> groupIncDec [1,2,31,23,22,1,43] 
[[1,2,31],[23,22,1],[43]] 
> groupIncDec [1,2,31,31,23,22,22,1,43] 
[[1,2,31,31],[23,22,22,1],[43]] 
2

groupBy平等,不是一般的二元關係意味着基團。例如:

Prelude Data.List> groupBy (<) [1,3,2] 
[[1,3,2]] 

它是第一元件1的跨距(如1<31<2)。


我想出了一個foldl的解決方案,但這不是一個很乾淨的解決方案。

groupIncDec :: Ord a => [a] -> [[a]] 
groupIncDec = map reverse . reverse . foldl f [] 
    where f []     x = [[x]] 
     f ([y]:ys)   x = [x,y]:ys 
     f ([email protected](y1:y2:_):yss) x = if x < y1 && y1 < y2 || x > y1 && y1 > y2 
           then (x:ys):yss 
           else [x]:ys:yss 

f[[a]] -> a -> [[a]]類型,即累積部分。

因爲Haskell List很擅長處理頭元素(在:的幫助下),所以結果被存儲在後面。因此需要map reverse . reverse


  1. http://hackage.haskell.org/package/base-4.10.0.0/docs/Data-List.html#v:groupBy
  2. http://hackage.haskell.org/package/base-4.10.0.0/docs/Data-List.html#v:foldl