170 likes | 289 Views
Д.з. Задача с листков: 1, sin 1, sin (sin 1)…. [1, sin 1, sin(sin 1),…] f x = x: f (sin x) sins = f 1 f 1 1 : f (sin 1) 1 : sin 1 : f (sin (sin 1)) 1 : sin 1 : sin (sin 1)) : f (sin (sin (sin 1))) …. Задача с листков: 1, - sin 1, sin (sin 1)….
E N D
Задача с листков:1, sin 1, sin (sin 1)… [1, sin 1, sin(sin 1),…] f x = x: f (sin x) sins = f 1 f 1 1 : f (sin 1) 1 : sin 1 : f (sin (sin 1)) 1 : sin 1 : sin (sin 1)) : f (sin (sin (sin 1))) …
Задача с листков:1, -sin 1, sin (sin 1)… [1, - sin 1, sin(sin 1), - sin(sin(sin 1)),sin(sin(sin (sin 1)))…] f x = x: f (- sin x) sins1 = f 1 • Еще вариант: f x sign = sign*x : f (sin x) (-sign) sins1 = f 1 1 f 1 1 1 : f (sin 1) (-1) 1 : - sin 1 : f (sin (sin 1)) 1 1 : - sin 1 : sin (sin 1) : f (sin (sin (sin 1))) (-1) …
ones [1, 11, 111, 1111, …] • Способ 1: Придумать формулу 99…99 (n цифр) = 10^(n+1) – 1 11…11 (n цифр) = (10^(n+1) – 1) / 9 • Способ 2: Придумать рекуррентное соотношение • следующее = 1 + предыдущее * 10 ones = lst 1 where lst n = n : lst (1+10*n)
zipWith Кстати, полезная функция: • zipWith zipWith f [x1, x2, x3] [y1, y2. y3] [f x1 y1, f x2 y2, f x3 y3] • Например: zipWith (+) [1,3] [20,50] [21,53]
Ленивая функции, возвращающая списки • Фактически значения возвращаются по одному, по требованию. Пример: список делителей • Задание 1: для данного числа найти список делителей dividers n = [i | i <- [2..n `div` 2], n `mod` i == 0] • Задание 2: для данного числа найти первый делитель firstDivider n = head (dividers n) • dividers будет искать столько делителей, сколько мы ее попросим (в данном случае 1)
Как ленивые функции обрабатывают списки? • lsin lsinn = sin n : lsin(n+1) • findNeg findNeg(x:xs) = if x < 0 then x else findNegxs findNeg (lsin 0) • lsinвычисляет sin 0 • findNegпроверяет sin 0 • lsinвычисляет sin 1 • findNegпроверяет sin 1 • … • Если одна функция создает список, а другая просматривает результат, они будут работать по очереди • На что похоже? • coroutine (сопрограммы) • генераторы (IEnumerable в C#)
Бесконечные списки и стандартные функции • Работают с бесконечными списками • map • filter • zip • zipWith • Не работают с бесконечными списками • length • sum • == • Скорее, не работает (хотя и может выдать ответ..) • list comprehension • М.б. только один бесконечный генератор, и только первый [(x,y)|x<-[1..],y<-[1..]] • Не работает!
Зацикливаем список • lst = 1:lst 1:1:1:… 1: • plusminus = 1:-1:plusminus 1:-1:1:-1:1:-1 1:-1: • Уже м.б. полезно! f [x1,x2,x3,x4,x5] x1 – x2 + x3 – x4 + x5 f xs = sum (zipWith (*) plusMinus xs) • Еще одно решение для задачи 2 на листках: sins1 = zipWith (*) plusMinus sins
cycle Кстати, забыл сказать, для порождения зацикленных списков есть полезная функция: • cycle cycle [1,-1] [1, -1, 1, -1, -1, …] cycle [1,2,3] [1,2,3,1,2,3,1,2,3,1,2,3,…]
Завязываем список в узел geom = 1 : map (/2) geom Называется: Прием "tying the knot"("завязывание в узел") • Еще одно решение задачи 1 на листке sins = 1 : map sin sins • Еще одно решение задачи из д.з. ones= 1 : map (\n -> 1+10*n) ones или ones = 1 : [1+10*n | n <- ones]
Можно понимать, как уравнение относительно последовательности [1, 1/2, 1/4, 1/8, 1/16, …] | v Делим все на 2 [1/2, 1/4, 1/8, 1/16, 1/32,…] | v Приписываем в начало 1 [1, 1/2, 1/4, 1/8, 1/16, …] Снова исходная последовательность! • Если придумали такую последовательность действий – это уже работающая программа на Haskell, осталось только ее записать geom = 1: map (/2) geom • И наоборот, если придумали программу, надо проверить, выполняется ли уравнение
Частично вычисленные данные – что есть похожее? • В C#:IEnumerable + блоки итераторов • Вообще это называется генераторы public static IEnumerable<double> Sinuses() { for (inti = 0; ; i++) { yield return Math.Sin(i); } } • Возвращает sin 0; sin 1; … по одному. • Только не список, а как что-то, почему можно пройти с помощью foreach
Пример использования foreach(var x in Sinuses()) { if (x > 0.99) break; Console.WriteLine(x); } • Для отладки бесконечных итераторов удобно использовать Take foreach(var x in Sinuses().Take(10)) { Console.WriteLine(x); }
Еще пример // Возвращает 1, 1/2, 1/4, 1/8, … public static IEnumerable<double> Geom() { double x = 1; for (;;) { yield return x; x /= 2; } } … foreach(var x in Geom().Take(10)) { Console.WriteLine(x); }