165 lines
6.6 KiB
Markdown
165 lines
6.6 KiB
Markdown
# What the Hell am I doing?
|
|
Learning about GHCI!
|
|
|
|
One caveat: doing negative numbers
|
|
|
|
5 * -3 will cause issues because it's not clear what '-' actually represents...
|
|
Does it mean subtraction or negation?
|
|
5 * (-3) fixes this issue.
|
|
|
|
## Boolean Stuff
|
|
'&&' means 'and'
|
|
'||' means 'or'
|
|
'not' is a negation
|
|
|
|
Types are also the law of the land here. You can't mix and match types across operations they're not specified for already.
|
|
This makes sense and is intuitive. Some expressions like '5' can take multiple types though, for example being an integer or a float.
|
|
|
|
## A quick note on functions
|
|
*infix* functions are those that go in between arguments
|
|
- e.g 5+10
|
|
- or 2*2.3
|
|
|
|
*prefix* functions are those that go *before* arguments. They are more common with non number types
|
|
- succ 0
|
|
- min 9 10
|
|
|
|
You can also make prefix functions with two arguments into infix functions when you want. Here's an example
|
|
- div 92 10
|
|
This integral divides 92 by 10, yielding 9. But at first glance, it is not clear what is the divisor. So instead we can write
|
|
- 92 'div' 10
|
|
This is easy and more clear for some applications.
|
|
|
|
# Baby's First Functions
|
|
1. I created my first Haskell program! It is called 'baby.hs'
|
|
2. It gets loaded into GHCI by doing ':l baby'
|
|
3. Now I created 'doubleUs', and loaded it the same way
|
|
I notice that things are 'a' type. They accept floats and integers no problem...
|
|
4. Now, we can rewrite 'doubleUs'
|
|
```haskell
|
|
doubleUs x y = doubleMe x + doubleMe y
|
|
```
|
|
This makes things flexible! We know too that doubleMe is obviously correct, so we don't have to spend more time making sure our implementation in doubleUs is correct too.
|
|
|
|
Notably, functions are not ordered in a Haskell script. It does not matter if doubleUs comes before or after doubleMe.
|
|
|
|
## if - then - else
|
|
Haskell REQUIRES an else statement. This is because functions are complete, and therefore they must return *something*.
|
|
|
|
## Function Names are Funky
|
|
also, ' is a perfectly valid character in naming things in Haskell. It is usually used for a couple of reasons
|
|
1. To denote that a function is 'strict', that is to say not lazy
|
|
2. To denote a slightly modified version of a function
|
|
|
|
The other notable thing about function names: they can't begin with capital letters.
|
|
|
|
Functions that don't take any parameters are **definitions** or **names**.
|
|
|
|
# An intro to lists
|
|
Lists in Haskell are *homogenous*. Every element in a list is the same data type.
|
|
|
|
Lists can be easily concatenated with '++'. This has a weird feature though in that Haskell cycles through the whole list on the LHS before concatenating. This can take a long time with bigger lists.
|
|
|
|
':' can sometimes solve this issue by putting the thing to be added right at the front of a list. This is instantaneous in comparison.
|
|
|
|
Lists can be indexed by '!!'. Indexes start at 0.
|
|
|
|
Lists, if their elements can be compared, are comparable. They are an 'all' comparison by default, first from the head and then by each element therafter. They return a SINGLE true or false.
|
|
|
|
## Here's some basic list operations:
|
|
Head: The first entry
|
|
Tail: Everything but the head
|
|
Init: Everything but the last
|
|
Last: Take a guess, Einstein.
|
|
|
|
length: says the length!
|
|
null: checks if the list is empty ('[]')
|
|
reverse: Reverses the list
|
|
take n: takes n first elements from the list. If n is greater than length(list), it only returns the whole list
|
|
drop n: does the opposite of take, drops n elements from the beginning of the list
|
|
maximum or minimum: expected
|
|
sum, product: expected
|
|
elem thing list: elem takes a thing, and looks for it in the list. If it is, it returns true. if not, false.
|
|
|
|
## Ranges
|
|
Ranges can be created using '..'.
|
|
[1..20] is all integers from 1 to 20
|
|
['a'..'z'] is all letters from a to z
|
|
|
|
Ranges can also be a little more clever and use a step:
|
|
[1,2..20] gives all EVEN numbers 1 to 20.
|
|
[3,6..20] gives all multiples of 3 between 3 and 20.
|
|
|
|
[1,2,4,8,16..100] will NOT work however, because you cn only specify one step.
|
|
|
|
Ranges don't work backwards by default. A step must be specified:
|
|
[20..1] FAIL
|
|
[20,19..1] pass!
|
|
|
|
Floats can get funky in ranges
|
|
[0.1, 0.3..1] yields [0.1,0.3,0.5,0.7,0.899999999999999,1.099999999999999999]... weird.
|
|
|
|
Infinite ranges are also a thing. [13,26..] is a totally valid statement. Because Haskell is 'lazy', we can say take 24 [13,25..] and get the first 24 elements of this range.
|
|
|
|
cycle takes a list and cycles it into an infinite list. This let's take work ad infinitium.
|
|
repeat does a similar thing but only with one element. But replicate does this too and is arguably easier
|
|
replicate n x = [x,x..x] where length [x,x..x] = n
|
|
|
|
## List Comprehensions
|
|
They're very similar to set building notation.
|
|
```haskell
|
|
-- instead of the following
|
|
take 10 [2, 4..]
|
|
|
|
-- we can write
|
|
[x*2 | x <- [1..10]]
|
|
|
|
-- we can also add predicates
|
|
[x*2 | x <- [1.10], x*2 >= 12]
|
|
-- will only yield even numbers greater than 12 in the first 10 elements of the set
|
|
```
|
|
|
|
boomBangs is an interesting one. We can write code that does a function as the function, while also incorporating predicates.
|
|
|
|
An easy way to remember: [Function | Set, Predicate, (predicate 2),...]
|
|
Fucking Stupid Predicates
|
|
|
|
We can also use multiple lists. If multiple lists are given (one for each variable), then all combinations of the two lists are used.
|
|
|
|
Strings are just lists. We can apply list comprehensions to them!
|
|
Here's a couple of useful examples:
|
|
```haskell
|
|
-- Now Writing our own custom list function
|
|
length' xs = sum [1 | _ <- xs]
|
|
-- use dummy variable _, draw an element from a list and replace it with 1. Sum all of those.
|
|
|
|
--- Strings are lists! We can use list comprehensions on them.
|
|
removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
|
|
```
|
|
|
|
Finally, list comprehensions can be nested. So we can use a comprehension to extract a list from a set of lists, and then use another comprehension on that lsit itself.
|
|
|
|
## Tuples
|
|
Tuples are somewhat confused with lists, but they are not exactly the same.
|
|
|
|
Tuples are useful in specific cases. Namely, when an explicit number of elements and their types are known. Tuples are designated by parentheses '()'
|
|
|
|
Also interestingly, tuples can have a mish mash of types inside them. The specific combination of types inside a tuple makes the tuple itself a certain type:
|
|
|
|
```haskell
|
|
[(1,2), (3,4), (5,6)] -- is okay!
|
|
[(1,2), (8,5,11), (5,6)] -- is NOT okay!
|
|
[(1,2), ('Okay?', 2)] -- is NOT okay!
|
|
```
|
|
|
|
Tuples have special names for their sizes:
|
|
(1,2) - Pair
|
|
(3,4,5) - Triple
|
|
(4,5,6,7) - 4-tuple
|
|
(2,3,..,n) - n-tuple
|
|
|
|
Here's some useful tuple functions:
|
|
fst: takes a pair and returns first component
|
|
snd: Suprise! gives the second component of a pair
|
|
zip: Takes two lists and makes pairs of the elements. Stops at the shorter list's end.
|