Haskell中的Runge-Kutta(RK4),类型系统问题



我试图在Haskell中实现四阶Runge-Kutta,但我发现很难使用Haskell类型的系统来完成这项任务。有人能帮忙吗?我希望将"State"one_answers"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的特定实例。。或者可能还有其他我看不到的方法吗?

这并不是您想要推出自己的类型类的真正原因。Haskell不是一种为所有事物创建类的OO语言,相反,类应该捕捉"深层数学概念"。在这个例子中,非常常见的"概念"是,您可以将差分变化相加,并且确信这样的类已经存在。

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中部分应用函数是很常见的,η-减少"流水线参数"。在这个例子中,您很可能想要重用一个特定的"RK4步进器",如果st参数是最后一个,那么您可以简单地使用

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

如果必须在simStep y = update_rk4 y f t h中绑定一个y变量,则需要使用f声明对其进行阴影处理,或者使用尴尬的f t y' = ...来消除歧义。在这种情况下,这并不是一个很大的增益,但如果你始终如一地应用η-reduction思想,它可以大大清理你的整个代码。

您可以使用具有函数依赖关系的多参数类型类,尽管这些不是标准的Haskell 98,而是GHC扩展。这些允许您定义一个类型类,其方法不能确定所有类型参数,否则这些参数将是不明确的。

例如,

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

如果没有函数依赖关系,addscale将是不明确的,因为对它们的调用不会修复state类型,从而无法解析typeclass约束。

请参阅以上链接中的更多示例、教程和讨论。另请参阅函数依赖项与类型族的比较,这是另一个响应中采用的方法。

相关内容

  • 没有找到相关文章

最新更新