简体   繁体   中英

Haskell IO create a list of strings and display it

I'm trying to write a program that allows the user to build up a list of strings by entering them in one at a time, and displays the list after every step.

Here is my code so far:

buildList :: [String] -> IO ()
buildList arr = do
    putStr "Enter a line:"
    str <- getLine
    if str == "" then
        return ()
    else do
        let newarr = arr : str
        putStrLn ("List is now: " ++ newarr)
        buildList newarr


listBuilder :: IO ()
listBuilder = do
    buildList []

listBuilder is starting the list by passing in the empty list, and I'm trying to use recursion so that the code keeps running until the user enters the empty string.

Its not working, any ideas welcome

Here is a desired input:

Enter a line: hello
List is now ["hello"]
Enter a line: world
List is now ["hello","world"]
Enter a line:

Error:

Couldn't match type `Char' with `[String]'
Expected type: [[String]]
  Actual type: String
In the second argument of `(:)', namely `str'
In the expression: arr : str
In an equation for `newarr': newarr = arr : str

EDIT:

This fixed it, thanks to the clues and use of show

buildList :: [String] -> IO ()
buildList arr = do
    putStr "Enter a line:"
    str <- getLine
    if str == "" then
        return ()
    else do
        let newarr = arr++[str]
        putStrLn ("List is now: " ++ show newarr)
        buildList newarr


listBuilder :: IO ()
listBuilder = do
    buildList []

You can get this working by

(a) putting the new string at the end of the list with arr++[str] instead of arr:str since : can only be used like singleThing:list ,
(b) splitting the run-round into a separate function, and
(c) passing the result on with return so you can use it elsewhere in your program

buildList arr = do
    putStrLn "Enter a line:"
    str <- getLine
    if str == "" then
        return arr
    else do
        tell (arr++[str])

tell arr = do
        putStrLn ("List is now: " ++ show arr) -- show arr to make it a String
        buildList arr

giving

Enter a line:
Hello
List is now: ["Hello"]
Enter a line:
world
List is now: ["Hello","world"]
Enter a line:

done

You can solve this problem more declaratively using the pipes and foldl libraries:

import Control.Foldl (purely, list)
import Pipes
import qualified Pipes.Prelude as P

main = runEffect $ P.stdinLn >-> purely P.scan list >-> P.print

You can read this as a pipeline:

  • P.stdinLn is a source of lines input by the user

  • P.scan behaves like Data.List.scanl , except for pipelines instead of lists. purely P.scan list says to continuously output the values seen so far.

  • P.print prints these output lists to the console

Here's an example of this pipeline in action:

$ ./example
[]
Test<Enter>
["Test"]
ABC<Enter>
["Test","ABC"]
42<Enter>
["Test","ABC","42"]
<Ctrl-D>
$

You can also easily switch out other ways to fold the lines just by changing the argument to purely scan . For example, if you switch out list with Control.Foldl.vector then it will output vectors of lines instead of lists.

To learn more, you can read the documentation for the pipes and foldl libraries.

The problem is that the : data constructor can only be used to append an element to the beginning of the list. When you write let arr=arr:str , you are using it to put an element at the end of the list. Instead, you can either construct your list backwards like this let arr=str:arr or use the ++ operator to append it to the end of the list like this let arr=arr++[str] .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM