Thursday, November 17, 2011

LYAH Chapter 2 - Starting Out


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.
  1. ghci> succ 9 + max 5 4 + 1  --> 16
  2. 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.
  1. ghci> "Steve Buscemi" !! 6  
  2. 'B'  
  3. ghci> [9.4,33.2,96.2,11.2,23.25] !! 1  
  4. 33.2  
·         Can’t have a list containing lists of different types
·         When using <, <=, > and >= to compare lists, they are compared in lexicographical order
  1. ghci> [3,2,1] > [2,1,0]  --> True  
  2. ghci> [3,4,2] > [2,4]  --> True  
  3. 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 []  -->  *** ExceptionPrelude.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

  1. ghci> [1..20]  --> [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]  
  2. ghci> ['a'..'z']  --> "abcdefghijklmnopqrstuvwxyz"  
  3. ghci> ['K'..'Z']  --> "KLMNOPQRSTUVWXYZ"   
Ranges by step
  1. ghci> [2,4..20]  --> [2,4,6,8,10,12,14,16,18,20]  
  2. 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.10.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.
·         A basic comprehension for a set that contains the first ten even natural numbers is:
·         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"]  
  1. ghci> let adjectives = ["lazy","grouchy","scheming"]  
  2. 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]]  
  1. 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