Lecture: Embracing Functions
Dear students:
In this chapter you learned about four different ways of making functions in Haskell: named definitions, lambdas, partial application, and composition. These four tools are like the hammer and saw used by a carpenter. Instead of making furniture and homes, you assemble functions into algorithms. Today we'll assemble functions to solve a handful of programming challenges. We'll also see pattern matching and revisit the canonical higher-order functions map, filter, and fold.
Head
In the most recent lab, you wrote the head
utility. Likely it was more complex than it needed to be. That's because you hadn't yet learned about pattern matching and higher-order functions! Now that you have them in your repertoire, you can rewrite it like this:
import System.Environment
main = do
file : nString : _ <- getArgs
text <- readFile file
let n = read nString :: Int
mapM_ putStrLn $ take n $ lines text
See that gauntlet on the last line? We could have written that as this composition:
(mapM_ putStrLn . take n . lines) text
Map, Filter, and Fold
Before we dive into more complex programs, let's solve a few higher-order function problems together. Use map
, filter
, and foldl
to solve these challenges:
I find map operations easiest to write. The time conversion is a map, and here's how I'd write it:
millisToSeconds :: [Double] -> [Double]
millisToSeconds millis = map (* 1000) millis
If I recognize that I'm just building a function from other functions, I can assign to millisToSeconds
rather than “define” a function:
millisToSeconds = map (* 1000)
This is point-free style. I've partially applied the map
function. We're still waiting on the list parameter.
Filter is the next easiest. We want to take the characters of the string that are not in "aeiou"
. The elem
function tests if a value is in a list. That leads us to this point-free definition:
consonants :: String -> String
consonants = filter (\c -> not $ elem c "aeiou")
Lastly we have the fold operation. We want to sum up the areas, so we write a lambda that destructures a rectangle, computes its area, and tacks it on to the accumulator:
area :: [(Double, Double)] -> Double
area = foldl (\accum (w, h) -> accum + w * h) 0
parseInts
In many forthcoming problems, we are going to read in a bunch of whole numbers from a file. We'll need to convert a list of String
to a list of Int
. Let's write a helper method to perform this collective conversion. To make it reusable, we'll put it in a module in Utilities.hs
:
module Utilities where
parseInts :: [String] -> [Int]
parseInts = map read
Advent of Code 2018, Day 1
Write a program that solves Advent of Code 2018, Day 1. Accept a file name containing the input as a command-line argument.
Here's one possible solution:
import System.Environment
import Utilities
main = do
first : _ <- getArgs
text <- readFile first
let strippedText = filter (/= '+') text
let frequencies = parseInts $ lines strippedText
print $ sum frequencies
TODO
Here's your list of things to do before we meet next:
See you next time.
Sincerely,