Tuesday, June 23, 2015

HASKELL PARSING FILE RECORDS EXAMPLE

HASKELL PARSING FILE RECORDS EXAMPLE

REVISED: Thursday, February 8, 2024




1. INTRODUCTION

Using Haskell to parse records stored on a file is a good way to  begin learning Haskell data structures.

2. HASKELL PARSING FILE RECORDS EXAMPLE

This example parses the records in the annual customer sales file sales1945.txt to obtain the total sales for one customer.  You can create the sales1945.txt file from the Annual Sales File Listing printed by the module. Each record in the file has a field for custID (customer identification), invoice (invoice number), usd (United States Dollars), and mmddyyyy (invoice date). The example computes the total annual sales to one customer based on the custID.

Module:

:{
customerFilter  -- Function which creates a new list containing all records matching the customer ID.

customerID  -- The variable name for identification of the customer.

customerSales  -- Function which computes the customer total annual sales.

customerSalesFileRecords  -- The variable name containing all of the records for the entire annual sales file.

salesByCustomer  -- Function for user to input sales file name and customer ID.

sales  -- Function which creates a new list of only customer sales amounts.  

total  -- Function which turns each customer sales invoice amount String into a Double and totals the amounts. 

totalCustomerSales  -- The variable name for the total annual customer sales for one customer.

usdLines  -- Function which creates a new list of usd lines.

:}

module Sales where
import Data.List.Split(endBy)
import Data.List(isInfixOf, isPrefixOf)

:{
The function's purpose is to compute total annual USD sales for one customer ID.

The '.' operator is used to compose functions. Function composition is a way to "compose" two functions together into a single function.

map takes a function and a list and applies that function to every element in the list, producing a new list.

filter is a function that takes a predicate and a list and then returns the list of elements that satisfy the predicate.

A predicate is a function that tells whether something is true or not.

lines function is defined in the Data.List library. lines takes a String, and breaks it up into a list of strings, splitting on newlines.

dropWhile creates a list from another list taking from it its elements from when the condition fails for the first time till the end of the list.

isInfixOf function takes two lists and returns True iff the first list is contained, wholly and intact, anywhere within the second.

isPrefixOf function takes two lists and returns True iff the first list is a prefix of the second.

endBy creates a new list by splitting a String into chunks terminated by the given subsequence which is "@" in the example below.
 
:}

customerSales :: String -> String -> Double
customerSales customerSalesFileRecords customerID  = total.sales.usdLines $ customerFilter customerSalesFileRecords customerID
                                    where usdLines = map $ filter (isPrefixOf "usd").lines
                                               sales       = map (dropWhile (> ' ')).concat
                                               total        = sum.map (\x -> read x :: Double)
                                               customerFilter customerSalesFileRecords customerID = filter (isInfixOf customerID) $ endBy "@" customerSalesFileRecords

:{
Function purpose is for user to input sales file name and customer ID.

Function prints sales file records.

Function returns total annual USD sales for one customer ID.  
:}                                      
salesByCustomer :: IO Double
salesByCustomer = do
    putStrLn "Enter Annual Sales File Name:"  -- For example, enter sales1945.txt
    customerSalesFileRecords <- getLine >>= readFile  -- Reads in entire sales1945.txt file.
    putStrLn "\nAnnual Sales File Listing:\n"
    putStrLn customerSalesFileRecords                                 
    putStrLn "\nEnd Of Annual Sales File Listing:\n"
    putStrLn "Enter Customer ID:"  -- For example, a1a
    customerID <- getLine
    return $ customerSales customerSalesFileRecords customerID
    
main :: IO()
main = do
    totalCustomerSales <- salesByCustomer
    putStrLn ("\nCustomer Total USD Sales: " ++ show totalCustomerSales)

GHCi:

GHCi, version 9.8.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude>

Prelude>  :load Sales
[1 of 1] Compiling Sales              (Sales.hs, interpreted )
Ok, modules loaded: Sales.
(0.09 secs, 34185296 bytes)
Prelude>

Prelude>  main
Loading package split-0.2.2 ... linking ... done.
Enter Annual Sales File Name:
sales1945.txt

Annual Sales File Listing:

custID a1a
invoice i1
usd 1.01
mmddyyyy 0101i945
@
custID b2b
invoice i2
usd 5678.05
mmddyyyy 02021945
@
custID c3c
invoice i3
usd 910.12
mmddyyyy 03101945
@
custID d4d
invoice i4
usd 1234.11
mmddyyyy 04111945
@
custID d4d
invoice i5
usd 5678.06
mmddyyyy 05211945
@
custID c3c
invoice i6
usd 910.01
mmddyyyy 06081945
@
custID b2b
invoice i7
usd 1111.08
mmddyyyy 11121945
@
custID a1a
invoice i8
usd 1000.03
mmddyyyy 11131945
@
custID a1a
invoice i9
usd 100.01
mmddyyyy 12251945
@
custID a1a
invoice i10
usd 10.02
mmddyyyy 12251945
@

End Of Annual Sales File Listing:

Enter Customer ID:
a1a

Customer Total USD Sales: 1111.07
Prelude>

3. CONCLUSION

In this tutorial you have seen a basic approach for using Haskell to parse file records.

4. REFERENCES

Bird, R. (2015). Thinking Functionally with Haskell. Cambridge, England: Cambridge University Press.

Davie, A. (1992). Introduction to Functional Programming Systems Using Haskell. Cambridge, England: Cambridge University Press.

Goerzen, J. & O'Sullivan, B. &  Stewart, D. (2008). Real World Haskell. Sebastopol, CA: O'Reilly Media, Inc.

Hutton, G. (2007). Programming in Haskell. New York: Cambridge University Press.

Lipovača, M. (2011). Learn You a Haskell for Great Good!: A Beginner's Guide. San Francisco, CA: No Starch Press, Inc.

Thompson, S. (2011). The Craft of Functional Programming. Edinburgh Gate, Harlow, England: Pearson Education Limited.