我试图在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
如果没有函数依赖关系,add
和scale
将是不明确的,因为对它们的调用不会修复state
类型,从而无法解析typeclass约束。
请参阅以上链接中的更多示例、教程和讨论。另请参阅函数依赖项与类型族的比较,这是另一个响应中采用的方法。