2016-09-22 1 views
4

Update: Ich habe eine UserVoice-Anfrage dafür erstellt: Expand on the Cardinality functions for Seq.Nicht-werfende Version von Seq.exactlyOne zum Testen auf eine Singleton-Sequenz

Ich brauche die Funktionalität von Seq.exactlyOne, aber mit einigen/keine Semantik. Mit anderen Worten, ich brauche entweder Seq.head, oder, wenn die Sequenz leer ist oder mehr als ein Element enthält, brauche ich nichts. Mit Seq.exactlyOne werfen Sie in solchen Fällen.

Ich glaube nicht, dass es eine eingebaute Möglichkeit gibt, dies zu erreichen (obwohl es so trivial klingt, dass man erwarten würde, dass es ein Gegenstück für Seq.singleton gibt). Ich kam mit dieser, aber es fühlt sich gewunden:

let trySingleton sq = 
    match Seq.isEmpty sq with 
    | true -> None 
    | false -> 
     match sq |> Seq.indexed |> Seq.tryFind (fst >> ((=) 1)) with 
     | Some _ -> None 
     | None -> Seq.exactlyOne sq |> Some 

Gibt:

> trySingleton [|1;2;3|];; 
val it : int option = None 
> trySingleton Seq.empty<int>;; 
val it : int option = None 
> trySingleton [|1|];; 
val it : int option = Some 1 

Gibt es eine einfache oder sogar eine integrierte Möglichkeit? Ich könnte versuchen/auf Seq.exactlyOne fangen, aber das ist Business-Logik um Ausnahmen zu bauen, würde ich lieber nicht (und es ist teuer).

UPDATE: war mir nicht bewusst, der Seq.tryItem Funktion, die dies einfacher machen würde:

let trySingleton sq = 
    match sq |> Seq.tryItem 1 with 
    | Some _ -> None 
    | None -> Seq.tryHead sq 

(besser, aber es fühlt sich immer noch ziemlich unbeholfen)

+1

BTW können Sie, schlage 'tryExactlyOne' als Ergänzung zur Sprache vor: https://fslang.uservoice.com/forums/245727-f-language Ich würde den Vorschlag selbst hinzufügen, aber ich habe keine Stimmen in diesem Forum. –

+0

@MarkSeemann: fertig, wie [hier zu sehen] (https://fslang.uservoice.com/forums/245727-f-language/suggestions/16308904-expand-on-cardinality-functions-seq-exactlyone-wi) , tx für den Vorschlag. – Abel

Antwort

6

Warum das nicht nähern Problem, indem Sie den Enumerator unbedingt behandeln?

let trySingleton' (xs : seq<_>) = 
    use en = xs.GetEnumerator() 
    if en.MoveNext() then 
     let res = en.Current 
     if en.MoveNext() then None 
     else Some res 
    else None 

trySingleton' Seq.empty<int> // None 
trySingleton' [1]    // Some 1 
trySingleton' [1;2]    // None 
+0

Es ist nicht "einfacher", aber es fühlt sich "sauberer" an. Beide Antworten zeigen, dass es keine triviale (dh Kernbibliothek) Vorgehensweise gibt, die ich übersehen habe. Eher willkürlich zwischen den beiden Lösungen, werde ich dieses für seine Kreativität und Sauberkeit akzeptieren, danke :). – Abel

3

Ich bin mir nicht bewusst, eine integrierte Funktion für diese, aber hier ist eine alternative Möglichkeit, es zu implementieren:

let tryExactlyOne xs = 
    match xs |> Seq.truncate 2 |> Seq.toList with 
    | [x] -> Some x 
    | _ -> None 

FSI Demo:

> [42] |> List.toSeq |> tryExactlyOne;; 
val it : int option = Some 42 

> [42; 1337] |> List.toSeq |> tryExactlyOne;; 
val it : int option = None 

> Seq.empty<int> |> tryExactlyOne;; 
val it : int option = None 
+0

Mir war nicht bewusst, dass 'truncate' nicht geworfen hat, aber die Docs sagen es deutlich (" höchstens X-Items). Nette Lösung! Nicht sicher, ob die 'Seq.toList' sich nachteilig auf die Performance auswirken wird, aber Da dies nur eine Liste mit 0, 1 oder 2 Elementen ist, würde ich erwarten, dass sie wenig Einfluss hat. – Abel

+0

@Abel Du musst versuchen, zweimal zu iterieren, um festzustellen, ob das Element ein Singleton ist Ich glaube nicht, dass Sie es effizienter machen können ("Seq.truncate" ist faul). Sowohl kaefers als auch Ihre eigene Lösung durchlaufen ebenfalls zweimal. –

+0

Ja, das ist wahr. Obwohl Ihre und meine Lösung zweimal auf Abruf iterieren (um Ich denke, dass kaefers nicht zweimal beim Aufruf iteriert, sondern den Iterationszeiger zurücksetzt, so dass die zweite Iteration nur im mehr als einem Szenario vorkommt. Daher denke ich, dass seine Lösung "sauberer" ist. – Abel

Verwandte Themen