2010-05-09 174 views
6

Me gustaría saber cuál es la mejor solución para crear menú sencillo con funcionalidad descrita a continuación (pseudo código) al igual que yo estoy acostumbrado a:menú de texto simple en Haskell

while (true) { 
    x = readLine(); 
    case (x): 
     x == "1" then do sth1 function 
     x == "2" then do sth2 function 
} 

O tal vez alguna otra idea sobre cómo hacer un menú que no esté en el patrón descrito arriba?

Respuesta

6

Algo así como

menu :: IO() 
menu = do 
     putStrLn . unlines $ map concatNums choices 
     choice <- getLine 
     case validate choice of 
     Just n -> execute . read $ choice 
     Nothing -> putStrLn "Please try again" 

     menu 
    where concatNums (i, (s, _)) = show i ++ ".) " ++ s 

validate :: String -> Maybe Int 
validate s = isValid (reads s) 
    where isValid []   = Nothing 
     isValid ((n, _):_) 
       | outOfBounds n = Nothing 
       | otherwise  = Just n 
     outOfBounds n = (n < 1) || (n > length choices) 

choices :: [(Int, (String, IO()))] 
choices = zip [1.. ] [ 
    ("DoSomething", foo) 
, ("Quit", bar) 
] 

execute :: Int -> IO() 
execute n = doExec $ filter (\(i, _) -> i == n) choices 
    where doExec ((_, (_,f)):_) = f 

foo = undefined 
bar = undefined 

Probablemente se podría dividir la enumeración de "opciones" para que sólo tenga las descripciones y funciones dentro de él, un poco de separación, pero esto funciona. ¡La evaluación de la función "menú" le permitirá elegir qué hacer!

+0

tan fácil :) una pregunta más: ¿podría explicar primera línea: (putSrtln .....) Im novato en Haskell y no sé que los operadores similares. $ y así sucesivamente. Si no es un problema para ti, me ayudaría. – gruber

+1

¡Acabo de editar mi respuesta y la hice más flexible! No probado de ninguna manera, simplemente lo escribí, pero debería ver lo que hice allí :). Sobre esa línea putStrLn: Básicamente escribí "putStrLn (unlines (map concatNums choices))" sin parantheses :) – LukeN

+0

Bien, probado ahora, ¡funciona como un encanto! – LukeN

10

Hay algunos paquetes interesantes para formas de alto nivel para la construcción de sistemas de línea de comandos en general:

  1. ui-command: Un marco para los programas de línea de comandos amigables
  2. haskeline: Una interfaz de línea de comandos para la entrada del usuario, escritos en Haskell.
  3. HCL: Biblioteca de alto nivel para construir interfaces de línea de comando.

Me gusta especialmente el comando ui-command, ya que es un marco completo para sus herramientas de línea de comandos: se enviará a las funciones del manejador que proporciona para cada comando y también proporcionará ayuda específica al usuario.

El objetivo es una sensación de pulido, en lugar de una sensación de hackish.

+0

hola, gracias por la ayuda, después de descargar ese paquete ui-command y todas las dependencias cuando intento compilar tengo ejemplo de error: no se pudo encontrar módulo 'Control.Monad.Trans': se encontró en varios paquetes: mónadas-fd-0.1.0.1-MTL 1.1.0.2 ¿Cómo puedo solucionar esto? – gruber

+0

Necesitarás anular el registro de mónadas-fd, creo. ghc-pkg anula el registro de mónadas-fd. –

0

Aquí hay otro ejemplo que es un poco más parecido a un menú, en el sentido de que lee caracteres individuales en reacciona directamente, sin requerir que el usuario presione enter.

import System.IO 
import System.Exit 

import Control.Monad 


main = forever (printMenu >> readChoice >>= menuAction) 

printMenu = putStr "\np)rint 'Hello, world!'\ne)xit\nyour choice: " >> hFlush stdout 

readChoice = hSetBuffering stdin NoBuffering >> hSetEcho stdin False >> getChar 

menuAction 'p' = putStrLn "\nHello, world!" 
menuAction 'e' = exitSuccess 
menuAction _ = hPutStrLn stderr "\nInvalid choice." 
Cuestiones relacionadas