スプライトを後で表示されるビットマップにブリットする命令型レンダリングエンジンを想像してみてください。これは、ビットマップ内の個々のピクセルを効率的に変更する機能に大きく依存しています。副作用のない言語でそのようなことをどのように行うのでしょうか?まったく異なるデータ構造が必要だと思いますか?
3 に答える
変更可能な状態を使用する任意のアルゴリズムを、状態と一緒に「文字列化」するアルゴリズムに変換できます。Haskell はこれを行う方法を提供しているため、状態 Monad を使用した命令型プログラミングのように感じられます。
ただし、基本的なブリット操作はより機能的なスタイルで実行できるように思えます。基本的に、2 つのビットマップを組み合わせて、ピクセル単位の操作で新しいビットマップを生成します。それは私には非常に機能的に聞こえます。
高品質の命令型コードは、優れた関数型コードよりも高速であることがよくありますが、速度を少し犠牲にしても構わないと思っている場合は、通常、純粋な関数型スタイルで非常に優れたアーキテクチャを作成できます。
Haskell には副作用があり、適切な場合はいつでも使用する必要があります。内側のループにある (したがってパフォーマンスが重要な) 高速なブリット ルーチンは、確かにミューテーションが適切な場所の 1 つなので、使用してください。いくつかのオプションがあります。
- ST(U)Array または IO(U)Array を使用して、Haskell で独自のロールを作成します。推奨されません。
- C で独自のロールを作成し、FFI で呼び出します。推奨されません。
- Gtk や OpenGL など、この種の操作を既に提供している多くのグラフィック ツールキットの 1 つを使用してください。強くお勧めします。
楽しみ!
画像を表現する自然な機能的な方法は、インデックス関数を使用することです。
Image :: (Int,Int) -> Color
この表現では、ある画像から別の画像に領域をブリットすることは、
blit area a b = \(x,y) -> if (x,y) `isInsideOf` area then a (x,y) else b (x,y)
平行移動または別の変換が必要な場合は、座標に直接適用できます。
translate (dx,dy) image = \(x,y) -> b (x+dx,y+dy)
この表現により、イメージポイントを自然に操作できます。たとえば、長方形以外の領域を簡単に操作したり、通常の画像スケーリングアルゴリズムの一部ではなく、画像補間を個別の関数として作成したりするなどのトリックを実行できます。
quadraticInterpolation :: ((Int,Int) -> Color) -> ((Double,Double) -> Color)
複数の画像を1つにまとめて計算した場合など、パフォーマンスが低下する場合があります。これにより、連続する計算ごとに各ピクセルの一連のテストが行われます。ただし、メモ化を適用することで、関数表現を一時的に配列にレンダリングし、それをインデックス関数に戻すことができるため、後続の操作によるパフォーマンスへの影響を排除できます。
メモ化は、プロセスに並列処理を導入するためにも使用できることに注意してください。