Question

I am trying to take a file name into my program and then read the file line by line into a list of string. I want the entire file to be read before going onto the rest of the program. My file I am trying to read is also about 10K lines. I also need to check each lines length to be able to put them into different lists. I currently have:

stageone :: String->[[String]]
stageone xs = do
        fileLines <-readFile.xs
        let line = lines fileLines
        xss -- dont know where to go from here
Was it helpful?

Solution

A simple way to read the file strictly is to use Text, which has a strict readFile by default:

import qualified Data.Text    as Text
import qualified Data.Text.IO as Text

main = do
    ls <- fmap Text.lines (Text.readFile "filename.txt")
    ... -- The world is your oyster!

By the second line of the program the entire file will have been processed already.

It's a good habit to learn to use Text instead of String, since Text is more efficient. To learn more about the text library, you can begin here.

OTHER TIPS

Welcome to Haskell. Unfortunately, your function's type signature won't work. You can't get away from the IO monad here.

stageone :: String -> IO [[String]]

is what you will end up with.

What you want to do is break up your requirements into functions and implement each one. These functions might be able to be pure functions without IO. Then come back and put them all together. This is how Haskell code is developed. Pay attention to the type signatures. If you get stumped, try to write just the signature and work from there.

How do you determine the length of a line?

How do you apply a function to each item in a list and keep the result?

How do you read a line from a file?

Once you have 10K line lengths, then what? Write them out? Print them?

Once you make the plan you can write and test these smaller pieces in ghci.

Good luck.

OK, here is the function in all of its point-free one line glory (or hideousness) with nary a lambda in sight...

module Main where

import Data.List ( sortBy, groupBy )
import Data.Function ( on )
import Data.Ord ( comparing )
import Control.Arrow ( (&&&) )

stageone :: String -> IO [[String]]
stageone = fmap (map (map snd) . groupBy ((==) `on` fst) . sortBy (comparing fst) . map (length &&& id) . lines) . readFile

Let's break it on down. The top level is a composition of readFile and fmap. We always read function composition from right to left. The point-free argument to stageone is passed to readFile, which returns an IO String into which fmap sends its function argument to transform the String inside the IO candy shell.

This function is another composition which we read right to left. First, we apply lines to the String to break it into a list of lines. We then map (length &&& id) over the list of lines, which transforms each line into a pair consisting of the length of the line and the line itself. Next, we sort the list of pairs by comparing the first element of each (the length). The sort is stable, so the lines will remain in their original order within a run of the same length lines. Next, we apply groupBy with ((==) `on` fst), which groups runs of pairs with the same first element (the length) into their own sublists. Finally, we apply map (map snd) over the list of lists of pairs. The outer map iterates over the groups, and the inner map iterates over the list of pairs within a group, replacing each pair with its second element (the line).

Phew!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top