2017-01-03 2 views
0

reduzieren Ich habe meinen Code für day 12 der Einführung des Codes mit monocle, eine Linsenbibliothek in scala refaktoriert.Wie man Boilerplate mit Monokel in scala

Ist es möglich, diesen Code zu verbessern:

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) 
    } 

Und hier ist noch einmal zu versuchen, die Änderung der einzelnen Felder zu trennen

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) 
    } 

eine Frage: Gibt es eine Möglichkeit Map.withDefaultValue zu verwenden, um mit Monokel?

Der vollständige Code ist hier: https://gist.github.com/YannMoisan/b8ba25afc041d88706545527d9ec1988

+1

Ich würde 'index' stattdessen verwenden (Die letzte Zeile von processInstruction3 sollte mit dem obigen Code ersetzt werden) von 'at', wenn Sie den Wert innerhalb der Karte ändern möchten, z '(State.mem composeOptional index (r)). Modify (_ + 1)' anstelle von '(State.mem composeLens at (r)). Modify (_. Map (_ + 1))' –

Antwort

-1

Sie könnten den zweiten Ansatz verwenden wollen, weil es Handhabung von zwei Feldern trennt. Die Funktionen sollten jedoch nicht nacheinander interpretiert werden (andThen), sondern sie sollten als PartialFunction s mit orElse kombiniert werden.

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)) 
} 

UPDATE (nach dem Yann Moisan Kommentar)

Die Ausführung kann im Fall einer Endlosschleife in Benutzer-Programm nicht beenden. Also statt der foldLeft brauchen wir eine rekursive Funktion, die die nächste Anweisung durch Zeiger extrahieren:

@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 _ 

+0

Anweisungen sollten nicht sein in der Reihenfolge wegen der Sprung (jnz) Anweisung ausgeführt. –

+0

Hier sind zwei Aspekte. Einer ist Modularität - die Handhabung von Anweisungen verschiedener Art getrennt. Und dies wird durch 'PartialFunction' erreicht. Der andere Aspekt ist der sequentielle Durchlauf von zwei Schritten für normale Befehle (Ausführung und Zeigervorschub). Beide Aspekte werden in 'processInstruction3' richtig behandelt. In 'processInstruction2'' (ptr andThen mem) 'im Fall von' Jnz' wird es auch vom zweiten Mustervergleichsblock behandelt (zum Glück gibt es keine andere ähnliche Anweisung und es wird der Standardfall durchlaufen). –

Verwandte Themen