評価を遅延するmap関数やzipWith関数
repaが提供する基本的な関数を見てみましょう。まずは,おなじみのmap関数とzipWith関数です。
Prelude Data.Array.Repa> :t Data.Array.Repa.map Data.Array.Repa.map :: (Shape sh, Repr r a) => (a -> b) -> Array r sh a -> Array D sh b Prelude Data.Array.Repa> :t Data.Array.Repa.zipWithData.Array.Repa.zipWith :: (Shape sh, Repr r2 b, Repr r1 a) => (a -> b -> c) -> Array r1 sh a -> Array r2 sh b -> Array D sh c
いずれも,引数として渡した配列と同じ型の配列ではなく,D型の配列を結果として返す関数として定義されています。
先に説明したように,D型の配列の内部表現は「配列に対して計算処理を行う関数」であり,実データではありません。
-- | Delayed arrays are represented as functions from the index to element value. -- -- Every time you index into a delayed array the element at that position -- is recomputed. data D data instance Array D sh e = ADelayed sh (sh -> e)
したがって,map関数やzipWith関数を配列に適用した時点では,実際には処理は行われません。D型の配列内の要素が必要になるまで,実際の処理は遅延されます。
map関数やzipWith関数で処理を遅延するのは,「配列の参照ごとに配列のコピーが作成されて,全体の処理速度が低下すること」を防ぐためです。今回は説明しませんが,repaが提供する行列演算では潜在的に配列のコピーを発生させる処理がいくつかあります。配列のコピーが発生すれば,それだけメモリーが消費され,全体の処理速度が低下します。repaではこの問題を解決するために,処理を意図的に遅延する関数としてmapやzipWithが定義されています。これについては,次回以降で詳しく説明します。