如何在scala中使用单片眼镜减少样板

如何在scala中使用单片眼镜减少样板,scala,lenses,monocle-scala,Scala,Lenses,Monocle Scala,我已经使用scala中的镜头库monocle重构了我的代码,以迎接代码的出现 是否可以改进此代码: type Register = String type Mem = Map[String, Int] @Lenses case class State(mem: Mem, pointer: Int) def processInstruction(instructions: Seq[Instruction]): State => State = { s =>

我已经使用scala中的镜头库monocle重构了我的代码,以迎接代码的出现

是否可以改进此代码:

  type Register = String
  type Mem = Map[String, Int]

  @Lenses
  case class State(mem: Mem, pointer: Int)

  def processInstruction(instructions: Seq[Instruction]): State => State = { s =>
    (instructions(s.pointer) match {
      case Inc(r) =>
        State.pointer.modify( _ + 1) andThen (State.mem composeLens at(r)).modify(_.map(_ + 1))
      case Dec(r) =>
        State.pointer.modify( _ + 1) andThen (State.mem composeLens at(r)).modify(_.map(_ - 1))
      case CpyInt(v, to) =>
        State.pointer.modify( _ + 1) andThen (State.mem composeLens at(to)).set(Some(v))
      case CpyReg(from, to) =>
        State.pointer.modify( _ + 1) andThen (State.mem composeLens at(to)).set(Some(s.mem(from)))
      case Jnz(r, v) => if (r != "1" && s.mem(r) == 0)
        State.pointer.modify( _ + 1)
      else
        State.pointer.modify( _ + v )
    }).apply(s)
  }
这里是另一个尝试,分离每个字段的修改

  def processInstruction2(instructions: Seq[Instruction]): State => State = { s =>
    val ptr = instructions(s.pointer) match {
      case Jnz(r, v) if !(r != "1" && s.mem(r) == 0) => State.pointer.modify(_ + v)
      case _ => State.pointer.modify(_ + 1)
    }

    val mem = instructions(s.pointer) match {
    case Inc(r) => (State.mem composeLens at(r)).modify(_.map(_ + 1))
    case Dec(r) => (State.mem composeLens at(r)).modify(_.map(_ - 1))
    case CpyInt(v, to) => (State.mem composeLens at(to)).set(Some(v))
    case CpyReg(from, to) => (State.mem composeLens at(to)).set(Some(s.mem(from)))
    case _ => identity[State]
  }
    (ptr andThen mem)(s)
  }
还有一个问题:有没有一种方法可以将
Map.withDefaultValue
与monocle一起使用


完整代码如下:

您可能希望使用第二种方法,因为它将两个字段的处理分开。 但是,函数不应按顺序解释(
),而应将它们作为
部分函数
s与
或LSE
组合

def processInstruction3(instructions: Seq[Instruction]): State => State = {
  val ptr: PartialFunction[Instruction, State => State] = {
    case Jnz(r, v) =>
      State.pointer.modify(_ + v)
  }

  val incPointer: State => State = State.pointer.modify( _ + 1)
  def reg(r: String): Lens[State, Option[Int]] = State.mem composeLens at(r)
  val mem: PartialFunction[Instruction, State => State] = {
    case Inc(r) => reg(r).modify(_.orElse(Option(0)).map(_ + 1))
    case Dec(r) => reg(r).modify(_.orElse(Option(0)).map(_ - 1))
    case CpyInt(v, to) => reg(to).set(Some(v))
    case CpyReg(from, to) => s => reg(to).set(reg(from).get(s))(s)
  }
  val interpreter = ptr orElse (mem andThen (_ andThen incPointer))
  s => instructions.foldLeft(s)((s, i) => interpreter(i)(s))
}
更新(在Yann Moisan评论之后)

如果用户程序中存在无限循环,则执行可能不会终止。因此,我们需要一些递归函数,而不是
foldLeft
,它将通过指针提取下一条指令:

@tailrec
def loop(s: State): State = {
  if(s.pointer>=instructions.length)
    s
  else {
    val instruction = instructions(s.pointer)
    val nextState = interpreter(instruction)(s)
    loop(nextState)
  }
}
loop _

(应将
processInstruction3
的最后一行替换为上述代码)

由于跳转(jnz)指令,指令不应按顺序执行。一种是模块化——分别处理各种指令。这是通过
PartialFunction
实现的。另一个方面是正常指令(执行和指针前进)的两个步骤的顺序运行。这两个方面在
过程说明3
中都得到了正确处理。在
processInstruction2
(ptr和THEN mem)
中,在
Jnz
的情况下,它也将由第二个模式匹配块处理(幸运的是没有其他类似的指令,它将通过默认情况)。当您想要修改映射内部的值时,我将使用
索引
而不是
at
,例如
(State.mem compose可选索引(r))。修改(+1)
而不是
(State.mem composeLens at(r))。修改(+1))