2015-05-27 2 views
9

Angenommen, Sie haben diese Vereinigung haben:Ist eine Musterübereinstimmung die einzige Möglichkeit, Daten mit einem Vereinigungsfall in Verbindung zu bringen?

type Thing = 
| Eagle 
| Elephant of int 

Und Ihr Code hat eine Liste von Elefanten, wie in

let l = [Elephant (1000); Elephant (1200)] 

Und Sie wollten über l, iterieren und mit jeder aus den Daten zugeordnet drucken Elephant. Gibt es eine Möglichkeit, ohne eine Musterübereinstimmung zu verwenden?

+0

Warum schlechte Mustererkennung? –

+0

Nun könnten Sie 'Mitglieder' hinzufügen oder Funktionen verwenden ... aber diese müssten mit Pattern-Matching dekonstruieren - aber was können Sie sonst noch erwarten? – Carsten

Antwort

0

Sie können Reflexion von Microsoft.FSharp.Reflection Namespace verwenden, aber es ist viel mühsamer und langsamer.

Mustervergleich ist wahrscheinlich der einfachste Weg, Daten von diskriminierten Union zu erhalten.

(Auch Sie haben eine Liste von Things alle Mitglieder sind zufällig von Elephant Union Fall).

10

In Ihrem Beispiel, sagen Sie, dass Sie eine Liste von Elefanten haben - was in diesem Fall wahr ist - aber die Art der l ist wirklich eine Liste von Thing Werten und so kann es die beiden Elefanten und Adler enthält. Aus diesem Grund müssen Sie den Mustervergleich verwenden, um alle möglichen Fälle zu behandeln.

Wenn Sie regelmäßig eine Liste verwenden müssen, die nur Elefanten enthält, dann könnte es sinnvoll sein, eine separate Art von Elefanten zu definieren. Etwas wie:

type ElephantInfo = { Size : int } 

type Thing = 
    | Elephant of ElephantInfo 
    | Eagle 

Jetzt können Sie eine Liste von Typ erstellen list<ElephantInfo>, die nur Elefanten enthalten kann und so brauchen Sie keine Mustervergleich:

let l1 = [ {Size=1}; {Size=2} ] 
for el in l1 do printfn "%d" el.Size 

Auf der anderen Seite, wenn Sie wollen mischen Elefanten und Adler, Sie list<Thing> erstellen werden und dann Musterabgleich verwenden:

let l2 = [ Elephant {Size=1}; Eagle ] 
3

Sie dies tun könnte:

l 
|> List.collect (function Elephant x -> [x] | _ -> []) 
|> List.iter (printfn "%i") 

Drucke

1000 
1200 

Es nutzt noch Pattern-Matching, aber es ist ziemlich minimal.

+1

'List.choose (Funktion Elefant x -> Einige x | _ -> Keine)' sieht hier besser aus. – bytebuster

+0

Danke, das ist effektiv, was ich jetzt mache. –

+0

Vielleicht verwende ich DUs nicht gut. Ich verwende sie, um Befehlszeilenoptionen intern darzustellen, die der Benutzer eingegeben hat, und einer der Fälle ist "InvalidCommand as string", um einen Befehl darzustellen, den das System nicht erkennt. Etwas später im Code extrahiere ich alle InvalidCommands aus dieser Liste, damit ich sie als separaten Anwendungsfall behandeln kann. Der Code, der diese Invallid-Befehle behandelt, muss eine Musterübereinstimmung machen, um die String-Daten zu erhalten (der falsche Text, den der Benutzer in der Befehlszeile eingegeben hat) und er kann die Liste nicht nur mit InvalidCommands behandeln Fälle der DU –

1

Es gibt eine Möglichkeit, die Musterübereinstimmung in die Kopfzeile der Funktion zu platzieren (oder eine let Bindung). Es ist immer noch eine Musterübereinstimmung.

// This function takes a tuple: 
// the first argument is a Thing, 
// the second is "default" weight to be processed if the first one is NOT an Elephant 
let processElephant (Elephant weight, _ | _, weight) = 
    weight 

let [<Literal>] NON_ELEPHANT_WEIGHT = -1 

// usage: 
let totalWeight = 
    [Elephant (1000); Elephant (1200)] 
    |> List.sumBy (fun el -> processElephant(el, NON_ELEPHANT_WEIGHT)) 

This question und seine answers bieten mit mehr Details.

3

Sie natürlich die Möglichkeit haben, gehen voll Ivory Tower (® Scott Wlaschin)

Wie in etwa:

type Thing = 
| Eagle 
| Elephant of int 

type MaybeElephantBuilder() =  
member this.Bind(x, f) = 
    match x with 
    | Eagle -> 0 
    | Elephant a -> f a 

member this.Return(x) = x 

let maybeElephant = new MaybeElephantBuilder() 

let l = 
[ Elephant(1000) 
    Elephant(1200) 
    ] 

let printIt v = 
let i = 
    maybeElephant { 
    let! elephantValue = v 
    return elephantValue 
    } 
printfn "%d" i 

l |> Seq.iter printIt 

Es wird sogar das Zeug mit den Eagles in dort geworfen Griff! Nun

...

Entfernen Sie die nicht-Eagles und der Code wird fliegen ...

let l = 
[ Eagle 
    Leadon 
    Elephant(1000) 
    Eagle 
    Meisner 
    Elephant(1200) 
    Eagle 
    Felder 
    ] 

l |> Seq.iter printIt 

Aber nein. Es ist nicht nett. Es ist nicht kurz. Es ist mehr zum Spaß (wenn das!) Als alles andere. Es ist wahrscheinlich der schlimmste Missbrauch von F # -Berechnungsausdrücken auch immer!

Und Sie benötigen Muster übereinstimmen irgendwo.

Thx Scott! Und Petricek.

Berechnung Ausdruck Zoo für real! ;-)

+1

NIce erste Post. Ich freue mich auf ähnliche Dinge in den kommenden Jahren! –

+1

@RubenBartelink Nein, nicht wirklich. Jedenfalls nicht zuerst ;-) –

+0

@ user1758475 Ah, aber kannst du * beweisen * du bist user4946443: P Cmon, es muss Internet-Handle-Generator-Apps da draußen sein! –

Verwandte Themen