'
quote character after the initial character is valud in a name. So x'
is a proper variable name. ghci
. Try these basic scalar operations 3 + 4
it * 2
(+) 4 5
:t
as in :t (+)
let g n = n + n
let { g 0 = 42 ; g n = n + n }
pattern match let { g n = n + n; g 0 = 42 }
first pattern overlaps second, not so good. let { fib 0 = 1 ; fib 1 = 1; fib n = fib(n-1) + fib(n-2) }
:t g
:show bindings
[ 1, 2, 4 ]
[ 3 .. 10000000 ]
texas range [1, 2 ] ++ [3, 4]
concatenation [ x | x <- [1 .. 20] ]
[ x | x <- [1 .. 20], mod x 2 == 1 ]
[ (x, y, z) | x <- [1 .. 5], y <- [1, 7], z <- [1, 3] ]
m
. The :{
and :}
brackets are just for when you are typing input directly into the interpreter. (See 2.4. Interactive evaluation at the prompt in http://www.haskell.org/ghc/docs/latest/html/users_guide/interactive-evaluation.html. The {
amd }
brackets, along with ;
are the "traditional" delimiter way of defining cases that is insensitive to white space. You normally use white space to indicate nesting, as for Python. -- Define the m function, where op is in prefix position |
:{ |
let { m op n [ ] = n |
; m op n (h:hs) = (op h (m op n hs)) |
} |
:} |
|
{- this is a long comment |
that covers three lines |
Of course, why not have yet another comment convention! -} |
|
-- The same form but in infix form |
:{ |
let { m op n [ ] = n |
; m op n (h:hs) = h `op` m op n hs |
} |
:} |
|
-- The same again, but using whitespace nesting and cases |
:{ |
let |
m op n [ ] = n |
m op n (h:hs) = (op h (m op n hs)) |
:} |
|
Prelude> let m op n [ ] = n
Prelude> :t m
m :: t -> t1 -> [t2] -> t1
m op n (h:hs) = (op h (m op n hs))
gives additional type information. It gives n the type t, and so knows the return type of m is t. It gives elements of [ ] the type t1, and then knows that the type of h must be t1. But h appears as the first argument of op, and the result type of m is the second argument of op, and the entire right hand side is of type t, so op must have type t1 -> t -> tPrelude> :t m
m :: (t1 -> t -> t) -> t -> [t1] -> t
m (*) 1 [1..3]
:
is cons
. let flip (x:y:z) = y : x : z |
flip [1, 2, 3, 4] |
flip [1, 2] |
flip [1] |
flip [2] |
flip "Hello" |
|
:{ |
let { flip [] = [] |
; flip (x:[]) = [x] |
; flip (x:y:z) = y:x:z |
} |
:} |
|
-- let shuffle (h1:hs1) (h2:hs2) = h1 : h2 : (shuffle hs1 hs2) |
let shuffle (h1:hs1) h2 = h1 : (shuffle h2 hs1) |
let ones = 1 : ones |
take 10 (shuffle ones [2..]) |
|
-- the safer version of shuffle |
:{ |
let { shuffle [ ] h = h |
; shuffle h [ ] = h |
; shuffle (h1:hs1) h2 = h1 : (shuffle h2 hs1) |
} |
:} |
|
|
ghci HaskellIntro-2.hs
, then in interactive mode you can try :help
, :reload
, :load
, etc. -- lequal :: a => [a] -> [a] -> Boolean |
lequal [] [] = True |
lequal (x:xs) [] = False |
lequal [] (y:ys) = False |
lequal (x:xs) (y:ys) = (x == y) && (lequal xs ys) |
|
-- examples of using wildcard _ |
|
lhead (x:_) = x |
|
ltail (_:xs) = xs |
|
llast [x] = x |
llast (_:xs) = llast xs |
|
linit [x] = [] |
linit (x:xs) = x : linit xs |
|
lnull [] = True |
lnull (_:_) = False |
|
-- remove duplicates |
nub [] = [] |
nub (x:xs) = x : nub (remove x xs) |
where |
remove y [] = [] |
remove y (z:zs) | y == z = remove y zs |
| otherwise = z : remove y zs |
|
|
-- lambda abstraction |
-- map (\x -> x + x) [1, 2, 3] |
|
-- map and filter |
|
lmap f [] = [] |
lmap f (x:xs) = (f x) : lmap f xs |
|
lfilter p [] = [] |
lfilter p (x:xs) | p x = x : filter p xs |
| otherwise = filter p xs |
|
-- shuffle two lists |
shuffle [] y = y |
shuffle x [] = x |
shuffle (x:xs) y = x : (shuffle y xs) |
|
-- infinite list of ones |
ones = 1 : ones |
|
-- induction over naturals |
-- f(0) = c |
-- f(n+1) = h(f(n)) |
|
-- natural recursive definition |
foldn h c 0 = c |
foldn h c n = h (foldn h c (n-1)) |
|
-- :t foldn (\x -> 1 + x) 2 |
|
-- induction and recursion over lists |
lfoldr f z [] = z |
lfoldr f z (x:xs) = (f x (lfoldr f z xs)) |
|
lfoldl f z [] = z |
lfoldl f z (x:xs) = (lfoldl f (f z x) xs) |
|
-- reverse a list efficiently |
-- note: in (f z x) of foldl, z is the reversed list being constructed |
-- in rev, the function f tacks a new element onto the list being constructed |
rev = foldl (\ ys y -> y:ys) [] |
|
-- data types are defined by constructor patterns |
-- Eq equality can be derived from structural equality |
|
data BinTree = L | N BinTree BinTree deriving (Eq, Show) |
|
makeBinTree 0 = L |
makeBinTree n = N (makeBinTree (n-1)) (makeBinTree (n-1)) |
|
size L = 1 |
size (N t1 t2) = 1 + size t1 + size t2 |
|
-- exercise: given n construct a binary tree of size n |
|
-- exercise: trees over strings |
:t T
:t N
let x = makeBinTree 5
size x
=
symbol is NOT the operational assignment of a procedural language. It should be thought of as a name-binding equation that assigns a name to an expression. For example, you can read x = 1
as x
is assigned to 1, or bound to 1. ::
, and then define its value. For example this declares x
to be an Integer
(arbitrary precision) and binds it to 1 + y
. x :: Integer
x = 1 + y
It is possible to have a definition like: x :: Integer
x = 1 + x
which has non-terminating reductions. When the value is non-termination we express this with the bottom symbol ⏊.
11. Haskell Intro Notes on Functional Programming / Version 2.10 2014-02-24 |