我维护了一些代码,遗憾的是,广泛使用引用来跟踪程序中的状态。在一个模块中使用它们特别令人震惊,它通过欧拉方法更新了许多值:
eulerUpdate states timestep = do
_val1 <- readReference (val1 states)
_dval1 <- readReference (dval1 states)
_val2 <- readReference (val2 states)
_dval2 <- readReference (dval2 states)
-- ...
_valn <- readReference (valn states)
_dvaln <- readReference (dvaln states)
let euler val deriv = val + deriv * timestep
writeReference (val1 states) euler _val1 _dval1
-- ...
writeReference (valn states) euler _valn _dvaln
我对Haskell相对较新,但我的理解是,这是一个可怕的,可怕的,不好的,非常糟糕的事情。我没有重构其周围的所有其他东西,而是希望可能有某种方法至少将它压缩到更少的LOC以便于阅读。是否有任何可以在这里完成的事情&#34; map&#34;将readReference (x states)
变成一堆标识符?我更多地看了Kliesli的箭,但是我看不到有什么能帮助我。
答案 0 :(得分:1)
首先:是否可以重新安排读写操作?如果是这样,您可以使用像
这样简单的东西let updateOne val dval = do
_val = readReference (val states)
_dval = readReference (dval states)
writeReference (val states) (euler _val _dval)
然后就像
一样使用它eulerUpdate states timestep = do
updateOne val1 dval1
updateOne val2 dval2
...
如果没有,并且写不好阅读,那么,您可能希望获得创造性并将读写操作分开,但是在相同的结构中:
data ReadWriteReference where
ReadWriteReference :: (States -> IO a) -> (States -> a -> IO ()) -> ReadWriteReference
performReadWrite :: States -> ReadWriteReference -> IO ()
performReadWrite states (ReadWriteReference read write) = do
a <- read states
write states a
makeRW :: (States -> Reference) -> (States -> Reference) -> ReadWriteReference
makeRW val dval = ReadWriteReference read write where
read states = do
_val <- readReference (val states)
_dval <- readReference (dval states)
writeReference (val states) (euler _val _dval)
(<+>) :: ReadWriteReference -> ReadWriteReference -> ReadWriteReference
ReadWriteReference read1 write1 <+> ReadWriteReference read2 write2 = ReadWriteReference read write where
read states = do
a <- read1 states
b <- read2 states
return (a, b)
write states (a, b) = do
write1 states a
write2 states b
现在您可以像这样使用它:
performReadWrite states $ makeRW val1 dval1 <+> makeRW val2 dval2 <+> ... <+> makeRW valn dvaln
您甚至可以将ReadWriteReference作为Monoid的实例并使用标准组合器。