1
+ import Data.Semigroup
2
+ import Data.Monoid
3
+ import Control.Monad.Writer
4
+
5
+ -- 1. Какое из выражений Haskell, приведенных ниже, соответствует
6
+ -- выражению chores(mow(lawn), repair(leaking + faucet)) на языках
7
+ -- C, Java или Python?
8
+
9
+ -- (а) chores (mow (lawn)) repair (leaking + faucet)
10
+ -- (б) chores (mow lawn) repair (leaking + faucet)
11
+ -- (в) chores (mow lawn) (repair (leaking + faucet))
12
+ -- (г) chores (mow lawn) (repair leaking + faucet)
13
+
14
+ -- Ответ: в)
15
+
16
+ -- 2. Существует ли выражение e и типы t1, t2, такие что (e :: t1) и
17
+ -- (e :: t2) возвращают разные значения? Если да, приведите пример и
18
+ -- напишите, когда определяется способ вычисления значения e: во время
19
+ -- компиляции или во время исполнения программы. Если таких значений и
20
+ -- типов не существует, объясните, почему.
21
+
22
+ -- Да, существует:
23
+ -- 3 <> 3 :: Sum Int
24
+ -- Sum {getSum = 6}
25
+ -- 3 <> 3 :: Product Int
26
+ -- Product {getProduct = 9}
27
+ -- Это выражение может вычисляться по-разному в зависимости от своего типа, способ вычисления
28
+ -- определяется во время компиляции.
29
+
30
+ -- 3. Рассмотрим терм uncurry (flip (,)).
31
+ -- (а) Найдите его тип.
32
+ -- (б) Подставьте в терм определения функций uncurry и flip из Prelude
33
+ -- и напишите цепочку редукций, которая заканчивается нормальной формой.
34
+ -- (в) Объясните, что делает функция, представленная исходным термом.
35
+
36
+ -- Напоминание: (,) :: a -> b -> (a, b) есть конструктор упорядоченных
37
+ -- пар.
38
+ -- Ответ:
39
+ -- а) uncurry (flip (,)) :: (b, a) -> (a, b)
40
+
41
+ -- б) Выпишем типы используемых функций:
42
+ -- uncurry :: (a -> b -> c) -> (a, b) -> c
43
+ -- flip :: (a -> b -> c) -> b -> a -> c
44
+ -- (,) :: a -> b -> (a, b)
45
+
46
+ -- Подстановка по шагам:
47
+ -- 1. flip (,) : (a -> b -> (a, b)) -> b -> a -> (a, b)
48
+ -- это редуцируется до flip (,) : b -> a -> (a, b) - исключается тип для (,)
49
+ -- 2. uncurry (flip (,)) : (b -> a -> (a, b)) -> (b, a) -> (a, b)
50
+ -- далее рудуцируется часть для flip (,) : b -> a -> (a, b)
51
+ -- 3. Получаем
52
+ -- uncurry (flip (,)) : (b, a) -> (a, b)
53
+ -- нормальную форму
54
+
55
+ -- в) Принимает на вход кортеж (b, a), на выходе получается перевернутый кортеж (a, b)
56
+
57
+ -- 4. Большинство студентов сделало задание 16 в Formula.hs
58
+ -- неправильно. В связи с этим рассмотрим следующую задачу. Тип
59
+ -- деревьев с произвольным коэффициентом ветвления задается так.
60
+
61
+ data Tree i a = Leaf a | Node (i -> Tree i a )
62
+
63
+ -- Тип i выступает в роли индексного множества. Поддеревья каждого
64
+ -- внутреннего узла индексируются элементами i. Так, если i = Bool,
65
+ -- то это двоичное дерево, если i задано определением
66
+ -- data I = I1 | I2 | I3
67
+ -- то это троичное дерево, а если i = Int, то коэффициент ветвления
68
+ -- равен бесконечности.
69
+
70
+ -- Объявите конструктор типов Tree i членом класса Foldable,
71
+ -- определив функцию foldMap.
72
+
73
+ instance (Bounded i , Enum i ) => Foldable (Tree i ) where
74
+ foldMap f (Leaf x) = f x
75
+ foldMap f (Node n) = foldr1 (<>) (map (foldMap f . n) [minBound .. maxBound ])
76
+
77
+ -- Напишите функцию fringe, которая возвращает список элементов,
78
+ -- хранящихся в листьях дерева. Функция fringe не должна явно
79
+ -- использовать рекурсию, а вместо этого должна использовать foldMap с
80
+ -- моноидом эндоморфизмов на типе списков. Обратите внимание, что это
81
+ -- не то же самое, что моноид списков.
82
+
83
+ fringe :: (Bounded i , Enum i ) => Tree i a -> [a ]
84
+ fringe t = appEndo (foldMap (Endo . (\ x y-> [x] ++ y)) t) []
85
+
86
+ -- Объявим бинарное дерево
87
+ type BinaryTree = Tree Bool Int
88
+
89
+ insert :: BinaryTree -> BinaryTree -> Bool -> BinaryTree
90
+ insert left right idx
91
+ | idx == True = left
92
+ | otherwise = right
93
+
94
+ tree = Node (insert(Leaf 1 ) (Leaf 2 ))
95
+ tree2 = Node (insert (Node (insert (Leaf 1 ) (Leaf 2 ))) (Node (insert (Leaf 3 ) (Leaf 4 ))) )
96
+
97
+ -- 5. Почти все студенты неправильно поняли определение монотонной
98
+ -- булевой функции и соответственно неправильно написали функцию
99
+ -- monotone в проекте. В связи с этим напишите аналогичную функцию,
100
+ -- которая принимает булеву функцию f от n аргументов и проверяет,
101
+ -- является ли она монотонной. Определение монотонной функции и
102
+ -- алгоритм проверки на монотонность см. в книге:
103
+ -- Гаврилов Г.П., Сапоженко А.А. Задачи и упражнения по дискретной
104
+ -- математике. М.: Физматлит, 2005
105
+ -- на с. 75, а определение используемого при этом частичного порядка на
106
+ -- наборах аргументов — на с. 10.
107
+
108
+ allEnvs :: Int -> [[Bool ]]
109
+ allEnvs 0 = [[] ]
110
+ allEnvs n = [x: xs | x <- [False , True ], xs <- allEnvs (n- 1 )]
111
+
112
+ compareXY :: ([Bool ], [Bool ]) -> Bool
113
+ compareXY ([] , [] ) = True
114
+ compareXY (x: xs, y: ys) =
115
+ not (x && not y ) && compareXY (xs, ys)
116
+
117
+ monotone' :: [Bool ] -> Bool
118
+ monotone' [x] = True
119
+ monotone' lst =
120
+ let halves = splitAt (length lst `div` 2 ) lst
121
+ in compareXY halves && monotone' (fst halves) && monotone' (snd halves)
122
+
123
+ monotone :: Int -> ([Bool ] -> Bool ) -> Bool
124
+ monotone n_args func =
125
+ let values = map func (allEnvs n_args)
126
+ in monotone' values
127
+
128
+ -- 6. Напишите функцию
129
+
130
+ avg :: [Int ] -> Float
131
+ avg lst =
132
+ let num_and_sum = snd (runWriter (numSum lst))
133
+ num = getSum (fst num_and_sum )
134
+ sum = getSum (snd num_and_sum )
135
+ in fromIntegral sum / fromIntegral num
136
+
137
+ -- которая возвращает среднее арифметическое списка. Эта функция
138
+ -- должна использовать функцию
139
+ -- numSum :: [Int] -> Writer (..., ...) ()
140
+
141
+ numSum :: [Int ] -> Writer (Sum Int , Sum Int ) ()
142
+ numSum [] = writer (mempty , (0 , 0 ))
143
+ numSum (x: xs) = do
144
+ numSum xs
145
+ tell (1 , Sum x)
146
+ return mempty
147
+
148
+ -- с некоторыми типами вместо многоточий, которая вычисляет количество
149
+ -- и сумму элементов в списке. Функции numSum и avg должны быть
150
+ -- аналогичны одноименным методам в следующей программе на Java, за
151
+ -- исключением того, что прохождение списка должно, как обычно,
152
+ -- использовать рекурсию, а не цикл for.
153
+
154
+ -- public class Avg {
155
+ -- private int s;
156
+ -- private int n;
157
+ --
158
+ -- private void update(int x) { s += x; n++; }
159
+ --
160
+ -- private void numSum(int[] a) {
161
+ -- for (int i = 0; i < a.length; i++)
162
+ -- update(a[i]);
163
+ -- }
164
+ --
165
+ -- public float average(int[] a) {
166
+ -- s = 0; n = 0;
167
+ -- numSum(a);
168
+ -- return (float)s/n;
169
+ -- }
170
+ --
171
+ -- public static void main(String[] args) {
172
+ -- Avg avg = new Avg();
173
+ -- int[] a = {1, 3, 2, 6, 4, 5};
174
+ -- System.out.println("The average is " + avg.average(a));
175
+ -- }
176
+ -- }
177
+
178
+ -- Другой способ сделать avg с монадой writer :
179
+ avg2 :: [Int ] -> Float
180
+ avg2 lst =
181
+ let sum_num = runWriter (numSum2 lst)
182
+ sum = fst sum_num
183
+ num = getSum (snd sum_num)
184
+ in fromIntegral sum / fromIntegral num
185
+
186
+ numSum2 :: [Int ] -> Writer (Sum Int ) Int
187
+ numSum2 [] = return 0
188
+ numSum2 (x: xs) = do
189
+ t <- numSum2 xs
190
+ tell 1
191
+ return (x + t)
192
+
193
+ -- 7. Напишите функцию allSums, которая возвращает суммы всех
194
+ -- подпоследовательностей списка-аргумента. Элементы
195
+ -- подпоследовательности могут быть разделены в исходном списке.
196
+ -- Порядок сумм неважен, и некоторые суммы могут совпадать. Например,
197
+
198
+ -- > allSums []
199
+ -- [0]
200
+ -- > allSums [2]
201
+ -- [2,0]
202
+ -- > allSums [1,3,4]
203
+ -- [8,4,5,1,7,3,4,0]
204
+
205
+ -- Используйте монаду списков и do-нотацию. Может быть полезным
206
+ -- использовать список [True, False]. Библиотечные функции (кроме (+)
207
+ -- и return) в основном решении использовать нельзя, но можно написать
208
+ -- альтернативные решения с функциями из Control.Monad.
209
+
210
+ allSubsets :: (Monad m ) => (m Bool ) -> [Int ] -> m [Int ]
211
+ allSubsets _ [] = return []
212
+ allSubsets p (x: xs) = do
213
+ flg <- p
214
+ ys <- allSubsets p xs
215
+ return (if flg then x: ys else ys)
216
+
217
+ allSums :: [Int ] -> [Int ]
218
+ allSums xs = [subsetSum s| s <- all_subsets]
219
+ where all_subsets = allSubsets [True , False ] xs
220
+ subsetSum :: [Int ] -> Int
221
+ subsetSum [] = 0
222
+ subsetSum (x: xs) = x + subsetSum xs
223
+
224
+ -- Решение с использованием библиотечных функций:
225
+ allSums2 xs = map sum $ filterM(const [True , False ]) xs
0 commit comments