2017-09-04 2 views

Répondre

6

Vous pouvez utiliser une fonction récursive générique

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

ou une récursif de la queue (si vous craignez un débordement de pile)

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

Les clauses de garde peuvent être sympa, mais dans ce cas je pense qu'elles les rendent plus confuses. Je pense juste '| h :: t -> si prédicat h alors (List.rev acc) @ t else boucle (h :: acc) t' est plus clair. – mydogisbox

+0

Un avantage de cette réponse est qu'elle arrête le traitement lorsque la première valeur correspondante est atteinte, ce qui économise beaucoup de travail. – TheQuickBrownFox

+0

Dans la version récursive en queue, vous pouvez changer le cas '[]' pour retourner l'entrée 'list' au lieu d'inverser l'accumulateur, qui est de toute façon la liste inversée. – 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 

L'état est un tuple. Le premier élément est un drapeau booléen indiquant si nous avons déjà supprimé un élément de la liste. Le deuxième élément est une option: certains lorsque nous voulons émettre l'élément, aucun autrement.

La dernière ligne prend les deuxièmes éléments des états et pour chacun d'eux émet la valeur enveloppée (dans le cas de Some) ou ne fait rien (dans le cas de None).

+0

Ceci a fonctionné le plus lentement dans mes tests. – Soldalma

2

Voici une courte alternative, qui dans mes tests a été plus rapide que les autres proposés jusqu'à présent:

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

Viser une solution intuitive.

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 

Pour les performances (listes longues).

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