2008年6月28日土曜日

Haskell でリストの要素を join - List.Data の intersperse, intercalate

Haskell で Python の String#join, Ruby の Array#join に相当するものは何だろう?

Prelude, Data.List を眺めてみるが、どこだろうか ... (@_@;)

 

自分で実装

とりえあえず、自分で書いてみる。 まず使い方を考えた場合、こんな感じ。

join ", " ["aaa", "bbb", "ccc"]

文字列と文字列のリストに join を適用すると、連結された結果の文字が返されると考え、

join :: String -> [String] -> String
join _ [] = []
join sep ss@(x:xs) = x ++ case length ss of {1 -> ""; otherwise -> sep} ++ join sep xs

ワイルドカード(ふつうのHaskellプログラミング, p170)、アズパターン(同, p173)、case 式(同, p177) の練習になったが、なんともぎこちない感じが。 ^^;

少し改良してみた。 case 式を使うのをやめて、リストパターン(同, p172)で。

join :: String -> [String] -> String
join sep [] = []
join sep [x]  = x
join sep (x:xs) = x ++ sep ++ join sep xs

最初、リストパターンについてわかっていなくて、次のように書いていた。

join sep [x]  |  length x == 1 = x

冗長だ ... (@_@;) これにずっと気がつかず、意味のないガードを。 ^^;

ちなみに、[], (x:xs) はデータコンストラクタによるパターンマッチ。(同上, p173) うーん、ややこしい。(+_+) パターンマッチによるフィールドへのアクセス(同上, p226)で書かれていたこと同じものに分類される。うーむ... CCC

 

Data.List

BBL::Wiki - Haskell::覚え書き によると、

Rubyでよく使うArray#joinはList.intersperse::a->[a]->[a]を利用すれば似たようなのが書ける。

Data.List の intersperse を見てみると、

The intersperse function takes an element and a list and `intersperses' that element between the elements of the list. For example,

 intersperse ',' "abcde" == "a,b,c,d,e"

ところで、この語の意味は Yahoo!辞書 – intersperse には、

1 …を間隔をおいて配置する, ばらまく, ちりばめる, 点在させる
flowers interspersed among the grass
草むらに点々と咲く花.

2 …に(…を)点在させる, …に(…で)変化をもたせる((with ...))
intersperse one's talk with good humor
おもしろいユーモアをはさんで話に変化をつける.

あるものの中に挟み込まさせるというイメージか。 join のように「つなげる」とは対照的な語が使われているところがおもしろい。

 

intersperse の実装を真似る

Haskell Code by HsColour にあるソースコードを見てみると、

intersperse  :: a -> [a] -> [a]
intersperse _   []      = []
intersperse _   [x]     = [x]
intersperse sep (x:xs)  = x : sep : intersperse sep xs

あ~、綺麗。 (@_@) これを真似して先ほど書いたコードを修正してみよう。

join :: [a] -> [[a]] -> [a]
join _ [] = []
join _ [x]  = x
join sep (x:xs) = x ++ sep ++ join sep xs

 

intercalate

intersperse のすぐに下に intercalate なる関数があった。

Yahoo!辞書 – intercalate によると

1 〈余分の日・月などを〉暦に挿入する, 閏(うるう)にする.

2 …を挿入する.

実装を見ると、intersperse を concat している。

intercalate :: [a] -> [[a]] -> [a]
intercalate xs xss = concat (intersperse xs xss)

map に対する concatMap のようなものか。

 

使い方のまとめ

import Data.List
main = do putStrLn $ intersperse ',' "abcd"
          putStrLn $ concat $ intersperse ", " ["aaa", "bbb", "ccc"]
          putStrLn $ intercalate ", " ["aaa", "bbb", "ccc"]