評価を遅延する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が定義されています。これについては,次回以降で詳しく説明します。