斯卡拉 – 纯度,参考透明度和国家Monad
我目前正在设计一种数值算法,作为其操作的一部分,需要多次更新双精度矢量.由于算法必须尽可能节省空间和时间,因此我不想对传统类型的FP代码进行编码,这些FP代码在每次操作后都会在引擎盖下创建许多版本的数据结构.我也不想创建可变数据结构并使它们全局可用.因此,我决定使用可变数据结构,但随后选择在状态monad中执行可变操作.由于这是我第一次尝试使用State monad,我想确认我是否拥有
>保留参考透明度 更新功能转换数据结构状态.由于破坏性更新已本地化在此函数中,并且无法从外部获取数据结构的句柄,因此我认为此函数是纯粹且引用透明的. def update(i : Int,d : Double) = State[ArraySeq[Double],Unit]{ case xs: ArraySeq[Double] => {xs(i) = d; (xs,())} } app函数是一个玩具函数,它会消耗一系列双精度并修改它的状态: def app : State[ArraySeq[Double],Unit] = for{ _ <- update(0,3.142) // do a heap of stuff on ArraySeq }yield() 呼叫: app(Vector(0.0,1.0,2.0,3.0,4.0).to[ArraySeq])._1.to[Vector] 结果: res0: Vector[Double] = Vector(3.142,4.0) 解决方法
我想你可以说你的更新本身是纯粹的,因为它只代表一些变异,但是一旦你运行它所有的赌注都是关闭的:
scala> val xs = List(1.0,3.0).to[ArraySeq] xs: scala.collection.mutable.ArraySeq[Double] = ArraySeq(1.0,3.0) scala> update(0,10).eval(xs) res0: scalaz.Id.Id[Unit] = () scala> xs res1: scala.collection.mutable.ArraySeq[Double] = ArraySeq(10.0,3.0) 这是一个糟糕的场景,它与纯粹或引用透明相反. 在你的例子中,State并没有真正为你买任何东西 – 事实上,你正在以一种没有其他人可以改变的ArraySeq的方式调用app.您也可以咬住子弹并在您控制的范围内以通常的方式使用可变数据结构 – 即,像这样编写应用程序: def app(xs: Vector[Double]): Vector[Double] = { val arr = xs.to[ArraySeq] // Perform all your updates in the usual way arr.toVector } 这实际上是纯粹的和引用透明的,但它也比State版本更诚实.如果我看到State [Foo,Unit]类型的值,我的假设是该值表示某种操作将Foo更改为新的Foo,而不会改变原始的Foo.这是状态monad的所有状态 – 它提供了一种很好的方法,可以对不可变数据结构进行建模操作,并以类似于变异的方式组合它们.如果你把它与实际的变异混合在一起,你很可能会混淆任何使用你代码的人. 如果你真的想要同时真正的变异和纯度,你可以看看Scalaz的STArray.这是一个非常聪明的解决方案,在像Haskell这样的语言中,这是一种很有意义的方法.我自己的感觉是,它在Scala中几乎总是错误的解决方案.如果你真的需要一个可变数组的性能,只需使用一个本地可变数组,并确保你不会泄漏到外部世界.如果你不需要那种表现(大部分时间你都不需要),可以使用State之类的东西. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |