さて、プログラム コードでこの関数を定義したことがわかりました。
st_zipOp :: (a -> a -> a) -> Stream a -> Stream a -> Stream a
st_zipOp f xs ys = St.foldr (\x r -> st_map (f x) r) xs ys
それはそれがするように見えることをします。Stream a
type の内部演算子を使用して、リストのような型であるtype の 2 つの要素を圧縮します (演算子を数回適用します、はい) a
。定義は非常に簡単です。
この方法で関数を定義したら、次の別のバージョンを試しました。
st_zipOp :: (a -> a -> a) -> Stream a -> Stream a -> Stream a
st_zipOp = St.foldr . (st_map .)
私の知る限り、これは上記とまったく同じ定義です。これは、以前の定義の無意味なバージョンにすぎません。
ただし、パフォーマンスの変化があるかどうかを確認したかったのですが、実際、ポイントフリー バージョンではプログラムの実行がわずかに悪化することがわかりました (メモリと時間の両方で)。
なぜこうなった?必要に応じて、この動作を再現するテスト プログラムを作成できます。
それが違いを生むかどうかをコンパイルし-O2
ています。
単純なテスト ケース
上記の動作を再現しようとして、次のコードを書きました。今回はリストを使用しましたが、パフォーマンスの変化は目立たなくなりましたが、まだ存在しています。これはコードです:
opEvery :: (a -> a -> a) -> [a] -> [a] -> [a]
opEvery f xs ys = foldr (\x r -> map (f x) r) xs ys
opEvery' :: (a -> a -> a) -> [a] -> [a] -> [a]
opEvery' = foldr . (map .)
main :: IO ()
main = print $ sum $ opEvery (+) [1..n] [1..n]
where
n :: Integer
n = 5000
opEvery
(明示的な引数のバージョン)を使用したプロファイリング結果:
total time = 2.91 secs (2906 ticks @ 1000 us, 1 processor)
total alloc = 1,300,813,124 bytes (excludes profiling overheads)
opEvery'
(ポイントフリーバージョン)を使用したプロファイリング結果:
total time = 3.24 secs (3242 ticks @ 1000 us, 1 processor)
total alloc = 1,300,933,160 bytes (excludes profiling overheads)
ただし、両方のバージョンが (あらゆる意味で) 同等であることを期待していました。