2017-09-13 12 views
1

リストが与えられた[1,2,31,23,22,1,43]高次関数を使用して要素[[1,2,31], [23,22,1], [43]]をグループ化しますか?増加/減少リストでは、要素は最初に要素が増加する順に、次に減少する順序になります。Haskell:増加する減少リストをグループ化するために使用するHigher Order関数

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

左単一の要素があるため、出力リスト[[a]]の各要素は、すべての単調増加または単調減少、上記の例のように、最初の要素[1,2,31]が全て増加して、第二[23,22,1]全て減少すべきであり、どちらかと前の状態はすべて減少していた、[43]はすべて増加すると分類できます。

groupByを使用するかどうかを判断できませんでした。どんなヒントもありがとう。

ありがとうございました。

[編集:。うまくいけば、さらに質問を明らかにするいくつかの詳細な説明を、追加]

+1

は、その機能が正確に何をしない:

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 

そうのように動作しますか?私はその例だけでは分かりません。 – AJFarmar

+0

は、 'groupIncDec'のように' '[[a]]、(a)) - >([[a]]、[a])の関数をラップすることができます。 'x'が' ']のときは、' 'x''が反対方向に向いていて、' 'xs''が' '' 'になっています。 –

+0

'groupBy'はメモリを持たないので使用できません。一度に2つの隣接する要素しか見ることができません。 –

答えて

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]をグループ化する方法に対応しています。dir'要素によってlstとグループ化して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 [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]]、すなわち蓄積部です。

ハスケルリストは(:の助けを借りて)ヘッド要素を処理するのが良いので、結果は後方に格納されます。したがって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
関連する問題