2016-07-29 7 views
1

Wie kann ich einen Parameter als Union-Case-Wert zurückgeben?Wie kann ich einen Parameter als Union-Case-Wert zurückgeben?

Ich habe die folgende Funktion:

let jumpBlack ((blackChecker:BlackChecker),(blackCheckers:BlackChecker list)) (redPiece:RedPiece) = 

    let yIncrementValue = -1 
    let minY = 0 

    match redPiece with 
    | RedPiece.RedChecker rc -> 
     let position = rc.Position |> jump blackChecker.Position yIncrementValue 
     match position with 
     | pos when pos = rc.Position -> RedPiece.RedChecker { rc with Position= position }, blackCheckers 
     | pos when pos.Y = minY  -> RedPiece.RedKing { RedKing.Position=position }, blackCheckers |> remove blackChecker 
     | _ ->       RedPiece.RedChecker { rc with Position= position }, blackCheckers |> remove blackChecker 

    | RedPiece.RedKing rk -> 
     let position = rk.Position |> jump blackChecker.Position yIncrementValue 
     match position with 
     | pos when pos = rk.Position -> RedPiece.RedKing { rk with Position= position }, blackCheckers 
     | pos when pos.Y = minY  -> RedPiece.RedKing { Position=position }, blackCheckers |> remove blackChecker 
     | _       -> RedPiece.RedKing { rk with Position= position }, blackCheckers |> remove blackChecker 

Insbesondere möchte ich in einer Funktion, diesen Teil der obigen Funktion Refactoring:

match redPiece with 
| RedPiece.RedChecker rc -> 
    let position = rc.Position |> jump blackChecker.Position yIncrementValue 
    match position with 
    | pos when pos = rc.Position -> RedPiece.RedChecker { rc with Position= position }, blackCheckers 
    | pos when pos.Y = minY  -> RedPiece.RedKing { RedKing.Position=position }, blackCheckers |> remove blackChecker 
    | _ ->       RedPiece.RedChecker { rc with Position= position }, blackCheckers |> remove blackChecker 

| RedPiece.RedKing rk -> 
    let position = rk.Position |> jump blackChecker.Position yIncrementValue 
    match position with 
    | pos when pos = rk.Position -> RedPiece.RedKing { rk with Position= position }, blackCheckers 
    | pos when pos.Y = minY  -> RedPiece.RedKing { Position=position }, blackCheckers |> remove blackChecker 
    | _       -> RedPiece.RedKing { rk with Position= position }, blackCheckers |> remove blackChecker 

Wie Refactoring ich die Dupliziert Code oben?

Ich bin fest, wie die Vervielfältigung zu entfernen, und nach wie vor die beiden unterschiedlichen Union-Typen (dh rot-Checker und roten König) zurückkehren

Domain:

(* Types *) 
type North = NorthEast | NorthWest 
type South = SouthEast | SouthWest 

type Direction = 
    | NorthEast 
    | NorthWest 
    | SouthEast 
    | SouthWest 

type Position =  { X:int; Y:int } 

type BlackChecker = { Position:Position } 
type RedChecker = { Position:Position } 
type BlackKing = { Position:Position } 
type RedKing =  { Position:Position } 

type Checker = 
    | BlackChecker of BlackChecker 
    | RedChecker of RedChecker 
    | BlackKing of BlackKing 
    | RedKing  of RedKing 

type King = 
    | BlackKing of BlackKing 
    | RedKing of RedKing 

type RedPiece = 
    | RedChecker of RedChecker 
    | RedKing of RedKing 

(* Functions *) 
let rec remove item list = list |> List.filter (fun x -> x <> item) 

let setRowPosition y1 y2 y3 index = 
    match index with 
    | x when x < 4 -> { X=x; Y=y1 } 
    | x when x < 8 -> { X=x-4; Y=y2 } 
    | _   -> { X=index-8; Y=y3 } 

let initializeBlack() = 
    let setPosition index = 
     index |> setRowPosition 7 6 5 

    let blackCheckers = List.init 12 setPosition |> List.map (fun pos -> { BlackChecker.Position= { X=pos.X; Y=pos.Y } }) 
    blackCheckers 

let initializeRed() = 
    let setPosition index = 
     index |> setRowPosition 0 1 2 

    let redCheckers = List.init 12 setPosition |> List.map (fun pos -> { RedChecker.Position= { X=pos.X; Y=pos.Y } }) 
    redCheckers 

let set (x, y) positions (position:Position) = 
    match not (positions |> List.exists (fun pos -> pos = { X=x; Y=y })) with 
    | true -> { X=x; Y=y } 
    | false -> position 

let moveBlack direction positions (checker:BlackChecker) = 
    let position = checker.Position 

    match direction with 
    | North.NorthEast -> { BlackChecker.Position= (positions, position) ||> set ((position.X + 1), (position.Y + 1)) } 
    | North.NorthWest -> { BlackChecker.Position= (positions, position) ||> set ((position.X - 1), (position.Y + 1)) } 

let moveRed direction positions (checker:RedChecker) = 
    let position = checker.Position 

    match direction with 
    | South.SouthEast -> { RedChecker.Position= (positions, position) ||> set ((position.X + 1), (position.Y - 1)) } 
    | South.SouthWest -> { RedChecker.Position= (positions, position) ||> set ((position.X - 1), (position.Y - 1)) } 

let moveKing direction positions (king:King) = 

    let position = match king with 
        | King.BlackKing bk -> bk.Position 
        | King.RedKing rk -> rk.Position 

    let result = match direction with 
       | NorthEast -> (positions, position) ||> set ((position.X + 1), (position.Y + 1)) 
       | NorthWest -> (positions, position) ||> set ((position.X - 1), (position.Y + 1)) 
       | SouthEast -> (positions, position) ||> set ((position.X + 1), (position.Y - 1)) 
       | SouthWest -> (positions, position) ||> set ((position.X - 1), (position.Y - 1)) 

    match king with 
    | King.BlackKing _ -> King.BlackKing { BlackKing.Position= result } 
    | King.RedKing _ -> King.RedKing { RedKing.Position= result } 

let jump target yDirection source = 
    let updateX value = { X=target.X + value 
          Y=target.Y + yDirection } 
    match source with 
    | position when position.Y + yDirection = target.Y && 
        position.X + 1 = target.X -> updateX 1 

    | position when position.Y + yDirection = target.Y && 
        position.X - 1 = target.X -> updateX -1 
    | _ -> source 

let jumpRed ((redChecker:RedChecker), (redCheckers:RedChecker list)) (blackChecker:BlackChecker) = 

    let yIncrementValue = 1 
    let maxY = 7 
    let position = blackChecker.Position |> jump redChecker.Position yIncrementValue 

    match position with 
    | pos when pos = blackChecker.Position -> BlackChecker { blackChecker with Position= position }, redCheckers 
    | pos when pos.Y = maxY    -> Checker.BlackKing { BlackKing.Position=position }, redCheckers |> remove redChecker 
    | _ -> BlackChecker { blackChecker with Position= position }, redCheckers |> remove redChecker 

let jumpBlack ((blackChecker:BlackChecker),(blackCheckers:BlackChecker list)) (redPiece:RedPiece) = 

    let yIncrementValue = -1 
    let minY = 0 

    match redPiece with 
    | RedPiece.RedChecker rc -> 
     let position = rc.Position |> jump blackChecker.Position yIncrementValue 
     match position with 
     | pos when pos = rc.Position -> RedPiece.RedChecker { rc with Position= position }, blackCheckers 
     | pos when pos.Y = minY  -> RedPiece.RedKing { RedKing.Position=position }, blackCheckers |> remove blackChecker 
     | _ ->       RedPiece.RedChecker { rc with Position= position }, blackCheckers |> remove blackChecker 

    | RedPiece.RedKing rk -> 
     let position = rk.Position |> jump blackChecker.Position yIncrementValue 
     match position with 
     | pos when pos = rk.Position -> RedPiece.RedKing { rk with Position= position }, blackCheckers 
     | pos when pos.Y = minY  -> RedPiece.RedKing { Position=position }, blackCheckers |> remove blackChecker 
     | _       -> RedPiece.RedKing { rk with Position= position }, blackCheckers |> remove blackChecker 

Tests:

[<Test> 
let ``red king jumps checker``() = 
    let blackChecker = { BlackChecker.Position= { X=1 ; Y=1 } } 
    let target = (blackChecker, [blackChecker]) 

    RedKing { RedKing.Position= { X=0 ; Y=2 } } |> jumpBlack target 
               |> fst 
               |> should equal (RedPiece.RedKing { RedKing.Position= { X=2 ; Y=0 } }) 

[<Test>] 
let ``black checker removed after being jumped``() = 
    let target = { BlackChecker.Position= { X=1 ; Y=1 } }, [] 
    RedChecker { RedChecker.Position= { X=2 ; Y=2 } } |> jumpBlack target 
                 |> snd 
                 |> should equal [] 
[<Test>] 
let ``red checker jumps black checker southeast``() = 
    let blackChecker = { BlackChecker.Position= { X=3 ; Y=2 } } 
    let target = blackChecker, [blackChecker] 

    RedChecker { RedChecker.Position= { X=2 ; Y=3 } } |> jumpBlack target 
                 |> fst 
                 |> should equal (RedChecker { RedChecker.Position= { X=4 ; Y=1 } }) 
+0

Ist es möglich, den Code zu konzentrieren, um sich auf das eigentliche Problem zu konzentrieren? Kannst du nicht ein Tupel zurückgeben oder es in etwas einpacken? – s952163

+0

zu [Code Review] (http://codereview.stackexchange.com/) wechseln – s952163

+0

Der Code ist die Funktion, jumpBlack. Der Bereich Domain und Test dient als Kontextinformationen. –

Antwort

2

In dem Code, den Sie umgestalten wollen, gibt es dort eine Übersicht d nur zwei Stellen zu sein, wo die zwei Teile des Codes etwas anderes tun - eins ist das Musterabgleich (wo man nach RedChecker sucht und ein anderes für RedKing) und der andere Teil ist der Körper der ersten und der dritten Zeile des Musters Matching (wo man RedChecker und andere RedKing zurückgibt).

Der Code funktioniert auch über verschiedene Arten, aber die sind alle vom gleichen Typ:

type BlackChecker = { Position:Position } 
type RedChecker = { Position:Position } 
type BlackKing = { Position:Position } 
type RedKing =  { Position:Position } 

den gemeinsamen Teil Extrahierung wird viel einfacher sein, wenn Sie nur die gleiche Art für alle diese verwenden:

type Piece = { Position:Position } 
type BlackChecker = Piece 
type RedChecker = Piece 
type BlackKing = Piece 
type RedKing = Piece 

so müssen Sie den Code durch zwei Dinge parametrieren - beide als Funktionen der folgenden Typen dargestellt werden:

detector  : Checker -> Piece option 
wrapper  : Piece -> Checker 

Der Schlüsseltrick hier ist, dass diese beiden Funktionen sich wie der diskriminierte Vereinigungsfall verhalten - der erste verhält sich als Mustervergleich mit dem DU-Fall und der zweite verhält sich wie ein Konstruktor des DU-Falls.

Jetzt können Sie die gemeinsame Funktionalität in so etwas wie extrahieren:

match detector redPiece with 
| Some rk -> 
    let position = rk.Position |> jump blackChecker.Position yIncrementValue 
    match position with 
    | pos when pos = rk.Position -> wrapper { rk with Position= position }, blackCheckers 
    | pos when pos.Y = minY  -> RedPiece.RedKing { Position=position }, blackCheckers |> remove blackChecker 
    | _       -> wrapper { rk with Position= position }, blackCheckers |> remove blackChecker 

| None -> // Handle the case when it is not the piece we are interested in 

Ich habe alles nicht getestet, aber hoffentlich diese zumindest zeigt den Gedankengang, dass Sie die gemeinsame Funktionalität aus den beiden zu extrahieren folgen Teile. Das heißt, ich wäre nicht allzu besorgt über Refactoring des Codes - wenn Sie ähnliche Sache nur zweimal wiederholen, ich fühle es ist oft einfacher, nur zwei Kopien zu behalten ...

+0

Danke Tomas. Ich werde auf deine Führung marinieren und sie einlassen. –

2

In Ordnung, Ihr Modell ist wirklich kompliziert ...Ich habe die folgende Vereinfachung:

type Position = { X:int; Y:int } 

type Color = 
    | Red 
    | Black 

type PieceType = 
    | King 
    | Checker 

type Piece = Color*PieceType*Position 

dann Ihre jumpBlack Funktion übersetzen erhalte ich:

let jumpBlack ((blackChecker:Piece),(blackCheckers:Piece list)) (redPiece:Piece) = 

    let yIncrementValue = -1 
    let minY = 0 

    match redPiece, blackChecker with 
    | (Red, Checker, position), (_, _, blackCheckerPosition) -> 
     let newposition = position |> jump blackCheckerPosition yIncrementValue 
     match newposition with 
     | pos when pos = position -> (Red, Checker, pos), blackCheckers 
     | pos when pos.Y = minY  -> (Red, King, pos) , blackCheckers |> remove blackChecker 
     | pos ->       (Red, Checker, pos), blackCheckers |> remove blackChecker 
    | (Red, King, position), (_, _, blackCheckerPosition) -> 
     let newposition = position |> jump blackCheckerPosition yIncrementValue 
     match newposition with 
     | pos when pos = position -> (Red, King, pos), blackCheckers 
     | pos when pos.Y = minY  -> (Red, King, pos), blackCheckers |> remove blackChecker 
     | pos       -> (Red, King, pos), blackCheckers |> remove blackChecker 
    | _ -> failwith "Invalid" //Deal with Black pieces here! 

Aber jetzt, es ist wirklich einfach, den Code Refactoring, weil wir sehen, dass, wenn pos nicht gleich der minY Wert bleibt es gleich PieceType, aber wenn es minY erreicht, wird es immer ein King.

let jumpBlackNew ((blackChecker:Piece),(blackCheckers:Piece list)) (redPiece:Piece) = 

    let yIncrementValue = -1 
    let minY = 0 

    match redPiece, blackChecker with 
    | (Red, pieceType, position), (_, _, blackCheckerPosition) -> 
     let newposition = position |> jump blackCheckerPosition yIncrementValue 
     match newposition with 
     | pos when pos = position -> (Red, pieceType, pos), blackCheckers 
     | pos when pos.Y = minY  -> (Red, King, pos) , blackCheckers |> remove blackChecker 
     | pos ->      (Red, pieceType, pos), blackCheckers |> remove blackChecker 
    | _ -> failwith "Invalid" //Deal with Black pieces here! 

auch Dies macht es viel einfacher für Sie Sprung nehmen in schwarz und rot Kontrolleure zu machen.

Verwandte Themen