2013-01-04 13 views
19

Sono relativamente nuovo per Haskell e sto cercando di capire come azioni diverse possono essere eseguite in sequenza usando la notazione. In particolare, sto scrivendo un programma per riferimento un algoritmo (una funzione)Come forzare la valutazione in Haskell?

foo :: [String] -> [String] 

A questo proposito mi piacerebbe scrivere una funzione come

import System.CPUTime 

benchmark :: [String] -> IO Integer 
benchmark inputList = do 
         start <- getCPUTime 
         let r = foo inputList 
         end <- getCPUTime 
         return (end - start) -- Possible conversion needed. 

L'ultima riga potrebbe aver bisogno di una conversione (ad esempio in millisecondi) ma questo non è l'argomento di questa domanda.

È questo il modo corretto di misurare il tempo necessario per calcolare la funzione foo su qualche argomento listaLista?

In altre parole, l'espressione foo inputList verrà completamente ridotta prima che venga eseguita l'azione end <- getCPUTime? O sarà r legato solo al thunk foo inputList?

Più in generale, come posso garantire che un'espressione sia completamente valutata prima che venga eseguita un'azione?


Questa domanda è stato chiesto un paio di mesi fa su programmatori (vedi here) ed aveva una risposta accettato lì, ma è stato chiuso come off-topic, perché appartiene in caso di overflow dello stack. Non è stato possibile spostare la domanda in stack overflow perché è precedente a 60 giorni. Quindi, d'accordo con i moderatori, sto ripubblicando la domanda qui e postando la domanda accettata perché penso che contenga alcune informazioni utili.

+4

Se siete solo interessati a benchmarking, si potrebbe desiderare di controllare le [criterio] (http://hackage.haskell.org/package/criterion) biblioteca. – hugomg

+0

La funzione 'foo' non verrà eseguita dal momento in cui si raggiunge la linea finale. Le funzioni di Haskell sono valutate solo su richiesta, quindi dovrai fare qualcosa con quel valore 'r' prima dell'assegnazione a' end'. –

risposta

15

Risposta originariamente data dall'utente ysdxon programmers:

Infatti si versione non benchmark vostro algoritmo. Siccome lo r non viene utilizzato, non verrà valutato affatto.

Si dovrebbe essere in grado di farlo con DeepSeq:

benchmark :: [String] -> IO Integer 
benchmark inputList = do 
        start <- getCPUTime 
        let r = foo inputList 
        end <- r `deepseq` getCPUTime 
        return (end - start) 

(a `deepseq` b) è una certa espressione "magico" che costringe la valutazione completa/ricorsiva di a prima di tornare b.

+2

Il modo usuale per salvarsi dall'assumersi credito per il lavoro altrui è rendere la wiki della comunità delle risposte. Quindi la risposta può ancora essere migliorata/accettata, ma non riceverai il credito :) –

+1

Non lo sapevo, grazie. Ho impostato il tag wiki della comunità. Ora i due upvoters possono rimuovere l'upvot, se capita di leggerlo di nuovo. – Giorgio

6

Vorrei usare l'estensione della lingua -XBangPatterns, trovo che sia abbastanza espressivo in tali situazioni. Quindi, si dovrebbe dire "let !r = foo inputList" come in:

{-# LANGUAGE BangPatterns #-} 
import System.CPUTime 

benchmark :: [String] -> IO Integer 
benchmark inputList = do 
         start <- getCPUTime 
         let !r = foo inputList 
         end <- getCPUTime 
         return (end - start) 
+1

Questo valuterà il risultato solo per il costruttore più esterno, qui è necessaria una valutazione completa. –

+1

I pattern bang garantiscono una valutazione completa o l'espressione verrà ridotta a una forma normale debole (http://stackoverflow.com/questions/6872898/haskell-what-is-weak-head-normal-form)? – Giorgio

+0

Puoi anche usare BangPatterns in foo. –

Problemi correlati