2016-05-29 25 views
7

Recentemente ho preso una deviazione in Clojure da F # e mi sono imbattuto in una macro chiamata cond. Ecco un esempio sull'uso:clojure like cond in F #

(cond 
(= target (nth arr mid)) mid 
(< target (nth arr mid)) (search left (- mid 1)) 
(> target (nth arr mid)) (search (+ mid 1) right) 
(= left right) -1) 

Questo significa quanto segue in pseudo codice:

if target == arr.[mid] then return mid 
if target < arr.[mid] then return (call search(left, mid-1)) 
if target > arr.[mid] then return (call search(mid+1, right)) 
if left == right then return -1 

Questo è solo un esempio di una ricerca binaria nel caso in cui vi sono state chiedendo che cosa è di destra e sinistra metà, ma non molto importante.

Ho cercato di trovare qualcosa di simile in F # ma non ho potuto, quindi ho deciso di provare a scriverlo da solo. Ho finito con qualcosa di simile:

type condition = bool * int 

let cond (conds: condition seq) = 
    conds |> Seq.pick(fun c -> if fst c then Some (snd c) else None) 

cond [| ((=) target arr.[mid], mid) 
     ((=) left right, -1) 
     ((<) target arr.[mid], recSrch left (mid-1)) 
     ((>) target arr.[mid], recSrch (mid+1) right) 
     |] 

Il problema qui è che voglio usarlo in una funzione ricorsiva, e perché recSrch sinistra (mid-1) sono in corso di valutazione da subito in modo da finire in un ciclo infinito. Voglio che venga valutato solo quando la condizione è valida. Inoltre, il modulo non è ancora pulito come in Clojure.

Qualche idea come potrei migliorare?

+0

Per quelli di noi che non conoscono Clojure tua domanda è estremamente opaco. – ildjarn

+0

Ci scusiamo per questo, ho aggiunto qualche pseudo codice per chiarire come si comporta il codice clojure. –

+0

Puoi pubblicare il codice completo, tra cui '' target'', '' left'', 'right''. – Gustavo

risposta

4

È necessario un modo per rendere ponderati i corpi delle condizioni. Ecco un modo di farlo, rendendo il corpo di una funzione da chiamare come eseguire iterazioni attraverso la sequenza di condizioni:

type condition = bool * (unit -> int) 

let cond (conds: condition seq) = 
    conds 
    |> Seq.pick(fun c -> 
     let pred, func = c 
     if pred then Some (func()) else None) 

cond [| ((=) target arr.[mid], fun() -> mid) 
     ((=) left right, fun() -> -1) 
     ((<) target arr.[mid], fun() -> recSrch left (mid-1)) 
     ((>) target arr.[mid], fun() -> recSrch (mid+1) right) 
     |] 

Nota che ha senso solo usare qualcosa di simile se la vostra lista di condizioni si suppone essere dinamico

Per le condizioni statiche, è disponibile la corrispondenza del modello con le clausole when. Questo ti dà una bella sintassi idiomatica e generalmente controlla le tue partite in modo esaustivo al momento della compilazione, quindi ne vale la pena.

let result = 
    match target with 
    | _ when target = arr.[mid] -> mid 
    | _ when left = right -> -1 
    | _ when target < arr.[mid] -> recSrch left (mid-1)  
    | _ when target > arr.[mid] -> recSrch (mid+1) right 
    | _ -> failwith "you need this case if the compiler can't figure if your matches are exhaustive" 

Diventa più bello se lo si avvolge come un motivo attivo.

4

in F #, c'è un costrutto del linguaggio per quel tipo di espressione:

if target = arr.[mid] then mid 
elif target < arr.[mid] then call (search(left, mid-1)) 
elif target > arr.[mid] then call (search(mid+1, right)) 
else -1 

... o, in generale: ho vista di Clojure cond macro come l'equivalente di pattern matching o if/elif/else blocchi. Ovviamente non sono esattamente gli stessi, perché Clojure è interpretato e digitato dinamicamente, mentre F # è compilato e tipizzato staticamente.

+0

La principale differenza con l'espressione di corrispondenza e la corrispondenza sta valutando su una condizione, mentre con cond si possono anche testare condizioni diverse. –

+0

@PeterV Sì, questo è il motivo per cui ho fornito l'esempio 'if/elif/else' qui, invece della corrispondenza del modello. –

+0

È vero, questo risolve il problema. Quello che volevo davvero è creare un costrutto simile alla macro cond in clojure, soprattutto perché mi piace quanto sia compatto. Potresti discutere se ha senso o meno. È per lo più solo codice ping-pong. Mi piacciono questi, anche se penso di poter imparare molto sulle lingue. –

7

Questo è uno schizzo utilizzando match che penso sia molto vicino al clojure.

Definisce Cond come un modello attivo parziale che prende la funzione di test come un argomento

let (|Cond|_|) f arg = 
    if f arg then Some() else None;; 

utilizzo è abbastanza facile

match 1 with 
|Cond ((=) 5) _ -> printfn "unlikely" 
| _ -> printfn "likely";; 
+0

Questo è un approccio davvero interessante. – scrwtp