0

我有以下代码片段:

val oldStep = step
try {
  someDangerousActionPotentiallyModifyingStep()
} finally {
  step = oldStep
}

是否可以编写一个通用的辅助方法来抽象出模式?然后我可以使用这样的辅助方法:

remember(step) {
  someDangerousActionPotentiallyModifyingStep()
}

当然下面的方法是行不通的,因为 Scala 不支持按引用传递:

def remember[T](x: T)(action: => Unit) {
  val previousValue = x
  try {
    action
  } finally {
    x = previousValue
  }
}

我通过将step数据成员更改为隐式参数解决了这个问题。这样,我就不必恢复 的旧值step,因为它的值在同一范围内永远不会改变 :)

4

3 回答 3

2

问题是 var 没有给你足够的控制权来做你想做的事。

作为对您问题的直接回答:您需要一个具有更新方法而不是字段的对象。像这样的东西:

class Box[T](var value:T) { 
  def apply = value
  def update(newValue:T) { value = newValue }
  override def toString = value.toString 
} 

然后你可以像这样解决你的问题:

def remember[T](box:Box[T])(action: =>Unit) { 
  val prev = box.apply
  try { 
    action 
  } catch { 
    case _ => box() = prev 
  } 
}

val step = new Box(0)

// working update
remember(step) { step() = 4 }

// step is now 4

// aborted update
remember(step) { step() = 5; throw new Exception }

// step is still 4

但请注意,这确实不是非常惯用的 scala。您应该尝试使您的 someDangerousActionPotentiallyModifyingStep 成为返回新步骤的无副作用函数。

基本上是这样的:

try {    
  step = someDangerousFunctionOfStep(step)
} catch {
  case _ => // we don't have to do anything because step is still the same
}

如果由于某种原因无法做到这一点,您可能需要研究Akka 代理,它们在概念上类似于上面的 Box,不同之处在于它们是线程安全的并且可以以事务方式使用。

以下是使用 akka 代理解决问题的方法:

首先你需要一个演员系统:

implicit val actorSystem = akka.actor.ActorSystem("test")

然后您可以定义一个包含步骤值的代理

val step = akka.agent.Agent(0)

现在您可以在事务中更新它:

import scala.concurrent.stm._    

atomic { txn => step() = 4 } 
// step.get will now return 4

atomic { txn => step() = 5; throw new Exception }
// step.get will still return 5. You will have to catch the exception if you don't want
// it to propagate outward

当您拥有多个代理并以原子方式更新它们时,akka 代理的真正威力就来了。有关规范的“帐户转移”交易示例,请参阅 akka 代理文档。

于 2013-06-27T15:35:28.723 回答
1

它非常丑陋(并且是对不变性的侮辱),但你可以尝试这样的事情:

def remember[T](newT:T, get: => T, set:(T) => Unit)(action: => Unit) = {
  var old = get
  set(newT)
  try{
    action
  }
  finally{
    set(old)
  }
}

然后像这样使用它:

var x = "hello"

remember[String]("world", x, x = _){
  //do something dangerous here 
}
于 2013-06-27T16:44:18.600 回答
1

我认为您正在寻找贷款模式。

http://blog.knoldus.com/2012/11/16/scalaknol-understanding-loan-pattern/

于 2013-06-27T21:09:14.357 回答