Haskell - loop over user input

后端 未结 4 975
半阙折子戏
半阙折子戏 2021-02-13 21:40

I have a program in haskell that has to read arbitrary lines of input from the user and when the user is finished the accumulated input has to be sent to a function.

In

相关标签:
4条回答
  • 2021-02-13 22:17

    The Haskell equivalent to iteration is recursion. You would also need to work in the IO monad, if you have to read lines of input. The general picture is:

    import Control.Monad
    
    main = do
      line <- getLine
      unless (line == "q") $ do
        -- process line
        main
    

    If you just want to accumulate all read lines in content, you don't have to do that. Just use getContents which will retrieve (lazily) all user input. Just stop when you see the 'q'. In quite idiomatic Haskell, all reading could be done in a single line of code:

    main = mapM_ process . takeWhile (/= "q") . lines =<< getContents
      where process line = do -- whatever you like, e.g.
                              putStrLn line
    

    If you read the first line of code from right to left, it says:

    1. get everything that the user will provide as input (never fear, this is lazy);

    2. split it in lines as it comes;

    3. only take lines as long as they're not equal to "q", stop when you see such a line;

    4. and call process for each line.

    If you didn't figure it out already, you need to read carefully a Haskell tutorial!

    0 讨论(0)
  • 2021-02-13 22:18

    Using pipes-4.0, which is coming out this weekend:

    import Pipes
    import qualified Pipes.Prelude as P
    
    f :: [String] -> IO ()
    f = ??
    
    main = do
        contents <- P.toListM (P.stdinLn >-> P.takeWhile (/= "q"))
        f contents
    

    That loads all the lines into memory. However, you can also process each line as it is being generated, too:

    f :: String -> IO ()
    
    main = runEffect $
        for (P.stdinLn >-> P.takeWhile (/= "q")) $ \str -> do
            lift (f str)
    

    That will stream the input and never load more than one line into memory.

    0 讨论(0)
  • 2021-02-13 22:18

    You could do something like

    import Control.Applicative ((<$>))
    
    input <- unlines . takeWhile (/= "q") . lines <$> getContents
    

    Then input would be what the user wrote up until (but not including) the q.

    0 讨论(0)
  • 2021-02-13 22:40

    It's reasonably simple in Haskell. The trickiest part is that you want to accumulate the sequence of user inputs. In an imperative language you use a loop to do this, whereas in Haskell the canonical way is to use a recursive helper function. It would look something like this:

    getUserLines :: IO String                      -- optional type signature
    getUserLines = go ""
      where go contents = do
        line <- getLine
        if line == "q"
            then return contents
            else go (contents ++ line ++ "\n")     -- add a newline
    

    This is actually a definition of an IO action which returns a String. Since it is an IO action, you access the returned string using the <- syntax rather than the = assignment syntax. If you want a quick overview, I recommend reading The IO Monad For People Who Simply Don't Care.

    You can use this function at the GHCI prompt like this

    >>> str <- getUserLines
    Hello<Enter>     -- user input
    World<Enter>     -- user input
    q<Enter>         -- user input
    >>> putStrLn str
    Hello            -- program output
    World            -- program output
    
    0 讨论(0)
提交回复
热议问题