2009-12-30 3 views
6

Ho una semplice funzione di quick sort f # definito come:Quicksort in F # - sintassi domanda

let rec qsort(xs:List<int>) = 

let smaller = xs |> List.filter(fun e -> e < xs.Head) 
let larger = xs |> List.filter(fun e -> e > xs.Head) 
match xs with 
| [] -> [] 
| _ -> qsort(smaller)@[xs.Head]@qsort(larger) 

C'è un modo in F # per scrivere più come Haskell:

qsort  :: [Int] -> [Int] 
qsort []  = [] 
qsort (x:xs) = 
qsort smaller ++ [x] ++ qsort larger 
where 
    smaller = [a | a <- xs, a <= x] 
    larger = [b | b <- xs, b >= x] 

So che la Algoritmo f # manca un < = e> =. La domanda riguarda più la sintassi/la leggibilità.

Grazie.

+1

FWIW , questo è * non * quicksort. In particolare, l'ultima volta che ho messo a confronto Haskell era più lento di 6.000 volte di un vero quicksort scritto in F #. Per informazioni sul vero algoritmo di quicksort (e non sulla versione bastardizzata delle comunità Haskell che manca completamente il punto di essere veloce!), Leggi l'articolo originale di Hoare del 1961: http://comjnl.oxfordjournals.org/cgi/content/short/5/1/10 –

risposta

9

volete che il vostro clausola secondo match di essere x :: xs, e di utilizzare il @ (aggiungere) Operatore in cui il tuo esempio Haskell utilizza ++:

let rec qsort xs = 
    match xs with 
    | [] -> [] 
    | x :: xs -> 
     let smaller = qsort (xs |> List.filter(fun e -> e <= x)) 
     let larger = qsort (xs |> List.filter(fun e -> e > x)) 
     smaller @ [x] @ larger 

Non è proprio la stessa definizione Haskell dalla sintassi dei casi , ma si spera abbastanza simile per te!

+1

Nota secondaria: penso che 'smaller @ x :: larger', piuttosto che' smaller @ x @ larger', sia meno carina ma un po 'più veloce nella pratica poiché evita di copiare nuovamente la lista dei target. – Juliet

+5

"e> = x" dovrebbe essere "e> x" altrimenti si finirà con la duplicazione degli elementi. – rysama

+0

@RodYan xs non contiene x, quindi non stai duplicando nulla. – curial

2

Haskell 'dove' la sintassi, che consente di utilizzare il nome di una funzione prima della sua definizione, tipo di mappe a F # 'let rec ... e'

let qsort xs = 
    let rec sort xs = 
     match ls with 
     |[] -> .... 
     |h::t -> (smaller t) @ h @ (larger t) 

    and smaller ls = //the 'and' lets you define the 
         // function after where it is used, 
         // like with 'where' in haskell 
     ... define smaller in terms of sort 
    and larger ls = 
     ... same 

    sort xs 
13

Questo è il più 'Haskellian' modo mi viene in mente, l'unica cosa che manca è essere in grado di dichiarare più piccolo/grande come un 'dove' la clausola:

let rec qsort:int list -> int list = function 
    | [] -> [] 
    | x::xs -> let smaller = [for a in xs do if a<=x then yield a] 
       let larger = [for b in xs do if b>x then yield b] 
       qsort smaller @ [x] @ qsort larger 

io so che non è parte della tua domanda, ma mi piacerebbe usare List.partition di dividere il elencare in caratteri più piccoli/più grandi in un'unica passata:

let rec qsort = function 
    | [] -> [] 
    | x::xs -> let smaller,larger = List.partition (fun y -> y<=x) xs 
       qsort smaller @ [x] @ qsort larger 
+0

Puntelli per l'aggiunta del suggerimento List.partition – Daniel

5

Questo sembra essere il più conciso quanto si può ottenere (che unisce le idee da altre risposte, e l'utilizzo di strigliare per gli operatori):

let rec qsort = function 
| [] -> [] 
| (x:int) :: xs -> 
    let smaller = List.filter ((>=) x) xs 
    let larger = List.filter ((<) x) xs 
    qsort smaller @ [x] @ qsort larger 
+0

'let smaller = List.filter ((<=) x) xs' btw. restituisce non gli elementi che sono più piccoli, restituisce l'elemento che è maggiore di x. Per ottenere tutti gli elementi più piccoli x devi scrivere 'fun y -> y <= x' ma il collegamento che hai usato si espande in' fun y -> x <= y' Nota che 'x' nella tua versione è il primo argomento non il secondo! Inoltre non puoi usare '> =' e '<=' allo stesso tempo. 'qsort [5; 1; 5; 3]' altrimenti restituisce '[1; 3; 5; 5; 5]', quasi tutti sembrano sbagliarsi. Per questo motivo, in questo caso non raccomanderei l'applicazione parziale con gli operatori. –

2

... Oppure si potrebbe fare una coda qsort ricorsiva che utilizzano CPS:

let qSort lst = 
    let rec qs l cont = 
     match l with 
     | []  -> cont [] 
     | (x::xs) -> qs (List.filter (fun e -> e <= x) xs) (fun smaller -> 
        qs (List.filter (fun e -> e > x) xs) (fun larger -> 
         smaller @ (x :: larger) |> cont)) 
    qs lst id 
1
let rec QuickSort l = 
     match l with 
     | [] -> [] 
     | _ -> QuickSort([for e in l do if e < (List.head l) then yield e]) @[(List.head l)]@ QuickSort([for e in l do if e > (List.head l) then yield e]) 
1

non dimenticate che lista ha un metodo di partizione, quindi

let rec quicksort ls = 
    match ls with 
    | [] -> [] 
    | h :: t -> let fore, aft = List.partition (fun i -> i < h) t 
       (quicksort fore) @ (h :: quicksort aft) 
0

Ho fatto alcune analisi degli algoritmi di ordinamento in F # qualche anno fa in uno stile molto imperativo; Stavo cercando di battere l'implementazione di magazzino .NET e sono riuscito a farlo here. Sono andato a dare la seguente risposta a me stesso oggi, ma FPish non mi permetterà di creare un account. Argh! Devo rendere il mio post da qualche parte, e qui è buono come ovunque, lol ...

Durante la lettura di "Learn You a Haskell per Great Good" ieri, l'autore ha creato un esempio per implementare quicksort. La descrizione era abbastanza chiara e ancor prima di arrivare al codice di esempio, una elegante soluzione ricorsiva (in Haskell) mi è venuta in mente. Immagino che non abbia mai avuto una sensazione intuitiva per come quicksort fa la sua cosa, perché la soluzione banale è abbastanza facile, se non molto efficiente.

Ecco la mia versione in F #:

let rec quicksort = function 
    | [] -> [] 
    | pivot :: xs -> 
     (left pivot xs) @ pivot :: (right pivot xs) 
and left pivot xs = quicksort [ for x in xs do if x <= pivot then yield x ] 
and right pivot xs = quicksort [ for x in xs do if x > pivot then yield x ] 

E, l'equivalente Haskell (mi piace questo ... pulita!):

quicksort :: Ord a => [a] -> [a] 
quicksort [] = [] 
quicksort (pivot : xs) = 
    left ++ pivot : right 
    where 
     left = quicksort [ x | x <- xs, x <= pivot ] 
     right = quicksort [ x | x <- xs, x > pivot ] 

Per sorrisi, ecco un altro F # versione (per lo più tail-ricorsiva) che è circa 2 volte la velocità della versione banale. Non hanno preso la briga di tempo questo contro il mio post originale, però, quindi nessuna idea come impila fino alla versione mutabili nel mio OP sul FPish.net (FSHub) da qualche anno fa ...

let rec quicksort' xs = 
    let rec aux pivot left right = function 
     | [] -> (quicksort' left) @ pivot :: (quicksort' right) 
     | x :: xs -> 
      if x <= pivot then 
       aux pivot (x :: left) right xs 
      else 
       aux pivot left (x::right) xs 
    match xs with 
    | [] -> [] 
    | x :: xs -> aux x [] [] xs