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.