如何在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),而应将它们组合为 PartialFunctions 和 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语言编写单片机程序,出现了堆栈溢出情况,怎么解决?堆栈指针怎么初始化?

如何利用嵌入式集成开发工具,让其更好地服务于设计?

如何使用 Hive MACRO 减少样板文件

单片机 | 一种串口高效收发数据的实现方法

3.4 51单片机-矩阵键盘

如何减少 responseJSON 中的样板,就像我使用 URLRequestConvertible 对相关的 Web 调用进行分组一样