2015-02-14 11 views
8

In OCaml, se devo scrivere una funzione utilizzando molti if-then-else, di seguito è la mia soluzione stupida e brutta.Scrivi codice nidissimo if-then-else nidificato abbastanza in OCaml?

let foo() = 
    let a1 = ... in 
    if (a1) then 
    result1 
    else 
    let a2 = ... in 
    if (a2) then 
     result2 
    else 
     let a3 = ... in 
     if (a3) then 
     result3 
     else 
     let a4 = ... in 
     if (a4) then 
      result4 
     else 
      result5. 

Come abbellire il codice sopra? Mi piace lo stile Java C/C++ & che usa "return" per salvare il rientro della prossima istruzione if. Posso fare la stessa cosa con OCaml?

int foo() = { 
    bool a1 = ...; 
    if (a1) 
    return result1; 

    bool a2 = ...; 
    if (a2) 
    return result2; 

    bool a3 = ...; 
    if (a3) 
    return result3; 

    bool a4 = ...; 
    if (a4) 
    return result4; 

    return result5; 
} 
+1

OCaml non ha un'istruzione 'return':' se' è probabilmente la strada da percorrere. Se ti senti disperata, puoi dividere le cose in un'altra funzione. – gsg

+0

Non vi è alcun motivo per indentare il primo esempio in una scala. Diversi editor lo indenterebbero verticalmente –

+0

Alcune librerie monad in Ocaml definiscono una funzione 'return' –

risposta

3

I if costrutti sintattici infatti non funzionano allo stesso modo in C e OCaml. In C, i moduli di sintassi if sono istruzioni, in OCaml sono espressioni. Il più vicino si ottiene in C a OCamlif è l'operatore ternario ?:. Se provi a riscrivere il tuo codice C utilizzando questo operatore invece di if, dovrai affrontare la stessa sfida. Ciò non significa che sia impossibile, tuttavia, poiché altre risposte ti forniscono soluzioni.

Il più semplice, che funziona in entrambe le lingue, è quello di tagliare il corpo della funzione in diverse funzioni secondarie (*), e utilizzare continuazioni:

let rec foo() = 
    let a1 = … (* computation *) in 
    if a1 
    then result1 
    else foo2() 
and foo2() = 
    let a2 = … in 
    if a2 
    then result1 
    else foo3() 
and foo3() = … (* etc *) 

Può ancora essere un po 'ingombrante quando si scrive metodi oggetto, ma è sempre possibile utilizzare le funzioni interne per riottenere il "saldo indentazione" nell'ambito del metodo.

Si noti inoltre che la parola chiave rec è lì con l'unico scopo di consentire a ciascuna continuazione di seguire il proprio chiamante nel layout sorgente, non c'è una vera ricorsione qui.


(*): anche @ggs lo ha menzionato nei commenti.

5

Non c'è return dichiarazione in OCaml, anche se è possibile emulare uno con l'aiuto di eccezioni:

exception Ret of t 

let my_ret x = raise (Ret x) 

let foo() = 
try 
    let a1 = ... in 
    if a1 then my_ret result1; 
    let a2 = ... in 
    if a2 then my_ret result2; 
    ... 
with Ret x -> x 

Un'altra soluzione utile potrebbe essere quella di utilizzare la valutazione pigra:

let foo() = 
let a1 = lazy ... 
and a2 = lazy ... 
and a3 = lazy ... 
in 
match a1, a2, a3 with 
| lazy true, _, _ -> result1 
| _, lazy true, _ -> result2 
| _, _, lazy true -> result3 
| _, _, _ -> result4 

Questo è uno degli esempi che usano pigro, probabilmente c'è un modo più conciso di esprimere il tuo calcolo.

5

libreria di base fornisce una funzione with_return, che ti permette di fare esiste un non locale da funzioni:

open Core_kernel.Std 
let foo() = with_return (fun goto -> 
    if a1 then goto.return 1; 
    if a2 then goto.return 2; 
    if a3 then goto.return 3; 
    if a4 then goto.return 4; 
    if a5 then goto.return 5; 
    return 6) 

Ma in generale è meglio usare pattern-matching o di ripensare il proprio codice. Ad esempio, se si dispone di un elenco di predicati, e seconda di ciò che predicato è vero che si desidera restituire un valore, questo significa che è possibile codificare questo come una ricerca in qualche struttura di mappatura:

let foo() = [ 
    clause1, expr1; 
    clause2, expr2; 
    clause3, expr3; 
] |> List.Assoc.find true 
    |> Option.value ~default:expr4 

Naturalmente, in in questo caso non hai una valutazione di cortocircuito. È possibile risolvere questo problema con una valutazione lenta o con thunk. Ma a meno che i tuoi calcoli siano davvero pesanti o producano effetti collaterali, non ne vale la pena.

0

A differenza delle espressioni if, le clausole match si estendono fino alla fine della funzione anche se contengono più istruzioni, senza bisogno di parentesi. Così si può fare:

let foo() = 
    match ... with 
    | true -> result1 
    | false -> 
    match ... with 
    | true -> result2 
    | false -> 
    match ... with 
    | true -> result3 
    | false -> 
    match ... with 
    | true -> result4 
    | false -> result5 

Non hai mostrare dove result1 viene da nel tuo esempio, in modo da non poter essere sicuri, ma si potrebbe trovare meglio avere la ... restituire un opzione con il risultato piuttosto di un bool, ad es

let foo() = 
    match ... with 
    | Some result1 -> result1 
    | None -> 
    ...