2017-12-29 36 views
1

Ich habe die folgende Datenstruktur des Blocks, die eine Bestandsaufnahme haben könnte, die Stapel von Gegenständen countain könnte:Wie ändert man den Wert von geschachtelten Optionals in OCaml einfach?

type item = Stone | Sand 
type stack = { 
    item : item; 
    size : int 
} 
type inventory = { 
    inA : stack option; 
    inB : stack option; 
    out : stack option; 
    prc : item option 
} 
type blockType = Container | Solid 
type block = { 
    blockType : blockType; 
    pos : vec; 
    oriented : vec option; 
    netID : int option; 
    sysID : int option; 
    inv : inventory option; 
    isMachine : bool 
} 

jetzt ich eine in der Theorie sehr einfache Funktion schreiben wollen, die einfach ein Element in die addiert " out "Slot eines Blocks Inventar. Meine sehr ausführlich Versuch war:

let addItem block = 
    let oldInv = get_some b.inv in 
    if is_some oldInv.out 
    then 
     let oldOut = get_some oldInv.out in 
     let newOut = {oldOut with size = oldOut.size+1} in 
     let newInv = {oldInv with out = Some newOut} in 
     {block with inv = Some newInv} 
    else 
     let newInv = {oldInv with out = Some {item=Stone; size=1}} in 
     {block with inv = Some newInv} 

und ich auch diese Heler Funktionen mehrere verschachtelte Blöcke Spiel

let is_some v = 
    match v with 
    | None -> false 
    | Some _ -> true 

let get_some v = 
    match v with 
    | None -> raise (Er "no some") 
    | Some s -> s 

Wie könnte ich das in eine elegantere Weise zu vermeiden?

+0

Wenn Ihre Frage ist, wie Sie dies auf eine elegantere Weise tun können, dann kann es eine gute Frage sein für [codereview] (https://codereview.stackexchange.com/). – vonaka

Antwort

1

Hier ist, was ich schreiben könnte:

let addItem block = 
    let newstack() = Some { item = Stone; size = 1 } in 
    let newinv() = 
     Some { 
      inA = None; inB = None; out = newstack(); prc = None 
     } 
    in 
    match block.inv with 
    | None -> 
     { block with inv = newinv() } 
    | Some ({ out = None; _ } as inv) -> 
     { block with inv = Some { inv with out = newstack() } } 
    | Some ({ out = Some stack; _ } as inv) -> 
     { block with inv = 
      Some { inv with 
       out = Some { stack with size = stack.size + 1 } 
      } 
     } 

Eleganz subjektiv ist. Aber der Punkt, den ich sehe, ist, Mustervergleiche für das zu verwenden, wofür es gut ist. Ihr Code scheint einen Mustervergleich unnötig zu vermeiden.

FWIW, Ihr Code schlägt fehl, wenn block.invNone ist. (Auch Sie haben b.inv, wo Sie wahrscheinlich wollen block.inv.)

+0

Danke das ist viel besser. Ich weiß, dass mein Code fehlschlagen würde, wenn block.inv None ist. In der Praxis sollte das kein Problem sein, da die Funktion nur mit Blöcken aufgerufen wird, wo dies nicht der Fall ist. Aber das ist keine gute Praxis der Ursache. Und yeah ich vermasselt die b und block Sache, während ich es über thx kopiert –

0

Könnten Sie bitte die Bedeutung der Felder des Inventartyps erklären? Alles, was eine Option ist, riecht schlecht ... Die ideale Datenstruktur beschränkt nur die Darstellung gültiger Zustände. Wenn ich besser in A inB ... verstehen könnte, könnte ich versuchen, mir einen anderen Vorschlag der Datenstruktur vorzustellen, der nur gültige Zustände von Daten ermöglicht. So viele optionale Felder, ich fühle es nicht

+1

Einfach gesagt, das optionale ist nur da, um einen leeren Steckplatz zu repräsentieren –

+0

Ich habe verstanden, gibt es eine Bedeutung hinter diesem leeren Steckplatz? Wie nie Block verwendet, im Aktivitätsblock, ... – Aldrik

+0

Nun, es bedeutet nur, dass es derzeit keinen Stapel in diesem Slot gibt, aber es könnte einen geben. Aber ich habe es bereits anhand all der Tipps, die ich bekommen habe, herausgefunden. Thx auf jeden Fall –

2

Um Ihren Code zu vereinfachen, erreichten Sie für Hilfsfunktionen, die auf Optionen arbeiten. Betrachten Sie diese Hilfsfunktion:

let (>>=) o f = 
    match o with 
    | None -> None 
    | Some x -> f x 

Dies definiert einen Operator, der eine Option auf seiner linken Seite nimmt. Wenn die Option None ist, wird dieser Operator als None ausgewertet (keine Änderung des Eingangs). Wenn die Option jedoch Some x ist, wendet der Bediener die Funktion auf der rechten Seite auf x an. Da der Operator bereits potenziell eine Option (None) auswertet, muss die Funktion dies natürlich auch tun. Mit diesem Operator können Sie einfach Funktionen verwenden und verketten, die None durch sie unverändert lassen oder den Inhalt einer Option bearbeiten.

was dazu führt:

let alter_inv block f = 
    { block with inv = block.inv >>= f } 

let alter_out block f = 
    alter_inv block (fun inv -> 
     Some { inv with out = inv.out >>= f }) 

let add_item block = 
    alter_out block (fun out -> Some { out with size = out.size + 1 }) 

let set_item block item = 
    alter_inv block (fun inv -> 
     match inv.out with 
     | None -> Some { inv with out = Some { item; size = 1 } } 
     | Some _ -> Some inv) 

ein Block ohne Inventar oder ohne out Slot gegeben, add_item block kehrt sie unverändert. Bei einem Block mit einem Inventar und etwas im out Slot, add_item block erhöht die Zählung.

jedoch in einem Kommentar sagen Sie

In der Praxis, dass [auf None auf Fehler Erhöhung] sollte nicht mit Blöcken aufgerufen wird, ein Problem, weil die Funktion sein, nur dann, wenn dies nicht der Fall ist.

Das heißt, dieser Teil Ihres Codes befasst sich nicht wirklich mit Optionen.Ihre Typen stimmen nicht mehr mit der Realität überein, und Sie geben die Sicherheit des Typprüfers auf: Dieser Teil Ihres Codes wird keine Fehler zur Laufzeit auslösen, sagen Sie, nicht weil der Compiler bewiesen hat, dass er nicht über die Typ System, aber weil Sie die Logik gründlich überprüft haben. Wenn Sie später einen Fehler in diese Logik einführen, kann der Compiler Sie nicht warnen. Ihr einziger Hinweis wird ein Überraschungslaufzeitfehler sein.

+1

ich denke, die Definition der (>> =) auf diese Weise wäre ein bisschen praktischer '' '' lassen (>> =) o f = übereinstimmen o mit | Keine -> Keine | Einige x -> Einige (f x) '' ''. Es fügt einige innerhalb der Definition des Operators hinzu und erlaubt es, sie aus den anderen Funktionen zu entfernen. – ghilesZ

+0

@ghilesZ Diese Definition wird oft 'map' genannt und ist ein wenig weniger flexibel, da sie eine Option nicht von' Some' verändert 'zu' None'. –

Verwandte Themen