-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathWeek03Live.hs
154 lines (122 loc) · 4.43 KB
/
Week03Live.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
{-# LANGUAGE ScopedTypeVariables #-}
module Week03Live where
import Prelude hiding (id, ($), (.), flip, map, filter)
-- Week 03 : HIGHER ORDER FUNCTIONS
-- This week : coursework to be released before Friday lecture.
-- worth 50%
-- Deadline : 17:00 Tuesday 3rd December 2024
id :: forall a. a -> a
id x = x
($) :: forall a b. (a -> b) -> a -> b
($) = id
-- Composition
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(.) f g = \x -> f (g x)
-- Pipe (|>)
(|>) :: forall a b. a -> (a -> b) -> b
(|>) = flip ($)
-- (|>) a f = f a
-- flip
flip :: forall a b c. (a -> b -> c) -> (b -> (a -> c))
flip f b a = f a b
-- partialApply
partialApply :: ((a, b) -> c) -> a -> (b -> c)
partialApply f x = \ y -> f (x, y)
------------------------------------------------------------------------------
-- map
map :: forall a b. (a -> b) -> [a] -> [b]
map f [] = []
map f (x : xs) = f x : map f xs
-- filter
filter :: (a -> Bool) -- test p
-> [a] -- values xs
-> [a] -- only the x in xs that satisfy p
filter p [] = []
filter p (x : xs)
| p x = x : filter p xs
| otherwise = filter p xs
-- dupAll
dupAll :: [a] -> [a]
dupAll xs = xs |> map (\x -> [x,x]) |> concat
-- xs |> map (\x -> x : x : )
-- Duplicating every element of a list by generating two-element lists
-- and then concatenating:
-- [1,2,3,4]
-- map (\x -> [x,x]) gives [[1,1], [2,2], [3,3], [4,4]]
-- concat gives [1,1,2,2,3,3,4,4]
-- dupAll [1,2,3,4] = [1,1,2,2,3,3,4,4]
-- What if we do something else in the mop?
--
-- Instead of constructing a two-element list for every element of the
-- input list, what if we return a _function_ that prepends two
-- elements to a list?
--
-- [1,2,3,4] |> map (\x ys -> x : x : ys)
-- gives
-- [\ys -> 1 : 1 : ys, \ys -> 2 : 2 : ys, \ys -> 3 : 3 : ys, \ys -> 4 : 4 : ys]
--
-- If we now 'map (\f -> f [])' after this, we fill in each 'ys' with
-- '[]', then we get a list of two-element lists again:
--
-- [1,2,3,4] |> map (\x ys -> x : x : ys) |> map (\f -> f [])
-- gives
-- [[1,1],[2,2],[3,3],[4,4]]
--
-- But there are more things we can do. Since we have a list of
-- functions, we can compose them all together with a function like
-- this:
composeAll :: [a -> a] -> a -> a
composeAll [] = id
composeAll (f:fs) = f . composeAll fs
-- [1,2,3,4] |> map (\x ys -> x : x : ys) |> composeAll
-- gives
-- [1,1,2,2,3,3,4,4]
--
-- Exercise: Why? Can you write out the steps that lead to this
-- answer?
--
-- So we get the same answer as before, but this idea of taking the
-- rest of the output as 'ys' enables extra power. Effectively we are
-- getting access to the “future” result. In this example we are then
-- prepending two copies of each element to this future result. But we
-- can do a bit more:
--
-- [1,2,3,4] |> map (\x ys -> [x] ++ ys ++ [x]) |> composeAll
-- gives
-- [1,2,3,4,4,3,2,1]
--
-- At every step, this puts each element of the input at the beginning
-- and end of the rest of the results, leading to this "balanced"
-- output.
--
-- This kind of "access to the future" is surprisingly useful. In some
-- programming languages it is possible to queue up things to do after
-- the current task has finished. In the Go programming langauge, for
-- example, there is a 'defer' instruction which adds some code to run
-- when the current function finishes. This is used to add "clean up"
-- code, similar to 'finally' blocks in Java. See
-- https://go.dev/blog/defer-panic-and-recover .
------------------------------------------------------------------------------
-- We didn't do this in the lecture, but it is similar to the
-- exercises at the end of the Week 03 Problems file.
data Formula a
= Atom a
| And (Formula a) (Formula a)
| Or (Formula a) (Formula a)
| Not (Formula a)
deriving Show
eval :: Formula Bool -> Bool
eval (Atom b) = b
eval (And p q) = eval p && eval q
eval (Or p q) = eval p || eval q
eval (Not p) = not (eval p)
evalWith :: (a -> Bool) -> Formula a -> Bool
evalWith valuation (Atom a) = valuation a
evalWith valuation (And p q) = evalWith valuation p && evalWith valuation q
evalWith valuation (Or p q) = evalWith valuation q || evalWith valuation q
evalWith valuation (Not p) = not (evalWith valuation p)
mapFormula :: (a -> b) -> Formula a -> Formula b
mapFormula f (Atom a) = Atom (f a)
mapFormula f (And p q) = And (mapFormula f p) (mapFormula f q)
mapFormula f (Or p q) = Or (mapFormula f p) (mapFormula f q)
mapFormula f (Not p) = Not (mapFormula f p)