2012-04-04 20 views
7

Sto cercando di chiamare le funzioni in modo dinamico in base ai contenuti trovati in un elenco di associazioni.Come si chiamano le funzioni in modo dinamico con Haskell

Ecco un esempio in semi-pseudo-codice. listaFunzioni passate a chiamateFunzioni.

listOfFunctions = [('function one', 'value one') 
        , ('function two', 'value two') 
        , ('function three', 'value three')] 

callFunctions x = loop through functions 
        if entry found 
        then call function with value 
        else do nothing 

Il nocciolo della questione non è scorrendo l'elenco, piuttosto, è come chiamare una funzione una volta che ho il suo nome?

Considerare questo caso d'uso per ulteriori chiarimenti. Si apre il prompt dei comandi e viene presentato il seguente menu.

1: Scrivere nuovo file vhost

2: Exit

Si scrive il nuovo file vhost e non viene presentato con un nuovo menu

1: Inserisci nuova direttiva

2: scrittura file

3: uscita

Si entra alcune nuove direttive per il vhost e ora sono pronti per scrivere il file.

Il programma non scriverà ciecamente ogni singola direttiva, anzi, scriverà solo quelli che hai fornito. È qui che entra in gioco l'elenco delle associazioni. Scrivere una gigantesca dichiarazione if/then/else o case è follia. Sarebbe molto più elegante scorrere l'elenco, cercare le direttive aggiunte e chiamare le funzioni per scriverle di conseguenza.

Quindi, loop, trovare un nome di funzione, chiamare detta funzione con il valore fornito.

Grazie a tutti coloro che possono dare una mano.

Edit:

Ecco la soluzione che mi è venuta in mente (critiche costruttive sono sempre i benvenuti).

Ho esportato le funzioni che scrivono le direttive in un elenco di associazioni poiché ogni risposta ha affermato che includere solo la funzione è la strada da percorrere.

funcMap = [("writeServerName", writeServerName) 
      ,("writeServeralias", writeServerAlias) 
      ,("writeDocRoot", writeDocRoot) 
      ,("writeLogLevel", writeErrorLog) 
      ,("writeErrorPipe", writeErrorPipe) 
      ,("writeVhostOpen", writeVhostOpen)] 

Nel file che scrive effettivamente gli host, quel file viene importato.

Ho un elenco associazione chiamata hostinfo per simulare un valore fittizio che sarebbero raccolti da un utente finale e una funzione denominata runFunction che utilizza la tecnica forniti dagli edalorzo filtrare attraverso entrambi gli elenchi. Corrispondendo ai tasti di entrambe le liste, mi assicuro che la funzione corretta venga chiamata con il giusto valore.

import Vhost.Directive 

hostInfo =  [("writeVhostOpen", "localhost:80") 
       ,("writeServerName", "norics.com")] 

runFunctions = [f val | (mapKey, f) <- funcMap, (key, val) <- hostInfo, mapKey == key] 

risposta

8

Dato che io sono farily nuovo a Haskell vi rischiare di prendere in considerazione il mio suggerimento molto ingenuo, ma comunque qui va:

let funcs = [("sum", (+3),1),("product", (*3),2),("square", (^2),4)] 
[f x | (name, f, x) <- funcs, name == "sum"] 

penso che soddisfa i requisiti della domanda, ma forse quello che intendi è più sofisticato di quello che posso vedere con la mia conoscenza ancora limitata di Haskell.

16

È possibile includere semplicemente la funzione nell'elenco direttamente; le funzioni sono valori, quindi puoi farli riferimento per nome in una lista. Una volta che li hai fuori dalla lista, applicarli è semplice come func value. Non è necessario coinvolgere i loro nomi.

+0

So che hai più voti sulla tua risposta, tuttavia, da quando ho finito per usare il codice di edalorzo ho accettato la sua risposta. – OpCodeOmega

1

Per prima cosa definiamo la nostra lista di funzioni. Questo potrebbe essere costruito usando più macchine, ma per il bene di esempio che basta fare un elenco esplicito:

listOfFunctions :: [(Int, IO())] 
listOfFunctions = [(0, print "HI")  -- notice the anonymous function 
        ,(1, someNamedFunction) -- and something more traditional here 
        ] 

someNamedFunction = getChar >>= \x -> print x >> print x 

Poi possiamo selezionare da questa lista però che vogliamo e eseguire la funzione:

executeFunctionWithVal :: Int -> IO() 
executeFunctionWithVal v = fromMaybe (return()) (lookup v listOfFunctions) 

e funziona (se si importa Data.Maybe):

Ok, modules loaded: Main. 
> executeFunctionWithVal 0 
"HI" 
> executeFunctionWithVal 01 
a'a' 
'a' 
+0

Si noti che, in questo caso, l'elenco non contiene effettivamente alcuna funzione; solo azioni 'IO'. – ehird

+0

Qualcuno si preoccupa di commentare il loro voto negativo? I plug-in –

1

non conservare le funzioni come stringhe, o meglio, provare a conservarle le funzioni reali e poi li codifica con una stringa. In questo modo puoi chiamare direttamente la funzione. Le funzioni sono valori di prima classe, quindi puoi chiamare la funzione usando qualunque nome tu assegni.

2

Potrebbe essere un po 'eccessivo (sono d'accordo con il ragionamento di ehird) ma è possibile valutare una stringa con codice Haskell utilizzando la funzione eval in System.Eval.Haskell.

EDIT

Come sottolineato nei commenti, hint è una scelta migliore per valutare le stringhe con le espressioni Haskell. Citando la pagina:

Questa libreria definisce una monade Interpreter. Permette di caricare i moduli Haskell, sfogliarli, controllare il tipo e valutare le stringhe con espressioni Haskell e persino forzarle in valori. La libreria è thread-safe e type-safe (anche la coercizione delle espressioni ai valori). È, essenzialmente, un enorme sottoinsieme dell'API GHC racchiuso in un'API più semplice. Funziona con GHC 6.10.x e 6.8.x

+0

non sono stati aggiornati dal 2010 e non funzionano con i GHC recenti; [hint] (http://hackage.haskell.org/package/hint) è un'opzione migliore se devi interpretare il codice Haskell in fase di esecuzione. – ehird

+0

@ehird grazie! Ho aggiornato la mia risposta con il tuo suggerimento. –

+1

@ehird qualcuno ha annunciato pochi giorni fa che 'plugins' è sotto nuova manutenzione e una nuova versione dovrebbe essere rilasciata in pochi giorni. Anche se non è ancora uscito. –

Problemi correlati