2012-01-24 10 views
9

Sto cercando di integrare numericamente una funzione in Haskell usando la regola trapezoidale, restituendo un anti-derivato che accetta gli argomenti a, b, per i punti finali dell'intervallo da integrare.Haskell: clausola where che fa riferimento a variabili associate in lambda

integrate :: (Float -> Float) -> (Float -> Float -> Float) 

integrate f 
    = \ a b -> d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b) 
    where 
     d = (b - a)/n 
     n = 1000 

In quanto sopra, io uso

n - for the number of subintervals 
d - for the width of each subinterval 

Questo quasi funziona, tranne che per gli argomenti legati a, b nel lambda. Ricevo il seguente messaggio di errore :

Not in scope: `b' 
Not in scope: `a' 

posso capire che l'ambito di a, b è limitato solo a questo espressione lambda, ma c'è una soluzione alternativa in Haskell in modo che non devo scrivere (ba)/n per ogni occorrenza di d in quanto sopra?

+4

TIL: Non è possibile utilizzare 'WHERE' con lambda [lasciare vs dove] (http://www.haskell.org/haskellwiki/Let_vs._Where). Vedi anche [dove la clausola 'where' è utile in Haskell] (http://stackoverflow.com/questions/6032183/where-does-the-where-clause-come-in-handy-in-haskell) – rampion

+0

grazie a tutti quelli che hanno risposto. Non mi ero reso conto che questo problema porta direttamente a una ben nota let vs dove la discussione. Voglio anche ringraziare coloro che hanno suggerito di scrivere la funzione come: integrare f a b = ... Questa è una soluzione carina e succinta. – Bylextor

risposta

1

prova:

integrate f a b = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b) 
    where 
     d = (b - a)/n 
     n = 1000 
16

stai pensando è necessario restituire una funzione che prende due Float s e restituisce un Float, ma in realtà che non è diverso a prendere due Float argomenti extra nella funzione integrate e l'utilizzo currying (vale a dire solo non fornire loro e il tipo di ritorno sarà Float -> Float -> Float).

in modo da poter riscrivere la funzione come questa

integrate :: (Float -> Float) -> Float -> Float -> Float 

integrate f a b 
    = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b) 
    where 
     d = (b - a)/n 
     n = 1000 

Oppure si potrebbe usare let ... in invece di where:

integrate f 
    = \a b -> 
     let d = (b - a/n) 
      n = 1000 
     in d * sum [ f (a + d * k) | k <- [0..n] ] - d/2.0 * (f a + f b) 
+2

FWIW, Matthew ha omesso le parentesi nella firma del tipo, ma non è necessario. È possibile mantenere la firma del tipo lo stesso e scrivere il corpo della funzione in questo modo nuovo. Le due firme sono * esattamente * equivalenti. – luqui

+0

Ben individuato. Intendevo dirlo quando stavo scrivendo la risposta, ma devo aver dimenticato. –

+0

Grazie. Riscrivere la funzione come integrata f a b = ... è un'ottima soluzione. – Bylextor

4

Certo.

integrate f a b = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b) 
    where 
     d = (b - a)/n 
     n = 1000 
2

Se ti ostini a dove:

integrate f = \a b -> case() of 
    () -> d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b) 
      where 
       d = (b - a)/n 
       n = 1000 

sembra piuttosto bello, non è vero? Per rendere il caso sembrare un po 'più motivati:

integrate f = \a b -> case (f a + f b) of 
    fs -> d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * fs 
      where 
       d = (b - a)/n 
       n = 1000 
+0

Molto intelligente! Grazie! – Bylextor

3

Hai un sacco di work-around.

Se non si conosce alcuna sintassi impegnativo se le espressioni lambda si può fare questo (che io amo di più per la sua bellezza teorico, ma non usare mai a causa della sua bruttezza sintattica):

integrate f 
    = \a b -> (\d -> d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)) 
      ((b - a)/n) 
    where 
     n = 1000 

Se ti piace definizioni e solo sapere where -syntax si può fare questo:

integrate f = go 
    where 
    n = 1000 
    go a b = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b) 
     where 
     d = (b - a)/n 

Se si conosce anche let -syntax, si può fare questo:

integrate f = 
    \a b -> let d = (b - a)/n 
      in d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b) 
    where 
    n = 1000 

Infine, se vi ricordate che a -> (b -> c -> d) è lo stesso di a -> b -> c -> d, si può fare l'ovvio:

integrate f a b = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b) 
    where 
    n = 1000 
    d = (b - a)/n 
+1

C'è un altro motivo per non usare la prima opzione: lambda non ha lasciato il polimorfismo. – luqui

+0

Grazie. L'opzione let è probabilmente la soluzione più semplice per il mio problema di variabile diretta. – Bylextor

+0

Puoi dirmi qualcosa in più su "go"? Ho provato a cercarlo, ma è molto difficile trovare informazioni su "go" in Haskell, perché i motori di ricerca lo confondono con la solita parola inglese "go", e "go" non appare nella maggior parte degli indici di libri. – Bylextor