如何在scala中使用单片机减少样板
Posted
技术标签:
【中文标题】如何在scala中使用单片机减少样板【英文标题】:How to reduce boilerplate with monocle in scala 【发布时间】:2017-05-18 01:13:26 【问题描述】:我使用 scala 中的一个镜头库 monocle 重构了代码的出现 day 12 的代码。
是否可以改进此代码:
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
与单片眼镜一起使用?
完整代码在这里:https://gist.github.com/YannMoisan/b8ba25afc041d88706545527d9ec1988
【问题讨论】:
当您想修改地图内的值时,我会使用index
而不是at
,例如(State.mem composeOptional index(r)).modify(_ + 1)
而不是 (State.mem composeLens at(r)).modify(_.map(_ + 1))
【参考方案1】:
您可能希望使用第二种方法,因为它分离了两个字段的处理。
但是,这些函数不应按顺序解释 (andThen
),而应将它们组合为 PartialFunction
s 和 orElse
。
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
实现的。另一方面是正常指令(执行和指针前进)的两个步骤的顺序运行。这两个方面都在processInstruction3
中得到了妥善处理。在processInstruction2
(ptr andThen mem)
的情况下Jnz
它也将由第二个模式匹配块处理(幸运的是没有其他类似的指令,它将通过默认情况)。以上是关于如何在scala中使用单片机减少样板的主要内容,如果未能解决你的问题,请参考以下文章
yongc语言编写单片机程序,出现了堆栈溢出情况,怎么解决?堆栈指针怎么初始化?
如何减少 responseJSON 中的样板,就像我使用 URLRequestConvertible 对相关的 Web 调用进行分组一样