Ninety-Nine Haskell Problems 9

1to10 11to20 21to30 31to40 51to60 61to70 71to80 81to90 91to99 all
Ninety-Nine Haskell Problems


九問目.

9 Problem 9

(**) Pack consecutive duplicates of list elements into sublists. If a list contains repeated elements
 they should be placed in separate sublists.

Example:
* (pack '(a a a a b c c a a d e e e e))
((A A A A) (B) (C C) (A A) (D) (E E E E))
<example in lisp>

Example in Haskell:
*Main> pack ['a', 'a', 'a', 'a', 'b', 'c', 'c', 'a', 'a', 'd', 'e', 'e', 'e', 'e']
["aaaa","b","cc","aa","d","eeee"]
http://www.haskell.org/haskellwiki/99_questions/1_to_10#Problem_9


さて,実は一日考えてもわからなかったので,即座に解答.

Solution:

pack (x:xs) = let (first,rest) = span (==x) xs
               in (x:first) : pack rest
pack [] = []

A more verbose solution is

pack :: Eq a => [a] -> [[a]]
pack [] = []
pack (x:xs) = (x:first) : pack rest
         where
           getReps [] = ([], [])
           getReps (y:ys)
                   | y == x = let (f,r) = getReps ys in (y:f, r)
                   | otherwise = ([], (y:ys))
           (first,rest) = getReps xs

This is implemented as group in Data.List. 


うーん,悔しいけど仕方ない.解読解読.
えー,まずletってなんだ?
どうやら,whereみたいに関数内で関数を定義するみたいだけど,それだけじゃよくわからんので,先にspan.

-- | 'span', applied to a predicate @p@ and a list @xs@, returns a tuple where
-- first element is longest prefix (possibly empty) of @xs@ of elements that
-- satisfy @p@ and second element is the remainder of the list:
-- 
-- > span (< 3) [1,2,3,4,1,2,3,4] == ([1,2],[3,4,1,2,3,4])
-- > span (< 9) [1,2,3] == ([1,2,3],[])
-- > span (< 0) [1,2,3] == ([],[1,2,3])
-- 
-- 'span' @p xs@ is equivalent to @('takeWhile' p xs, 'dropWhile' p xs)@

span                    :: (a -> Bool) -> [a] -> ([a],[a])
span _ xs@[]            =  (xs, xs)
span p xs@(x:xs')
         | p x          =  let (ys,zs) = span p xs' in (x:ys,zs)
         | otherwise    =  ([],xs)
http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/GHC-List.html#span

とのこと.ってlet使われてるじゃねーか!
説明には,listの先頭からpを満たすものを集めてリストにし,残りもリストにして,二つのリストをtupleで返すっぽいことが書いてある.なんだよtupleって.まぁよくわからないけども,()で定義される構造っぽい.
で,このことをふまえて,ソースにあたる.
あんましよくわからないながら,じーっとみてるとなんとかわかってきた.

         | p x          =  let (ys,zs) = span p xs' in (x:ys,zs)

とりあえず,この行はxがpを満たせば実行(?)される.つまり,

let (ys,zs) = span p xs' in (x:ys,zs)

となるんだけど,右辺の(ys,zs)には,span p xs'の結果が入ると(spanの戻り値は([a],[a])だからね!).で,もともとのspanにはinのあとの,(x:ys,zs)が返される.
それはひとまず置いて,xがpを満たさなかった場合,([],xs)が返される.
さぁ,僕は頭の中で再生するのは無理だぜ!
ということで,手動実行.

  span (< 3) [1,2,3,4,1,2,3,4]
= let (ys1,zs1) = span (< 3) [2,3,4,1,2,3,4] in (1:ys1,zs1)
= let (ys1,zs1) = let (ys2,zs2) = span (< 3) [3,4,1,2,3,4] in (2:ys2, zs) in (1:ys1,zs1)
= let (ys1,zs1) = let (ys2,zs2) = ([], [3,4,1,2,3,4]) in (2:ys2, zs) in (1:ys1,zs1)
= let (ys1,zs1) =(2:[], [3,4,1,2,3,4]) in (1:ys1,zs1)
= let (ys1,zs1) =([2], [3,4,1,2,3,4]) in (1:ys1,zs1)
= (1:[2], [3,4,1,2,3,4])
= ([1,2], [3,4,1,2,3,4])

おお…….無理矢理やってみたけど,結構むずいぜ…….
ま,まぁとにかく,どんな挙動をするかはわかったからよし!次!
で,spanがわかったところで(ついでにletも若干わかった),packを再掲.

pack (x:xs) = let (first,rest) = span (==x) xs
               in (x:first) : pack rest
pack [] = []

ほうほう,なかなかわかってきたぜぇ!
spanの第一引数で(==x),で第二引数が,xs.つまり,xsの先頭にある,xと同じ値を持つものを,firstにぶち込む!restには残り!そして,元のpackには,firstの先頭にxを付け加え,さらに残りをpackしたものを結合が返される!((x:first)はそれ自体がリストの要素.(:)で結合されるからね).
うーん,こうみてみると,素晴らしい.こんな風にかけるのはHaskellの(ていうか,関数型言語の,か?)威力だなって感じがする.
はいはい,こういうことねって感じがしてきた.でも,自分でこういう風に書けるかっていうと,まだまだ無理だなぁ.いつかさらっとかけるといい.再帰が自然にでてくるから,こういう感覚を身に付けるためだけでも,Haskellの勉強には意味があるんじゃないかって気がしてきた.

1to10 11to20 21to30 31to40 51to60 61to70 71to80 81to90 91to99 all
Ninety-Nine Haskell Problems