zipを考える
Haskellのzip,zipWith関数はこんな感じで定義することができます.
zip :: [a] -> [b] -> [(a,b)] zip [] ys = [] zip xs [] = [] zip (x:xs) (y:ys) = (x,y):zip xs ys zipWith :: (a->a->a) -> [a] -> [a] -> [a] zipWith f [] ys = [] zipWith f xs [] = [] zipWith f (x:xs) (y:ys) = f x y :zipWith f xs ys
ライブラリのソースコードがどこにあるか分からないので(^^;,実際はどういう定義なのかは分かりません.処理系に組み込まれているということはないとは思うのですが.
相手にするリストの個数を増やしていって,zip3/zipWith3からzip7/zipWith7まであるようですが(4から7はData.Listモジュールを読み込むと使える),こういう風に具体的に数が決め打ちになっているのは,私はあんまり好きではないです.そこで
zipWithAny :: ([a] -> b) -> [[a]] -> [b]
なんていう型で
zipWithAny sum [ [1,2,3,4,5,...], [10,20,30,40,50,...], [100,200,300,400,500,...] ] = [111,222,333,444,555,...]
というような関数,つまり,
[ [a11,a12,a13,....],[a21,a22,a23,...],[a31,a32,a33,...],...,[an1,an2,an3,...],... ] -> [ (f [a11,a21,a31,...,an1, ...]), (f [a12,a22,a32,...,an2, ...]),... ]
となる関数を定義してみました.イメージは行列で各行に対して何か操作をして,ベクトルを返すというところです.
と変換して,各列にmapを適用するということです*1.
zipWithAny :: ([a] -> b) -> [[a]] -> [b] zipWithAny f ([]:xs) = [] zipWithAny f xs = (tupleToListWith . unzip) $ map headTail xs where headTail xs = (head xs, tail xs) tupleToListWith (a,bs) = (f a):zipWithAny f bs
この定義では,暗黙に第二引数のリストの各要素(これもリスト)が同じ要素数(無限も含みます)を持つことを仮定しています.同じであるので,最初の要素が空になればすべて空なので空リストを返すとして,これを終了条件としています.ghciで実行させると
> zipWithAny sum [ [1,2,3], [10,20,30], [100,200,300] ] [111,222,333] > zipWithAny product [ [1,2,3], [10,20,30], [100,200,300] ] [1000,8000,27000] > zipWithAny (flip (!!) 2 ) [ [1,2,3], [10,20,30], [100,200,300] ] [100,200,300]
となって期待した動作です.
追記その1(2007/07/11)
_さんからのコメントで「転置」の存在を知りました.
import List zipWithAny :: ([a] -> b) -> [[a]] -> [b] zipWithAny f = map f . transpse
ですっきりできますね.すっきりした上に,「暗黙の仮定」も緩和できます.どうもありがとうございました.下手を承知で晒している甲斐がありました.
追記その2(2007/07/11)
「Haskellでインデントは意味がある」・・わかってるつもりでもつい.
import List zipWithAny :: ([a] -> b) -> [[a]] -> [b] zipWithAny f = map f . transpose
こんな風にすると,エラーがでるんですよね。。結構真剣に悩みました(-_-;;;
*1:_さんのコメントより行列を前面にだして書いてみました.まさに転置で最初からこう書けば分かりやすかったです.