2009-11-10 20 views
0

Ce code divise une liste en deux parties par un prédicat qui prend une liste et renvoie false au moment de la division.Comment optimiser la division en F # de plus?

let split pred ys = 
    let rec split' l r = 
     match r with 
     | [] -> [] 
     | x::xs -> if pred (x::l) then x::(split' (x::l) xs) else [] 
    let res = split' [] ys 
    let last = ys |> Seq.skip (Seq.length res) |> Seq.toList 
    (res, last) 

Est-ce que quelqu'un connaît des façons plus optimales et plus simples de le faire en F #?

+0

Pouvez-vous préciser ce que pred fait? Si cela nécessite de vérifier tous les éléments ensemble et pas un par un, il n'y a aucun moyen de contourner O (n^2). – gradbot

+0

Essayez-vous de diviser la liste en deux lorsque vous arrivez à la première valeur qui renvoie true à partir de votre prédicat? – gradbot

+0

Oui, vous avez raison O (n^2) est le meilleur pour cette situation. A propos de predicate: il s'agit du point de partage, lorsque le prédicat renvoie false. Mais ce n'est pas si important car il est facile d'inverser le résultat du prédicat. –

Répondre

2

Eh bien, vous pouvez le rendre récursif, mais vous devez inverser la liste. Vous ne voudriez pas le plier car il peut sortir de la boucle récursive à tout moment. J'ai fait un peu de test et l'inversion de la liste est plus que compensée par la récursivité de la queue.

// val pred : ('a list -> bool) 
let split pred xs = 
    let rec split' l xs ys = 
     match xs with 
     | [] -> [], ys 
     | x::xs -> if pred (x::l) then (split' (x::l) xs (x::ys)) else x::xs, ys 
    let last, res = split' [] xs [] 
    (res |> List.rev, last) 

Une version similaire à celle de Brian qui est récursive en queue et prend un seul prédicat de valeur.

// val pred : ('a -> bool) 
let split pred xs = 
    let rec split' xs ys = 
     match xs with 
     | [] -> [], ys 
     | x::xs -> if pred x then (split' xs (x::ys)) else (x::xs), ys 
    let last, res = split' xs [] 
    (res |> List.rev, last) 

Ceci est différent de la partition de fonction de bibliothèque en ce qu'elle arrête de prendre des éléments dès que le prédicat retourne faux peu comme Seq.takeWhile.

// library function 
let x, y = List.partition (fun x -> x < 5) li 
printfn "%A" x // [1; 3; 2; 4] 
printfn "%A" y // [5; 7; 6; 8] 

let x, y = split (fun x -> x < 5) li 
printfn "%A" x // [1; 3] 
printfn "%A" y // [5; 7; 2; 4; 6; 8] 
+0

takeWhile ont un prédicat ('a -> bool), mais j'ai vraiment besoin (' a list -> bool) comme prédicat. Et l'ordre d'entrée doit également être préservé. –

+0

Peut être ce "Seq.skip" pourrait être sauté et directement pour dériver le résultat à droite. Je penserai plus à propos de. –

+1

J'ai supprimé Seq.skip de mon premier exemple de code. Il est similaire au second exemple mais prend toujours pred: ('une liste -> bool) – gradbot

0

Non récursive, mais:

let rec Break pred list = 
    match list with 
    | [] -> [],[] 
    | x::xs when pred x -> 
     let a,b = Break pred xs 
     x::a, b 
    | x::xs -> [x], xs 

let li = [1; 3; 5; 7; 2; 4; 6; 8] 
let a, b = Break (fun x -> x < 5) li  
printfn "%A" a // [1; 3; 5] 
printfn "%A" b // [7; 2; 4; 6; 8] 

// Also note this library function 
let x, y = List.partition (fun x -> x < 5) li 
printfn "%A" x // [1; 3; 2; 4] 
printfn "%A" y // [5; 7; 6; 8] 
+0

Ya, je ne peux pas dire s'il veut tester la liste en morceaux avec son prédicat ou un par un. – gradbot

+0

L'ordre de saisie doit être conservé et le prédicat doit prendre une liste comme pour [1..10] -> [1], [1; 2], [1; 2, 3], etc. –