Microsoft PowerPoint – Week6.pptx
2022‐10‐12
Copyright By PowCoder代写 加微信 powcoder
Ch 9: More Input and More Output
Ch 10: Functionally Solving Problems
University of the Fraser Valley
COMP 481: Functional and Logic Programming
• Reading and Writing Files
• The Bracket Function
• Writing with Handles
• To‐Do Function
• Command‐Line Arguments
• Handling Bad Input
• Randomness
• Toss a Coin
• Sequences of Random Numbers
• Randomness and I/O
• ByteStrings
• Strict and Lazy ByteStrings
• Copying Files with ByteStrings
Chapter 10
• Reverse Polish Notation Calculator
• Writing an RPN Function
• Adding More Operations
• Heathrow to London
• Using Input for the Road System
2022‐10‐12
Operations
import Data.List
‐‐ Get a list of unique sums from the sums of all possible pairs
of items from list1 and list2
list1 = [5, 6, 7, 8]
list2 = [1, 2, 3, 4]
method1 = nub [a+b | a <‐ list1, b <‐ list2] method2 = nub $ fmap (+) list1 <*> list2
method3 = nub $ (+) <$> list1 <*> list2
method4 = nub $ pure (+) <*> list1 <*> list2
method5 = nub $ [(+)] <*> list1 <*> list2
Streams are sequences of data supplied over time.
• similar to characters supplied by keyboard as the user types them
• not much random access, only the next character in the sequence
2022‐10‐12
File Input
Save the following text data as `haiku.txt` in a subfolder
named `data`:
I’m a lil’ teapot
What’s with that airplane food, huh?
It’s so small, tasteless
Then save the following script as `capslocker.hs`:
{‐ ### capslocker.hs v1 ### ‐}
import Control.Monad
import Data.Char
main = forever $ do
l <‐ getLine putStrLn $ map toUpper l Command‐Line Compile and Compile the program and execute with a redirect: stack ghc capslocker.hs capslocker < data\haiku.txt (Windows) ./capslocker < data/haiku.txt (Unix‐like) Choose the above execute based on your platform. • in Windows, there is no need for the `./` prefix • the redirect `<` takes the contents of the `haiku.txt` file one line at a time and pipes it into standard input • this is the same as if we had typed it on the keyboard while `capslocker` executed 2022‐10‐12 getContents We can simplify the `capslocker` program by making use of `getContents` function. • `getContents` does lazy I/O in Haskell • it gets the input when required later, instead of right away • it reads standard input until it gets an end‐of‐file character • its type is `getContents :: IO String` • it takes care of how much input to get for us, unlike `forever` {‐ ### capslocker.hs v2 ### ‐} import Data.Char contents <‐ getContents putStr $ map toUpper contents getContents To run `capslocker` without redirected input, user input will continue to get echoed as capitalized messages to stdout: • to stop the program, send it an end‐of‐file signal • in Windows, press `CTRL‐Z` and then press `ENTER` • in other platforms, press `CTRL‐D` Some notes about execution involving `getContents`: • `getContents` makes a promise to keep processing more lines of input, one line at a time • it does not process a line in the future until it is typed • because `contents` is bound to `getContents`, it acts similarly • because `map toUpper` takes `contents` as input, it also promises to execute per line as entered • they all repeat execution for each line until an end‐of‐file character is 2022‐10‐12 We can practice lazy input like this more with a program that restricts which input should be displayed depending on length: {‐ ### shortLinesOnly.hs v1 ### ‐} contents <‐ getContents putStr (shortLinesOnly contents) shortLinesOnly :: String ‐> String
shortLinesOnly =
unlines . filter (\line ‐> length line < 10) . lines • `lines` and `unlines` functions behave like `words` and `unwords` • but for input separated by end‐of‐line characters split into elements of a list and back shortLinesOnly Save the following text in the `data` folder as `short.txt`: {‐ ### data\short.txt ### ‐} i am a loooooooooong line!!! yeah i'm long so what hahahaha!!!!!! short line loooooooooooooooooooooooooooong * go ahead and redirect the above text file into `shortLinesOnly` 2022‐10‐12 *shortLinesOnly The previous implementation has a useful function to do the same kind of operations on each line of input: {‐ ### shortLinesOnly.hs v2 ### ‐} main = interact shortLinesOnly shortLinesOnly :: String ‐> String
shortLinesOnly =
unlines . filter (\line ‐> length line < 10) . lines
• `interact` takes a function of type `String ‐> String` as a parameter
and gives back an I/O action
• the I/O action performs the same execution as the previous version of
our `shortLinesOnly` program
• i.e.: it processes one line at a time of streamed input
• * we can either redirect, or type lines of input ourselves
*Palindromes
We can practice `interact` more by writing `palindromes.hs`
to test whether a line of input is a palindrome.
• a palindrome is a string with characters in forward sequence that
also have the same sequence backward
• let us write a function to pass to `interact` so that it replaces
palindromes with
• “palindrome”, or
• “not palindrome”
2022‐10‐12
*Palindromes
{‐ ### palindromes.hs ### ‐}
main = interact respondPalindromes
respondPalindromes :: String ‐> String
respondPalindromes =
. map (\line ‐>
if isPal line
then “palindrome”
else “not palindrome“
isPal :: String ‐> Bool
isPal xs = xs == (reverse xs)
*Palindromes
Try out palindromes.hswith some input:
{‐ ### data\words.txt ### ‐}
2022‐10‐12
— Reading and Writing Files —
hGetContents
Save the following in a file called `data/girlfriend.txt` (from
‘s hit song “Girlfriend”):
Hey! Hey! You! You!
I don’t like your girlfriend!
No way! No way!
I think you need a new one!
First, read file contents and print it to standard output:
{‐ ### girlfriend.hs v1 ### ‐}
import System.IO
handle <‐ openFile "data/girlFriend.txt" ReadMode contents <‐ hGetContents handle putStr contents hClose handle 2022‐10‐12 • `openFile :: FilePath ‐> IOMode ‐> IO Handle`
• parameters take:
• (1) a specified file,
• (2) an IOMode (`ReadMode`), and
• (3) an I/O action to open a file and yield the file’s associated handle
• `FilePath` is just a type synonym for `String`,
i.e.: `type FilePath = String`
• `IOMode` is defined like so:
• `data IOMode =
ReadMode | WriteMode | AppendMode | ReadWriteMode`
• (note that it is not `IO Mode` with a space, which would be an I/O
action with yield of `Mode`)
• `IOMode` is just an enumeration
• programs in general (not just Haskell) are allowed by the OS to work
with a file by its file descriptor
• the file descriptor in Haskell is typed as a `Handle` that implements a
bit more functionality with the mode
• `getContents` works with standard input
• `hGetContents` works with specified files
• these functions are altogether otherwise the same
• `hGetContents` will only load parts of the file as needed, which helps
when we are loading a large file to avoid hogging RAM
• `handle` points to current location in the file to keep track of reading it
• `putStr` is familiar
• `hClose` takes a handle and returns an I/O action that closes the file
• always close a resource obtained from outside of your program
• otherwise, the program could terminate trying to open a file with a
handle that has not been closed
2022‐10‐12
Function `withFile` compacts the execution of our previous
program, and it has the following signature:
withFile :: FilePath ‐> IOMode ‐> (Handle ‐> IO a) ‐> IO a
• `FilePath` takes a specified file
• `(Handle ‐> IO a)` function
• lastly, `withFile` returns an I/O action
• it will open the file
• perform some action with the function passed in
• close the file
• if anything goes wrong, `withFile` makes sure to close the file
{‐ ### girlfriend.hs v2 ### ‐}
import System.IO
withFile “data/girlfriend.txt” ReadMode
(\handle ‐> do
contents <‐ hGetContents handle putStr contents • `(\handle ‐> do …)` function ( :: handle ‐> I/O a )
• it is typical to pass it in as a lambda this way
• `withFile` makes sure to close the file handle if anything goes wrong
2022‐10‐12
Exceptions
Errors during execution that happen (e.g.: calling `head` on
an empty list) typically print an error message:
• these are called exceptions
• `withFile` ensured a file handle is closed when an exception is raised
• for any resource (such as a file), we need to ensure it is released
given any situation or error
Then the common design for working with a resource and
dealing with input and output using it is as follows:
• get the resource as an I/O action
• ensure it can be closed regardless of exception
• process the data of the resource as an I/O action
• (altogether, these describe an I/O action of the resource type)
— The Bracket Function —
2022‐10‐12
Then the following type for the `bracket` function…
bracket :: IO a ‐> (a ‐> IO b) ‐> (a ‐> IO c) ‐> IO a
…matches its parameters (and return type) for the
common design described.
• the first `IO a` is the resource obtained, but as an I/O action
• `(a ‐> IO b)` is a function to execute regardless of exception
• `(a ‐> IO c)` is a function to process the resource
• the last `IO a` needs to match context of input I/O action `IO a`
Then implementing a function such as `withFile` becomes
very concise:
{‐ example of exception handling ‐}
withFile’ :: FilePath ‐> IOMode ‐> (Handle ‐> IO a) ‐> IO a
withFile’ name mode f =
bracket (openFile name mode)
(\handle ‐> hClose handle)
(\handle ‐> f handle)
2022‐10‐12
—Working with Handles —
There are counterpart functions for the same actions with
handles that we have been doing with stdin and stdout:
• `hGetLine`
• `hPutStr`
• `hPutStrLn`
• `hGetChar`
2022‐10‐12
Using files as if they were large strings is very common, so
we have three more functions to help with this:
• `readFile :: FilePath ‐> IO String` take a specified file and
performs I/O read action (lazily) and can bind to some variable
• a bit more concise than calling `openFile` and then `hGetContents`
• `writeFile :: FilePath ‐> String ‐> IO ()` takes a specified file, a
string, and performs I/O write action
• if the file already exists, its contents are first erased before writing
• `appendFile :: FilePath ‐> String ‐> IO ()`
• same as `writeFile`, but does not erase contents first, and instead
appends the given string after the contents of the file
(Simranjit Singh)
import System.IO
import Data.Char
main :: IO ()
line <‐ getLine let num = read line :: Int contents <‐ readFile "msg.txt" writeFile "secretmsg.txt" (encode num contents) encode :: Int ‐> String ‐> String
encode offset msg = map (\c ‐> chr $ ord c + offset) msg
2022‐10‐12
appendFile
import System.IO
student <‐ getLine appendFile "classList.txt" (student ++ ", ") File classList.txt: Directories import System.IO import System.Directory import Data.List import System.Process createDirectoryIfMissing False "planner" contents <‐ getDirectoryContents "planner" let cats = unlines contents putStrLn "Choose Subject:" putStrLn cats catName <‐ getLine putStrLn "Enter assignment name:" newAssign <‐ getLine appendFile ("planner/" ++ catName ++ ".txt") (newAssign ++ "\n") 2022‐10‐12 Directories • getDirectoryContents will show `.` and `..` • listDirectory is the same, but does not show `.` and `..` • (we have seen appendFile in a previous example) — To‐Do List — 2022‐10‐12 To‐Do List We have enough to write an application to keep a list of things we would like to do and store them in a file for us. Creating files involves POSIX libraries, so for now, create the `data/todo.txt` file, and write the following program: {‐ ### todo.hs v1 ### ‐} import System.IO todoItem <‐ getLine appendFile "data/todo.txt" (todoItem ++ "\n") To‐Do List Type in some chores, but as it is, you have to run the program for each to do item you want to add: {‐ ### data\todo.txt ### ‐} iron the dishes dust the dog take the salad out of the oven • you can observe the contents of the to do list file with • `cat data/todo.txt` in Bash, • and with `type data/todo.txt` in Windows CMD 2022‐10‐12 {‐ ### deletetodo.hs v1 ### ‐} import System.IO import System.Directory import Data.List conetents <‐ readFile "data/todo.txt" todoTasks = lines contents numberedTasks = zipWith (\n line ‐> show n ++ ” ‐ ” ++ line)
[0..] todoTasks
putStrLn “These are your TO‐DO items:”
mapM_ putStrLn numberedTasks
putStrLn “Which one do you want to delete?”
numberString <‐ getLine (cont’d on next slide…) number = read numberString newTodoItems = unlines $ delete (todoTasks !! number) todoTasks (tempName, tempHandle) <‐ openTempFile "." "temp" hPutStr tempHandle newTodoItems hClose tempHandle removeFile "data/todo.txt" renameFile tempName "data/todo.txt“ 2022‐10‐12 • `todoTasks` stores a list of lines from the `todo.txt` file • it looks something like `["iron…","dust…","take the salad…"]` • `zipWith` prefixes a number to each line in the `todoTasks` list • `numberedTasks` looks something like `["0 – iron…","1 – dust…","2 ‐ take the salad…"]` • `mapM_` prints out the `numberedTasks`, each element on a separate • `number` gets assigned to a choice from the user of which numbered line to delete • `!!` accesses the element we want, and `delete` removes the first occurrence of it from the list • `unlines` combines the edited list back into one string (with end‐of‐ line characters between elements) • the function `openTempFile` is a round‐about way to later replace the old todo list file • `openTempFile` takes two parameters: • a temporary directory, which we give as the current directory "." • the prefix to a temporary file name, which we call "temp", and a few random digits are added • the new temporary file name and handle are returned in a pair • we use the temporary file to write the new edited list and replace the old file 2022‐10‐12 Exceptions There are some issues with the `deletetodo.hs`: • if the program terminates from some kind of error • the temporary file is created, • but not renamed • we need functionality similar to `bracket` where • a resource gets cleaned up automatically when we are done • but only if there is some kind of exception {‐ ### deletetodo.hs v2 ### ‐} import System.IO import System.Directory import Data.List import Control.Exception contents <‐ readFile "data/todo.txt" todoTasks = lines contents numberedTasks = zipWith (\n line ‐> show n ++ ” ‐ ” ++ line)
[0..] todoTasks
putStrLn “These are your TO‐DO items:”
mapM_ putStrLn numberedTasks
putStrLn “Which one do you want to delete?”
numberString <‐ getLine `bracketOnError` `Control.Exception` 2022‐10‐12 number = read numberString newTodoItems = unlines $ delete (todoTasks !! number) todoTasks bracketOnError (openTempFile "." "temp") (\(tempName, tempHandle) ‐> do
hClose tempHandle
removeFile tempName
(\(tempName, tempHandle) ‐> do
hPutStr tempHandle newTodoItems
hClose tempHandle
removeFile “data/todo.txt”
renameFile tempName “data/todo.txt”
The parameters of
`bracketOnError`:
• the resource first
• what to do on an exception
• what to do as expected normally
— Command‐Line Arguments —
2022‐10‐12
Command‐Line
The To‐Do App limitations only work with the one text file,
and only add or delete one item from a list one at a time.
Instead, we can specify what we would like our program to
do exactly when we execute it. We use `System.Environment`:
• it lets us read command‐line arguments:
{‐ ### argTest.hs ### ‐}
import System.Environment
import Data.List
args <‐ getArgs progName <‐ getProgName putStrLn "The arguments are:" mapM putStrLn args putStrLn "The program name is:" putStrLn progName We combine the smaller example programs to insert and delete items for our to‐do list. Altogether, we want the user to choose command‐line options for controlling its functionality: • view tasks • add tasks • delete tasks To add a task to the `todo.txt` file, we will want to enter into the terminal, and similarly for the other two options: (this is the desired behaviour from terminal execution) ./todo add todo.txt "Find the magic sword of power" ./todo view todo.txt ./todo remove todo.txt 2 2022‐10‐12 The program start will decide between which function to run which matches the first command argument input: {‐ ### todo.hs part 1 ### ‐} import System.Environment import System.Directory import System.IO import Data.List dispatch :: String ‐> [String] ‐> IO ()
dispatch “add” = add
dispatch “view” = view
dispatch “remove” = remove
(command:argList) <‐ getArgs dispatch command argList • in `main` we first get the arguments matched with pattern `(command:argList)` • then `command` should pattern match with one of the defined versions of the `dispatch` function • the result returns the function to apply to the `argList` So, suppose we type in the terminal: ./todo add todo.txt "Find the magic sword of power" Then `main` should parse `add` as the command and `["todo.txt", "Find the magic sword of power"]` 2022‐10‐12 Next, we implement the three functions for our app, starting with `add`: {‐ ### todo.hs part 2, add v1 ### ‐} add :: [String] ‐> IO ()
add (filename:toDoItems) =
appendFile filename (unwords (toDoItems ++ [“\n”]))
• (no errors dealt with for mismatched file names until later)
• the rest of the list is dealt with as one long task to be
appended on a new line at the end of the user‐specified file
• `unwords` will concatenate all string elements in the list together as one
string separated by a space
Before we move on to combine with the `view` and
`remove` functions, add the following imports:
{‐ ### todo.hs part 3 ### ‐}
import Data.Foldable
import Control.Exception
2022‐10‐12
Foldable module is for `mapM_` and Exception module is for
use of `bracketOnError` to handle potential file issues.
{‐ ### todo.hs part 4 ### ‐}
view :: [String] ‐> IO ()
view (filename:remainder) = do
contents <‐ readFile filename todoTasks = lines contents numberedTasks = zipWith (\n line ‐> show n ++ ” ‐ ” ++ line)
[0..] todoTasks
putStrLn “These are your To‐Do items:”
mapM_ putStrLn numberedTasks
from To‐Do
And basically the same function as before with `remove`:
{‐ ### todo.hs part 4 ### ‐}
remove :: [String] ‐> IO ()
remove [filename, numString] = do
contents <‐ readFile filename todoTasks = lines contents numberedTasks = zipWith (\n line ‐> show n ++ ” ‐ ” ++ line)
[0..] todoTasks
putStrLn “These are your To‐Do items:”
程序代写 CS代考 加微信: powcoder QQ: 1823890830 Email: powcoder@163.com