Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I'm fairly new to Haskell. I've been trying to create a utility to count the number of words and lines in Haskell for a few days to help me understand the language better. However, I'm struggling to get it working.
So far, I have:
wordCountUtility = do
putStrLn "Please enter the filename:"
filename <- getLine
putStrLn ("The file name you have entered is: " ++ filename)
contents <- readFile filename -- read the file specified in “name” into “contents”
lower <- (return . map toLower) contents
putStrLn lower
I have tried to use 'chop' and found print . length . words =<< getContents and have modified it a number of times, but I have had no luck.
I've also had a look at quite a few similar answers on Stack Overflow, such as : identifying number of words in a paragraph using haskell
The output should be somewhat similar to this:
Amount of Lines within the file
Lines : 10
Amount of Words found within the file
Words : 110
Any help would be much appreciated.
Your wordCountUtility should probably be not be counting words yet. You should just stop at something like
commandLineUtility :: (String -> String) -> IO ()
commandLineUtility fn = do
putStrLn "Please enter the filename:"
filename <- getLine
putStrLn ("The file name you have entered is: " ++ filename)
contents <- readFile filename -- read the file specified in “name” into “contents”
lower <- (return . fn) contents
putStrLn lower
(I am keeping this as close to your text as possible.) Now, though, you have to figure out what
(String -> String) function you want to apply. This is a pure function and should be developed separately. So you might write:
cwlcount :: String -> (Int, Int, Int)
cwlcount str = (length str, length (words str), length (lines str))
format :: (Int, Int, Int) -> String
format (c,w,l) = unlines $
["Number of characters:"
, show c
, "Number of words:"
, show w
, "Number of lines:"
, show l
]
So (format . cwlcount) :: String -> String and you can write:
main :: IO ()
main = commandLineUtility (format . cwlcount)
Of course there are a million objections to this program, but you can improve it by investigating the parts piecemeal. For one thing, it is irritating that the whole list of characters is brought into memory and three length calculations are made for it separately. The Predude.getLine is not very user friendly either...
At the moment, then, our results look so:
$ ghci Sonia_CS.hs
GHCi, version 7.8.3: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
[1 of 1] Compiling Main ( Sonia_CS.hs, interpreted )
Ok, modules loaded: Main.
>>> main
Please enter the filename:
Sonia_CS.hs
The file name you have entered is: Sonia_CS.hs
Number of characters:
816
Number of words:
110
Number of lines:
25
Or better:
$ ghc -O2 Sonia_CS.hs
[1 of 1] Compiling Main ( Sonia_CS.hs, Sonia_CS.o )
Linking Sonia_CS ...
$ ./Sonia_CS
Please enter the filename:
/usr/share/dict/words
The file name you have entered is: /usr/share/dict/words
Number of characters:
2493109
Number of words:
235886
Number of lines:
235886
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 12 months ago.
Improve this question
I am trying to write a something like a repl in Haskell and I want to replicate this code in C:
for (int c = getc(stdin); c != 'e'; c = getc(stdin)) {
printf("I got %c!\n", c);
}
I could use recursion but I am afraid of exeeding the limit.
You should use recursion. Haskell is designed to handle recursion without limits, and recursion can and should be used even for effectively infinite loops (e.g., event processing loops or REPL loops).
So, you could more or less write your program as follows:
main = do
txt <- getLine
if txt /= "exit" then do
putStrLn $ "I got " ++ txt ++ "!"
main
else do
return ()
giving:
$ ./repl
foo
I got foo!
bar
I got bar!
exit
$
I've written it to grab a whole line of input instead of single character, since there are typically issues with buffered input when trying to grab input character by character. This infinite recursion works fine and will not exceed any limit no matter how many billions of lines it processes before exiting.
In most real-world programs, you don't want the whole main program to loop, so you typically write something like the following. Here, I've used when which is a nicer looking way of writing the pattern if xxx then yyy else return ().
import Control.Monad -- for definition of "when"
main = do
-- initialization
putStrLn $ "Welcome to my REPL!"
-- start the main loop
loop
-- clean up
putStrLn $ "Thanks so much for using my REPL!"
-- definition of main loop
loop = do
txt <- getLine
when (txt /= "exit") $ do
putStrLn $ "I got " ++ txt ++ "!"
loop
I am new to Haskell. I am at the last part of a school project. I have to take tuples and print them to an outfile and separate them by a tab column. So (709,4226408), (12965,4226412) and (5,4226016) should have and output of
709 4226408
12965 4226412
5 4226016
What I have been trying to do is this:
genOutput :: (Int, Int) -> String
genOutput (a,b) = (show a) ++ "\t" ++ (show b)
And this gives outputs like:
"709\t4226408"
"12965\t4226412"
"5\t4226016"
There are 3 things wrong with this. 1) Quotes still appear in the output. 2) The \t tab does not actually become a tab space. .Whenever I try to make an actual tab for the "" it just comes out as a " " space. 3) They are not aligned into columns like the above example. I know Text.Printf exists but we are not allowed to import anything other than:
import System.IO
import Data.List
import System.Environment
that's the output you get from GHCi I guess? Try to use putStrLn instead:
Prelude> genOutput (1,42)
"1\t42"
Prelude> putStrLn $ genOutput (1,42)
1 42
Why is that?
If you tell GHCi to evaluate an expression it will do so and (more or less) output it using show - show is designed to work with read and will usually output a value as if you would input it directly into Haskell. For a String that will include escape sequences and the "s
Now using putStrLn it will take the string and print it to stdout as you would expect.
Using print
Another reason could be that you use print to output your value - print is show + putStrLn so it'll show the values first re-introducing the escapes (as GHCi would) - so if you use print change it to putStrLn if you are using Strings
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have a bunch of strings stored in a config file. The strings contain values that must be evaluated at runtime:
"aaa #{val1} bbb"
"ccc #{val235} ddd"
How can I replace the #{valN} at runtime using the standard, out of the box, Haskell library? Do string interpolations, that is.
I can change the format of parts that have to be interpolated, if Haskell requires so. Namely, instead of #{valN} I could use ${valN} or anything else.
As far as I know, there are three ways to do what you want:
1. (++) operator
Since String is just a list of Char, so you can use (++) operator to combine many strings:
> "aaa " ++ show (val1) ++ " bbb"
> "ccc " ++ show (val235) ++ " ddd"
2. printf
printf can really return String type:
λ> printf "Hi, %s!" "Bob" :: String
"Hi, Bob!"
λ> :t it
it :: String
So all you need to do is giving a explicit type signature to your function which you need to return String.
3. string-interpolate package
The following is the sample code from the package document
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
import Data.Text
import Data.String.Interpolate ( i )
λ> age = 33 :: Int
λ> name = "Tatiana" :: Text
λ> [i|{"name": "#{name}", "age": #{age}}|] :: String
>>> "{\"name\": \"Tatiana\", \"age\": 33}"
λ> [i|
Name: #{name}
Age: #{age}
|] :: String
>>> "\nName: Tatiana\nAge: 33\n"
Of course, there are some other interpolation libraries, which are also listed in the package pages.
An option not mentioned in the comments or in Z-Y.L's answer is typelits-printf:
ghci> putStrLn $ printf #"You have %.2f dollars, %s" 3.62 "Luigi"
You have 3.62 dollars, Luigi
See more examples on https://github.com/mstksg/typelits-printf
Haskell Stack Overflow layout preprocessor
module StackOverflow where -- yes, the source of this post compiles as is
Skip down to What to do to get it working if you want to play with this first (1/2 way down).
Skip down to What I would like if I witter on a bit and you just want to find out what help I'm seeking.
TLDR Question summary:
Can I get ghci to add filename completion to the :so command I defined in my ghci.conf?
Could I somehow define a ghci command that returns code for compilation instead of returning a ghci command, or
does ghci instead have a better way for me to plug in Haskell code as a
file-extension-specific pre-processor, so :l would work for .hs and .lhs files as usual, but use my handwritten preprocessor for .so files?
Background:
Haskell supports literate programming in .lhs source files, two ways:
LaTeX style \begin{code} and \end{code}.
Bird tracks: Code starts with > , anything else is a comment.
There must be a blank line between code and comments (to stop trivial accidental misuse of >).
Don't Bird tracks rules sound similar to StackOverflow's code blocks?
References: 1. The .ghci manual
2. GHCi haskellwiki
3. Neil Mitchell blogs about :{ and :} in .ghci
The preprocessor
I like writing SO answers in a text editor, and I like to make a post that consists of code that works,
but end up with comment blocks or >s that I have to edit out before posting, which is less fun.
So, I wrote myself a pre-processor.
If I've pasted some ghci stuff in as a code block, it usually starts with * or :.
If the line is completely blank, I don't want it treated as code, because otherwise
I get accidental code-next-to-comment-line errors because I can't see the 4 spaces I accidentally
left on an otherwise blank line.
If the preceeding line was not code, this line shouldn't be either, so we can cope with StackOverflow's
use of indentation for text layout purposes outside code blocks.
At first we don't know (I don't know) whether this line is code or text:
dunnoNow :: [String] -> [String]
dunnoNow [] = []
dunnoNow (line:lines)
| all (==' ') line = line:dunnoNow lines -- next line could be either
| otherwise = let (first4,therest) = splitAt 4 line in
if first4 /=" " --
|| null therest -- so the next line won't ever crash
|| head therest `elem` "*:" -- special chars that don't start lines of code.
then line:knowNow False lines -- this isn't code, so the next line isn't either
else ('>':line):knowNow True lines -- this is code, add > and the next line has to be too
but if we know, we should keep in the same mode until we hit a blank line:
knowNow :: Bool -> [String] -> [String]
knowNow _ [] = []
knowNow itsCode (line:lines)
| all (==' ') line = line:dunnoNow lines
| otherwise = (if itsCode then '>':line else line):knowNow itsCode lines
Getting ghci to use the preprocessor
Now we can take a module name, preprocess that file, and tell ghci to load it:
loadso :: String -> IO String
loadso fn = fmap (unlines.dunnoNow.lines) (readFile $ fn++".so") -- so2bird each line
>>= writeFile (fn++"_so.lhs") -- write to a new file
>> return (":def! rso (\\_ -> return \":so "++ fn ++"\")\n:load "++fn++"_so.lhs")
I've used silently redefining the :rso command becuase my previous attemts to use
let currentStackOverflowFile = .... or currentStackOverflowFile <- return ...
didn't get me anywhere.
What to do to get it working
Now I need to put it in my ghci.conf file, i.e. in appdata/ghc/ghci.conf
as per the instructions
:{
let dunnoNow [] = []
dunnoNow (line:lines)
| all (==' ') line = line:dunnoNow lines -- next line could be either
| otherwise = let (first4,therest) = splitAt 4 line in
if first4 /=" " --
|| null therest -- so the next line won't ever crash
|| head therest `elem` "*:" -- special chars that don't start lines of code.
then line:knowNow False lines -- this isn't code, so the next line isn't either
else ('>':line):knowNow True lines -- this is code, add > and the next line has to be too
knowNow _ [] = []
knowNow itsCode (line:lines)
| all (==' ') line = line:dunnoNow lines
| otherwise = (if itsCode then '>':line else line):knowNow itsCode lines
loadso fn = fmap (unlines.dunnoNow.lines) (readFile $ fn++".so") -- convert each line
>>= writeFile (fn++"_so.lhs") -- write to a new file
>> return (":def! rso (\\_ -> return \":so "++ fn ++"\")\n:load "++fn++"_so.lhs")
:}
:def so loadso
Usage
Now I can save this entire post in LiterateSo.so and do lovely things in ghci like
*Prelude> :so StackOverflow
[1 of 1] Compiling StackOverflow ( StackOverflow_so.lhs, interpreted )
Ok, modules loaded: StackOverflow.
*StackOverflow> :rso
[1 of 1] Compiling StackOverflow ( StackOverflow_so.lhs, interpreted )
Ok, modules loaded: StackOverflow.
*StackOverflow>
Hooray!
What I would like:
I would prefer to enable ghci to support this more directly. It would be nice to get rid of the intermediate .lhs file.
Also, it seems ghci does filename completion starting at the shortest substring of :load that determines
you're actually doing load, so using :lso instead of :so doesn't fool it.
(I would not like to rewrite my code in C. I also would not like to recompile ghci from source.)
TLDR Question reminder:
Can I get ghci to add filename completion to the :so command I defined in my ghci.conf?
Could I somehow define a ghci command that returns code for compilation instead of returning a ghci command, or
does ghci instead have a better way for me to plug in Haskell code as a
file-extension-specific pre-processor, so :l would work for .hs and .lhs files as usual, but use my handwritten preprocessor for .so files?
I would try to make a standalone preprocessor that runs SO preprocessing code or the standard literary preprocessor, depending on file extension. Then just use :set -pgmL SO-preprocessor in ghci.conf.
For the standard literary preprocessor, run the unlit program, or use Distribution.Simple.PreProcess.Unlit.
This way, :load and filename completion just work normally.
GHCI passes 4 arguments to the preprocessor, in order: -h, the label, the source file name, and the destination file name. The preprocessor should read the source and write to the destination. The label is used to output #line pragmas. You can ignore it if you don't alter the line count of the source (i.e. replace "comment" lines with -- comments or blank lines).
This question already has an answer here:
Why shouldn't I mix tabs and spaces?
(1 answer)
Closed 6 years ago.
I am trying to learn haskell by writing a simple file copy util:
main = do
putStr "Source: "
srcPath <- getLine
putStr "Destination: "
destPath <- getLine
putStrLn ("Copying from " ++ srcPath ++ " to " ++ destPath ++ "...")
contents <- readFile srcPath
writeFile destPath contents
putStrLn "Finished"
This gets me
GHCi, version 6.10.4: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer ... linking ... done.
Loading package base ... linking ... done.
[1 of 1] Compiling Main ( D:\Test.hs, interpreted )
D:\Test.hs:8:22: Not in scope: `contents'
Failed, modules loaded: none.
Prelude>
I don't understand that compiler error because the variable seems to be ok. What is wrong?
Here is a repro file: at rapidshare
It looks like you mixed tabs and spaces (just look at your question in "edit" view to see the issue). While your editor views the code evenly indented, the Compiler seems to have a different interpretation how wide a tab should be, resulting in the writeFile destPath contents line being additionally indented. So the source is interpreted like this:
...
putStrLn ("Copying from " ++ srcPath ++ " to " ++ destPath ++ "...")
contents <- readFile srcPath writeFile destPath contents
putStrLn "Finished"
In this interpretation of the source code contents is used before it is created, so you get a compiler error.
To avoid these kind of errors best don't use tabs, or at least take additional care that you use them consistently.
That looks correct. I just pasted that into a .hs file and :loaded it into GHCi. Works here and I have the same GHC version as you.