2017-09-04 1 views
5

Ich habe eine Liste und ich möchte ein Element entfernen, das einige Kriterien erfüllt, aber nur ein Element entfernt.F # filter nur das erste Auftreten aus der Liste

let items = [1;2;3] 

let predicate x = 
    x >= 2 

let result = items |> List.fold ... 
// result = [1;3] 

Wie erreicht man eine Methodenrückgabe mit [1; 3]?

Antwort

6

Sie eine allgemeine rekursive Funktion

let rec removeFirst predicate = function 
    | [] -> [] 
    | h :: t when predicate h -> t 
    | h :: t -> h :: removeFirst predicate t 

oder einen Schwanz rekursive man verwenden kann (wenn Sie einen Stapelüberlauf befürchten)

let removeFirst predicate list = 
    let rec loop acc = function 
     | [] -> List.rev acc 
     | h :: t when predicate h -> (List.rev acc) @ t 
     | h :: t -> loop (h :: acc) t 
    loop [] list 
+0

Guard-Klauseln können nett sein, aber in diesem Fall glaube ich, dass sie es verwirrender machen. Ich denke nur '| h :: t -> wenn Prädikat h dann (List.rev acc) @ t else Schleife (h :: acc) t 'ist klarer. – mydogisbox

+0

Ein Vorteil dieser Antwort ist, dass sie die Verarbeitung stoppt, wenn der erste übereinstimmende Wert erreicht wird, so dass viel Arbeit eingespart werden kann. – TheQuickBrownFox

+0

In der Tail-rekursive Version können Sie den '[]' - Fall ändern, um die Eingabe 'list' zurückzugeben, anstatt den Akkumulator umzukehren, was sowieso nur die umgekehrte Liste ist. – TheQuickBrownFox

3
let result = 
    items 
    |>List.scan (fun (removed, _) item -> 
     if removed then true, Some(item) //If already removed, just propagate 
     elif predicate item then true, None //If not removed but predicate matches, don't propagate 
     else false, Some(item)) //If not removed and predicate doesn't match, propagate 
     (false, None) 
    |>List.choose snd 

Der Zustand ist ein Tupel. Das erste Element ist ein boolesches Flag, das angibt, ob wir bereits ein Element aus der Liste entfernt haben. Das zweite Element ist eine Option: Einige, wenn wir den Artikel ausgeben möchten, sonst keine.

Die letzte Zeile nimmt die zweiten Elemente aus den Zuständen und für jede von ihnen emittiert den verpackten Wert (im Falle von einige) oder nichts (im Falle von keiner).

+0

Dies lief die langsamste in meinen Tests. – Soldalma

2

Hier ist eine kurze Alternative, die in meinen Tests schneller war als die anderen bisher vorgeschlagen:

let removeFirst p xs = 
    match List.tryFindIndex p xs with 
    | Some i -> List.take i xs @ List.skip (i+1) xs 
    | None -> xs 
0

Ziel für eine intuitive Lösung.

let removeAt index list = 
    let left, right = List.splitAt index list 
    left @ (List.skip 1 right) 

let removeFirst predicate list = 
    match List.tryFindIndex predicate list with 
    | Some index -> removeAt index list 
    | None -> list 

Für Leistung (lange Listen).

let removeFirst predicate list = 
    let rec finish acc rem = 
     match rem with 
     | [] -> acc 
     | x::xs -> finish (x::acc) xs 
    and find l p acc rem = 
     match rem with 
     | [] -> l 
     | x::xs -> 
      if p x then finish xs acc 
      else find l p (x::acc) xs 
    find list predicate [] list 
Verwandte Themen