2012-04-04 10 views
7

Estoy buscando llamar funciones dinámicamente en función de los contenidos encontrados en una lista de asociación.Cómo llama funciones dinámicamente con Haskell

Aquí hay un ejemplo en semi-pseudo-código. listOfFunctions pasará a callFunctions.

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 

El quid de la cuestión no es un bucle a través de la lista, más bien, es la forma de llamar a una función una vez que tenga su nombre?

Considere este caso de uso para mayor aclaración. Abre el símbolo del sistema y aparece el siguiente menú.

1: Escribir nuevo archivo de host virtual

2: Salir

Usted escribe el nuevo archivo de host virtual y no se presentan con un nuevo menú

1: Introducir la nueva Directiva

2: Escribir archivo

3: Salir

Se introduce algunas nuevas directrices para el host virtual y ahora está listo para escribir el archivo.

El programa no va a escribir ciegamente todas y cada una de las directivas que pueda, sino que solo escribirá las que usted suministró. Aquí es donde entra en juego la lista de asociaciones. Escribir una declaración gigante de si/entonces/otro o caso es una locura. Sería mucho más elegante recorrer la lista, buscar qué directivas se agregaron y llamar a las funciones para escribirlas en consecuencia.

Por lo tanto, buclee, busque un nombre de función, llame a dicha función con el valor suministrado.

Gracias a todos los que pueden ayudar con esto.

Editar:

Aquí está la solución que yo he llegado con (críticas constructivas siempre son bienvenidos).

Exporté las funciones que escriben las directivas en una lista de asociaciones, ya que cada respuesta proporcionada decía que incluir la función es el camino a seguir.

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

En el archivo que realmente escribe los hosts, ese archivo se importa.

I tiene una lista de asociación llamada hostinfo para simular algunos valor ficticio que se desprende de un usuario final y una función llamada runFunction que utiliza la técnica suministrado por edalorzo filtrar a través de ambas listas. Al hacer coincidir las teclas de ambas listas, me aseguro de que se llame a la función correcta con el valor correcto.

import Vhost.Directive 

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

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

Respuesta

8

Puesto que soy nuevo en Haskell farily voy a correr el riesgo de que se tiene en cuenta mi sugerencia muy ingenuo, pero de todos modos aquí va:

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

creo que cumple con los requisitos de la pregunta, pero tal vez lo que pretende es más sofisticado de lo que puedo ver con mi conocimiento limitado de Haskell.

16

Puede simplemente incluir directamente la función en la lista; las funciones son valores, por lo que puede referenciarlos por nombre en una lista. Una vez que los sacas de la lista, aplicarlos es tan simple como func value. No hay necesidad de involucrar sus nombres en absoluto.

+0

Sé que tienes más votos en tu respuesta, sin embargo, desde que terminé usando el código de edalorzo, acepté su respuesta. – OpCodeOmega

1

Primero definimos nuestra lista de funciones. Ello podría lograrse mediante más maquinaria, pero en aras de ejemplo que acabo de hacer una lista explícita:

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 

entonces podemos seleccionar de esta lista como queramos y ejecutar la función:

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

y funciona (si importa Data.Maybe):

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

Tenga en cuenta que, en este caso, la lista en realidad no contiene ninguna función; solo 'acciones IO'. – ehird

+0

¿Alguien quiere comentar sobre su voto negativo? –

1

no guarde las funciones como cadenas, o más bien, trata de almacenar las funciones reales y luego marcarlos con una cadena. De esta forma, puede llamar a la función directamente. Las funciones son valores de primera clase, por lo que puede llamar a la función utilizando el nombre que le asigne.

2

Puede ser un poco exagerado (estoy de acuerdo con el razonamiento de ehird) pero puede evaluar una cadena con código Haskell utilizando la función eval en System.Eval.Haskell.

EDITAR

Como se señaló en los comentarios, hint es una mejor opción para la evaluación de cadenas con expresiones Haskell. Citando la página:

Esta biblioteca define una mónada de intérprete. Permite cargar módulos de Haskell, examinarlos, verificarlos y evaluar cadenas con expresiones de Haskell e incluso forzarlas a valores. La biblioteca es segura para hilos y segura (incluso la coerción de expresiones a valores). Esencialmente, es un gran subconjunto de la API GHC envuelto en una API más simple. Funciona con GHC 6.10.x y 6.8.x

+0

plugins no se ha actualizado desde 2010, y no funciona con GHCs recientes; [sugerencia] (http://hackage.haskell.org/package/hint) es una mejor opción si necesita interpretar el código Haskell en tiempo de ejecución. – ehird

+0

@ehird gracias! Actualicé mi respuesta con tu sugerencia. –

+1

@ehird alguien anunció hace unos días que 'plugins' está bajo un nuevo mantenimiento y una nueva versión debería ser lanzada en unos días. Aunque aún no ha salido. –

Cuestiones relacionadas