2013-06-13 12 views
7

Supponiamo di avere una funzione null in haskell, che viene usata più volte nel codice. Viene sempre valutato solo una volta? Ho già provato il seguente codice:Valutazione delle funzioni nulle in Haskell

sayHello :: Int 
sayHello = unsafePerformIO $ do 
    putStr "Hello" 
    return 42 

test :: Int -> [Int] 
test 0 = [] 
test n = (sayHello:(test (n-1))) 

Quando chiamo prova 10, scrive "Ciao" ony una volta, quindi è che indica il risultato della funzione viene memorizzato dopo la prima valutazione. La mia domanda è, è garantita? Otterrò lo stesso risultato su diversi compilatori?

Modifica Il motivo per cui ho usato unsafePerformIO è per verificare se sayHello viene valutato più di una volta. Non lo uso nel mio programma. Normalmente mi aspetto che Hello abbia lo stesso risultato ogni volta che viene valutato. Ma è un'operazione che richiede tempo, così ho voluto sapere se è stato possibile accedere in questo modo, o se deve essere passato come argomento dovunque ce n'è bisogno per assicurarsi che non viene valutato più volte, vale a dire:

test _ 0 = [] 
test s n = (s:(test (n-1))) 
... 
test sayHello 10 

Secondo le risposte dovrebbe essere usato.

risposta

17

Non esiste una funzione null. Una funzione in Haskell ha esattamente un argomento e ha sempre il tipo ... -> .... sayHello è un valore - un Int - ma non una funzione. Vedi this article per ulteriori informazioni.

Sulle garanzie: No, non si ottiene alcuna garanzia. Il rapporto Haskell specifica che Haskell non è severo, quindi sai a quale valore le cose alla fine si riducono, ma non una particolare strategia di valutazione. La strategia di valutazione in genere utilizzata da GHC è lazy evaluation, ovvero valutazione non rigida con condivisione, ma non offre forti garanzie in merito: l'ottimizzatore potrebbe mescolare il codice in modo che le cose vengano valutate più di una volta.

Esistono anche varie eccezioni: ad esempio, foo :: Num a => a è polimorfico, quindi probabilmente non verrà condiviso (è compilato per una funzione effettiva). A volte un valore puro può essere valutato da più di un thread contemporaneamente (ciò non accadrà in questo caso perché unsafePerformIO utilizza esplicitamente noDuplicate per evitarlo). Quindi, quando si programma, è generalmente possibile aspettarsi pigrizia, ma se si desidera qualsiasi tipo di garanzie dovrete essere molto attenti. Il report in sé non ti darà davvero nulla su come viene valutato il tuo programma.

unsafePerformIO ti dà ancora meno garanzie, naturalmente. C'è un motivo per cui è chiamato "non sicuro".

5

Le funzioni senza argomenti di livello superiore come sayHello sono denominate moduli di domanda costanti e vengono sempre memorizzate (almeno in GHC - vedere http://www.haskell.org/ghc/docs/7.2.1/html/users_guide/profiling.html). Dovresti ricorrere a trucchi come passare argomenti fittizi e trasformare le ottimizzazioni in non condividere un CAF a livello globale.

Edit: citazione dal link qui sopra -

Haskell è un linguaggio pigro, e certe espressioni sono sempre e solo valutate una volta. Ad esempio, se scriviamo: x = nfib 25 quindi x verrà valutato una sola volta (se non del tutto) e le richieste successive di per x visualizzeranno immediatamente il risultato memorizzato nella cache. La definizione x è chiamata CAF (modulo di domanda costante), perché non ha argomenti.

1

Se si vuole "ciao" n volte a stampa, è necessario rimuovere la unsafePermformIO, in modo che il tempo di esecuzione sarà sapere che non può ottimizzare le chiamate via ripetuti a putStr. Non sono chiaro se si desidera restituire la lista di int, quindi ho scritto due versioni di test, una delle quali restituisce(), una [Int].

sayHello2 :: IO Int 
sayHello2 = do 
    putStr "Hello" 
    return 42 

test2 :: Int -> IO() 
test2 0 = return() 
test2 n = do 
    sayHello2 
    test2 (n-1) 

test3 :: Int -> IO [Int] 
test3 0 = return [] 
test3 n = do 
    r <- sayHello2 
    l <- test3 (n-1) 
    return $ r:l 
Problemi correlati