Chapter 2: Starting Out
Calling
Functions
Baby’s First
Functions
An Intro to
Lists
Concatenation
Accessing
List Elements
Lists
Inside Lists
Comparing
Lists
More
List Operations
Texas Ranges
I’m a List
Comprehension.
Tuples
Using
Tuples.
Using
Pairs
Finding
the Right Triangle
Function Summary
·
succ function
takes anything that has a defined successor and returns that successor.
·
min returns the
one that's lesser and max returns the
one that's greater.
·
div
·
if … then … else … (an
expression)
·
: (cons)
takes a number and a list of numbers or a character and a list of characters,
·
++ takes two
lists.
·
[] is an empty list
·
!! gets an
element out of a list by index – indices start at 0.
·
head
takes a list and returns its head. The head of a list is basically its first
element.
·
tail takes a
list and returns its tail. In other words, it chops off a list's head.
·
last takes a
list and returns its last element.
·
init takes a
list and returns everything except its last element.
·
length
takes a list and returns its length, obviously.
·
null checks if
a list is empty. If it is, it returns True, otherwise
it returns False.
·
reverse
reverses a list.
·
take takes
number and a list. It extracts that many elements from the beginning of the
list.
·
drop
works in a similar way, only it drops the number of elements from the beginning
of a list.
·
maximum
takes a list of stuff that can be put in some kind of order and returns the
biggest element.
·
minimum
returns the smallest.
·
sum takes a
list of numbers and returns their sum.
·
product takes a
list of numbers and returns their product.
·
elem takes a
thing and a list of things and tells us if that thing is an element of the
list.
·
cycle
takes a list and cycles it into an infinite list
·
repeat takes an
element and produces an infinite list of just that element
·
replicate :: Int -> a -> [a] some
number of the same element in a list.
· list
comprehension: [ output fn | variable <- input set, predicate ]
·
ghci> [x*2 | x <- [1..10]] --> [2,4,6,8,10,12,14,16,18,20]
·
_
means that we don't care what we'll draw from the list
·
fst
takes a pair and returns its first component.
·
snd
takes a pair and returns its second component.
·
zip
– takes two lists and then zips them together into one list
by joining the matching elements into pairs.
Chapter 2 Starting Out
Ready, set, go!
:set
prompt "ghci> "
Equality
·
Testing for equality is done like so.
o
ghci> 5 == 5 à True
o
ghci> 5 /= 5 à False
o
ghci> "hello" == "hello" à True
·
What about doing 5 +
"llama"
No instance for (Num [Char])
arising from a use of `+' at <interactive>:1:0-9
Possible fix: add an instance declaration for (Num [Char])
In the expression: 5 + "llama"
In the definition of `it': it = 5 + "llama"
·
Whereas +
works only on things that are considered numbers, ==
works on any two things that can be compared. But the catch is that they both
have to be the same type of thing.
·
“*” is a function that takes two numbers and multiplies them. This
is what we call an infix
function.
Prefix
·
Most functions that aren't used with numbers are prefix functions. Function
application (calling a function by putting a space after it and then typing out
the parameters) has the highest precedence.
·
The succ
function takes anything that has a defined successor and returns that
successor.
·
min returns the
one that's lesser and max returns the
one that's greater.
- ghci> succ 9 + max 5 4 + 1 --> 16
- ghci> (succ 9) + (max 5 4) + 1 --> 16
·
If a function takes two parameters, we can also call it as an
infix function by surrounding it with backticks.
·
Doing div 92 10 same
as 92
`div` 10
·
Functions in Haskell don't have to be in any particular order, so
it doesn't matter if you define doubleMe
first and then doubleUs
or if you do it the other way around.
If Statement
·
doubleSmallNumber x = if x > 100 then x else x*2
·
The else part is mandatory
in Haskell.
·
If statement is an expression
(a piece of code that returns a value).
·
doubleSmallNumber' x = (if x > 100 then x else x*2) + 1
Prime
·
We usually use '
to either denote a strict version of a function (one that isn't lazy) or a
slightly modified version of a function or a variable.
Name/Definition
·
conanO'Brien = "It's a-me, Conan O'Brien!"
·
In the function name we didn't capitalize Conan's name. That's
because functions can't begin with
uppercase letters. We'll see why a bit later.
·
This is a definition
(or a name) – a function
doesn't take any parameters.
·
Because we can't change what names (and functions) mean once we've
defined them, conanO'Brien
and the string "It's a-me, Conan
O'Brien!" can be used interchangeably.
An intro to lists
·
homogenous data structure. It stores several
elements of the same type.
·
The ++
operator puts two lists together
o
ghci> [1,2,3,4] ++ [9,10,11,12] --> [1,2,3,4,9,10,11,12]
o
ghci> "hello" ++ " " ++ "world" --> "hello world"
o
ghci> ['w','o'] ++ ['o','t'] -->
"woot"
List Append Efficiency
o
When you put together two lists (even if you append a singleton
list to a list, for instance: [1,2,3] ++
[4]), internally, Haskell has to walk through the whole list on the
left side of ++.
o
That's not a problem when dealing with lists that aren't too big.
o
But putting something at the end of a list that's fifty million
entries long is going to take a while.
o
However, putting something at the beginning of a list using the : operator (also called the cons
operator) is instantaneous.
o
ghci> 'A':" SMALL CAT" --> "A SMALL CAT"
o
ghci> 5:[1,2,3,4,5] --> [5,1,2,3,4,5]
·
: (cons)
takes a number and a list of numbers or a character and a list of characters,
·
++ takes two
lists.
·
[1,2,3] is actually just syntactic sugar for 1:2:3:[].
·
[] is an empty list
·
[1]
is a singleton list
·
!! gets an
element out of a list by index – indices start at 0.
- ghci> "Steve Buscemi" !! 6
- 'B'
- ghci> [9.4,33.2,96.2,11.2,23.25] !! 1
- 33.2
·
Can’t have a list containing lists of different
types
·
When using <, <=, > and >=
to compare lists, they are compared in lexicographical order
- ghci> [3,2,1] > [2,1,0] --> True
- ghci> [3,4,2] > [2,4] --> True
- ghci> [3,4,2] == [3,4,2] --> True
List functions
·
head
takes a list and returns its head. The head of a list is basically its first
element.
·
tail takes a
list and returns its tail. In other words, it chops off a list's head.
·
last takes a
list and returns its last element.
·
init takes a
list and returns everything except its last element.
- ghci> head [] --> *** Exception: Prelude.head: empty list
·
length
takes a list and returns its length.
·
null checks if
a list is empty. If it is, it returns True, otherwise
it returns False.
·
Use this function instead of xs == []
ghci> null [] --> True
·
reverse
reverses a list.
·
take takes
number and a list. It extracts that many elements from the beginning of the
list. Watch.
1.
ghci> take 5 [1,2] --> [1,2]
2.
ghci> take 0 [6,6,6] --> []
·
drop
works in a similar way, only it drops the number of elements from the beginning
of a list.
·
maximum
takes a list of stuff that can be put in some kind of order and returns the
biggest element.
·
minimum
returns the smallest.
·
sum takes a
list of numbers and returns their sum.
·
product takes a
list of numbers and returns their product.
·
elem takes a
thing and a list of things and tells us if that thing is an element of the
list. It's usually called as an infix function because it's easier to read that
way
- ghci> 4 `elem` [3,4,5,6] --> True
- ghci> 10 `elem` [3,4,5,6] --> False
Ranges
- ghci> [1..20] --> [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
- ghci> ['a'..'z'] --> "abcdefghijklmnopqrstuvwxyz"
- ghci> ['K'..'Z'] --> "KLMNOPQRSTUVWXYZ"
Ranges by step
- ghci> [2,4..20] --> [2,4,6,8,10,12,14,16,18,20]
- ghci> [3,6..20] --> [3,6,9,12,15,18]
·
Can't do [1,2,4,8,16..100]
and expect to get all the powers of 2.
·
Can only specify one step.
·
Some sequences that aren't arithmetic are
ambiguous if given only by a few of their first terms.
·
Can't just do [20..1],
you have to do [20,19..1].
·
Don’t use floating points in list ranges because
they are not completely precise (by definition).
·
ghci> [0.1, 0.3 .. 1] --> [0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]
Infinite lists
·
E.g. first 24 multiples of 13.
·
Sure, you could do [13,26..24*13]
·
Better way: take 24
[13,26..]
·
Haskell won't try to evaluate the infinite list
immediately because it would never finish. It'll wait to see what you want to
get out of that infinite lists. And here
it sees you just want the first 24 elements and it gladly obliges.
·
cycle
takes a list and cycles it into an infinite list. If you just try to display
the result, it will go on forever so you have to slice it off somewhere.
·
ghci> take 10 (cycle [1,2,3]) --> [1,2,3,1,2,3,1,2,3,1]
·
ghci> take 12 (cycle "LOL ") --> "LOL LOL LOL "
·
repeat takes an
element and produces an infinite list of just that element. It's like cycling a
list with only one element.
·
ghci> take 10 (repeat 5) --> [5,5,5,5,5,5,5,5,5,5]
·
Although it's simpler to just use the replicate
function if you want some number of the same element in a list.
·
replicate 3 10 --> [10,10,10]
List comprehensions
·
Set
comprehensions used for building more specific sets out of general
sets.
·
The part before the pipe is called the output function, x
is the variable, N
is the input set and x <= 10 is the predicate.
That means that the set contains the doubles of all natural numbers that
satisfy the predicate.
·
ghci> [x*2 | x <- [1..10]] --> [2,4,6,8,10,12,14,16,18,20]
·
Filtering:
Add
a condition/predicate) to that comprehension.
1.
ghci> [x*2 | x <- [1..10], x*2 >= 12] --> [12,14,16,18,20]
1.
ghci> [ x | x <- [50..100], x `mod` 7 == 3] --> [52,59,66,73,80,87,94]
2.
ghci> let boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]
1.
ghci> boomBangs [7..13] --> ["BOOM!","BOOM!","BANG!","BANG!"]
2.
ghci> [ x | x <- [10..20], x /= 13, x /= 15, x /= 19] --> [10,11,12,14,16,17,18,20]
·
When drawing from several lists, comprehensions
produce all combinations of the
given lists and then join them by the output function we supply.
·
A list produced by a comprehension that draws
from two lists of length 4 will have a length of 16, provided we don't filter
them.
·
If we have two lists, [2,5,10]
and [8,10,11] and we want to get the products of all the
possible combinations between numbers in those lists, here's what we'd do.
·
ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]] --> [16,20,22,40,50,55,80,100,110]
·
All possible products that are more than 50?
1.
ghci> [ x*y | x <- [2,5,10], y <- [8,10,11], x*y > 50] --> [55,80,100,110]
1.
Combine a list of adjectives and a list of nouns
… for epic hilarity.
2.
ghci> let nouns = ["hobo","frog","pope"]
- ghci> let adjectives = ["lazy","grouchy","scheming"]
- ghci> [adjective ++ " " ++ noun | adjective <- adjectives, noun <- nouns] --> ["lazy hobo","lazy frog","lazy pope","grouchy hobo","grouchy frog", "grouchy pope","scheming hobo","scheming frog","scheming pope"]
·
_ means that we don't
care what we'll draw from the list. This function replaces every element of a
list with 1 and then sums that up.
·
length' xs = sum [1 | _ <- xs]
·
Nested list comprehensions are also possible if
you're operating on lists that contain lists. A list contains several lists of
numbers. Let's remove all odd numbers without flattening the list.
1.
ghci> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]
- ghci> [ [ x | x <- xs, even x ] | xs <- xxs] --> [[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]
Tuples
·
Tuples can also be used to represent a wide
variety of data. For instance, if we wanted to represent someone's name and age
in Haskell, we could use a triple: ("Christopher",
"Walken", 55).
·
Use tuples when you know in advance how many
components some piece of data should have.
·
Tuples are much more rigid because each
different size of tuple is its own type, so you can't write a general function
to append an element to a tuple — you'd have to write a function for appending
to a pair, one function for appending to a triple, one function for appending
to a 4-tuple, etc.
·
While there are singleton lists, there's no
such thing as a singleton tuple.
·
A singleton tuple would just be the value it
contains and as such would have no benefit to us.
·
Like lists, tuples can be compared with each
other if their components can be compared.
·
Only you can't compare two tuples of different
sizes, whereas you can compare two lists of different sizes.
·
Two useful functions that operate on pairs:
·
fst
takes a pair and returns its first component.
1.
ghci> fst (8,11) --> 8
2.
ghci> fst ("Wow", False) --> "Wow"
·
snd
takes a pair and returns its second component.
1.
ghci> snd (8,11) --> 11
2.
ghci> snd ("Wow", False) --> False
·
Note: these functions
operate only on pairs.
·
zip
– takes two lists and then zips them together into one list
by joining the matching elements into pairs.
·
Especially useful for when you want to combine
two lists in a way or traverse two lists simultaneously.
1.
ghci> zip [1,2,3,4,5] [5,5,5,5,5] --> [(1,5),(2,5),(3,5),(4,5),(5,5)]
2.
ghci> zip [1 .. 5] ["one", "two", "three", "four", "five"] --> [(1,"one"),(2,"two"),(3,"three"),(4,"four"),(5,"five")]
3.
ghci> zip [5,3,2,6,2,7,2,5,4,6,6] ["im","a","turtle"] --> [(5,"im"),(3,"a"),(2,"turtle")]
·
Because Haskell is lazy, we can zip finite lists
with infinite lists:
1.
ghci> zip [1..] ["apple", "orange", "cherry", "mango"] --> [(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")]
·
Combining tuples and list comprehensions: which
right triangle that has integers for all sides and all sides equal to or
smaller than 10 has a perimeter of 24?
·
ghci> let triangles = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ]
·
ghci> let rightTriangles = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2]
·
ghci> let rightTriangles' = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24]
·
ghci> rightTriangles' --> [(6,8,10)]
·
This is a common pattern in functional
programming. You take a starting set of solutions and then you apply
transformations to those solutions and filter them until you get the right
ones.
Questions
·
Q: Can you
do infix with more than two params?
·
Q: Can you
zip triples etc?
·
Q: Why
does this run out of memory: head (reverse [1..100000000])
But this come back straight away: last
[1..100000000] ??
No comments:
Post a Comment