1

Haskell で 4 次の Runge-Kutta を実装しようとしていますが、このタスクに Haskell 型システムを使用するのは難しいと思います。誰か助けてくれませんか?次のコードで、'State' および 'DState' 型を型クラスに変更したいと考えています。

data State = State Double deriving (Show)
data DState = DState Double deriving (Show)

update :: State -> DState -> State
update (State x) (DState y) = State (x+y)

add :: DState -> DState -> DState
add (DState x) (DState y) = DState (x + y)

scale :: Double -> DState -> DState
scale h (DState x) = DState (h*x)


update_rk4 :: State -> (Double -> State -> DState) -> Double -> Double -> State
update_rk4 y f t h = update y (scale (h*(1.0/6.0)) s) where
  s = add k1 (add s2 (add s3 k4))
  s2 = scale 2 k2
  s3 = scale 2 k3
  k1 = f t y
  k2 = f (t+0.5*h) ( update y (scale (0.5*h) k1) )
  k3 = f (t+0.5*h) ( update y (scale (0.5*h) k2) )
  k4 = f (t+h) ( update y (scale h k3) )

State と DState は、State の特定のインスタンスが DState の特定のインスタンスを必要とするという意味で絡み合っているため、型クラスを定式化するのは難しいようです。

4

2 に答える 2

9

これは、実際には、独自の型クラスをロールバックしたいものではありません。Haskell は、すべてのクラスを作成するオブジェクト指向言語ではなく、クラスは「深い数学的概念」を捉えることになっています。この例では、非常に一般的な「概念」は、差分変更を追加できるというものであり、そのようなクラスがすでに十分に存在することを確認してください。

update :: (AffineSpace st, d ~ Diff st) => st -> d -> st
      -- note that idiomatic argument order would be `d -> st -> st` instead.
update = (.+^)

add :: VectorSpace d => d -> d -> d
add = (^+^)

scale :: (VectorSpace d, h ~ Scalar d)
   => h -> d -> d
scale = (*^)


update_rk4 :: (AffineSpace st, d ~ Diff st, VectorSpace d, h ~ Scalar d, Fractional h)
     => st -> (h -> st -> d) -> h -> h -> st
     -- again, more idiomatic order is `(h -> st -> d) -> h -> h -> st -> st`.

引数を最後に置くことをお勧めする理由についてst: Haskell では関数を部分的に適用し、「パイプライン引数」をη-reduce するのが一般的です。この例では、特定の「RK4-ステッパー」を再利用したい可能性が非常に高く、st引数が最後にある場合は、次のように簡単に実行できます。

simStep :: ParticularState -> ParticularState
simStep = update_rk4 f t h
 where f t y = ...
       ...

ですでにy変数をバインドするsimStep y = update_rk4 y f t h必要がある場合は、f宣言でそれを隠すか、厄介な で曖昧さをなくす必要がありますf t y' = ...。この場合、これは大きな利点ではありませんが、η-reduction のアイデアを一貫して適用すると、コード全体を大幅にクリーンアップできます。

于 2014-10-19T13:09:29.973 に答える
0

これらは標準の Haskell 98 ではなく、GHC 拡張ですが、関数の依存関係を持つ複数パラメーターの型クラスを使用できます。これらにより、メソッドがすべての型パラメーターを決定しない型クラスを定義できます。そうでなければ、あいまいになります。

例えば、

class RK4 state dstate | dstate -> state where
    update :: state -> dstate -> state
    add :: dstate -> dstate -> dstate
    scale :: dstate -> dstate -> dstate

関数の依存関係がなければ、それらへの呼び出しが型を修正せず、型クラスの制約を解決できないため、あいまいになりますaddscalestate

上記のリンクで、その他の例、チュートリアル、およびディスカッションを参照してください。機能依存関係とタイプ ファミリーの比較も参照してください。これは、他の応答で採用されたアプローチです。

于 2014-10-19T13:09:12.410 に答える