
```python
def reduce(f, init, lst):
acc = init
for e in lst:
acc = f(acc, e)
return acc
reduce(
lambda a, b: a + b,
0,
[1,2,3])
```
```haskell
foldl :: (s -> a -> s) -> s -> [a] -> s
foldl _ _ [] = acc
foldl f acc (x:xs) = foldl f (f acc x) xs
foldl (+) 0 [1,2,3]
```
*Функции `map`, `fold`, `filter` позволяют скрыть структуру данных.*
----
### Map-Reduce
```erlang
start(Name, Age) ->
spawn(fun() -> loop(Name, Age) end).
loop(Name, Age) ->
receive
{description, From} ->
From ! {self(), io:format("~s is ~p years old", [Name, Age])},
loop(Name, Age);
{speak, Sound, From} ->
From ! {self(), io:format("~s says ~s", [Name, Sound])},
loop(Name, Age);
{happy_birth_day, From} ->
NewAge = Age + 1,
From ! {self(), NewAge},
loop(Name, NewAge)
end.
```
```erlang
description(Dog) ->
Dog ! {description, self()},
receive {Dog, Msg} -> Msg end.
speak(Dog, Sound) ->
Dog ! {speak, Sound, self()},
receive {Dog, Msg} -> Msg end.
happy_birth_day(Dog) ->
Dog ! {happy_birth_day, self()},
receive {Dog, NewAge} -> NewAge end.
```
```erlang
Dog = start("Buddy", 1), % => <0.93.0>
description(Dog), % => Buddy is 1 years old__ok
speak(Dog, "bark"), % => Buddy says bark__ok
happy_birth_day(Dog), % => 2
description(Dog). % => Buddy is 2 years old__ok
```
---
## Бесточечный стиль, комбинаторы
Комбинаторное программирование — парадигма программирования, не требующая явного упоминания аргументов определяемых функций и использующая вместо переменных комбинаторы и их композиции.
----
### Java streams
```java
Stream.of("a1", "a2", "a3")
.map(s -> s.substring(1))
.mapToInt(Integer::parseInt)
.max()
.ifPresent(System.out::println); // => 3
```
```java
List persons =
Arrays.asList(
new Person("Max", 18),
new Person("Peter", 23),
new Person("Pamela", 23),
new Person("David", 12));
String phrase = persons
.stream()
.filter(p -> p.age >= 18)
.map(p -> p.name)
.collect(Collectors.joining(
" and ",
"In Germany ",
" are of legal age."));
System.out.println(phrase);
// => In Germany Max and Peter and Pamela are of legal age.
```
Notes:
----
### Бесточечный стиль,
комбинаторы в Haskell
```haskell
foldl (+) 0 . map (\Just x -> x) . filter isJust . map readMaybe
```
где:
- `readMaybe` — пытаемся преобразовать строку в значение заданного типа. Пример:
- `readMaybe "1" :: Maybe Int <=> Just 1`
- `readMaybe "f" :: Maybe Int <=> Nothing`
- `(.)` — комбинация функций (`f x = g(h(x)) <=> f = g . h`)
- `\x -> ...` — анонимная функция от одного аргумента
----
### Трансдьюсеры
Transducers are composable algorithmic transformations. They are independent from the context of their input and output sources and specify only the essence of the transformation in terms of an individual element.
```clojure
(fn [coll]
(->> coll
(filter #(re-matches #"^\d+$" %))
(map #(Integer/parseInt %))
(reduce +)))
```
```clojure
(defn try-int [s]
(when (re-matches #"^\d+$" %)
(Integer/parseInt %)))
(fn [coll]
(->> coll
(keep try-int)
(reduce +)))
```
где:
- `(fn [x] ...)` и `#(... % ...)` — анонимные функции
- `re-match` — проверка регулярного выражения
- `(re-matches #"^\d+$" "1") <=> "1"`
- `(re-matches #"^\d+$" "a") <=> nil`
----
### Threading Macros (Clojure)
`->`
```clojure
(defn transform* [person]
(-> person
(assoc :hair-color :gray)
(update :age inc)))
```
```clojure
(defn transform* [person]
(-> person
(assoc ,,, :hair-color :gray)
(update ,,, :age inc)))
```
```clojure
(some-> x try-int inc)
```
`->>`
```clojure
(defn calculate* []
(->> (range 10)
(filter odd?)
(map #(* % %))
(reduce +)))
```
```clojure
(defn calculate* []
(->> (range 10)
(filter odd? ,,,)
(map #(* % %) ,,,)
(reduce + ,,,)))
```
----
**Джон Бэкус**. Можно ли освободить программирование от стиля фон Неймана? Функциональный стиль и алгебра программ

Объясните алгоритм?

---
## Рекурсия и вычислительная сложность
```haskell
fib 0 = 1
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
```
*Question*: В чём проблема реализации?

$O(\phi n)$
$\phi = 1 + \sqrt{\frac{5}{2}}$.
----
### Мемоизация. Python. Библиотека
Сохранение результатов выполнения функций для предотвращения повторных вычислений.
```python
import time
def dur(f):
t = time.time()
x = f()
print("dur: ", time.time()-t)
return x
def fib(n):
if n == 0 or n == 1: return 1
return fib(n-1) + fib(n-2)
dur(lambda: fib(32)) # => dur: 1.2896251678466797
import functools
fib = functools.cache(fib)
dur(lambda: fib(32)) # => dur: 2.8848648071289062e-05
```
----
### Мемоизация. Python. Реализация
```python
def mycache(f):
buf = {}
def foo(x):
if x in buf: return buf[x]
tmp = f(x)
buf[x] = tmp
return tmp
return foo
@mycache
def fib(n):
if n == 0 or n == 1: return 1
return fib(n-1) + fib(n-2)
# def fib(n):
# ...
# fib = mycache(fib)
dur(lambda: fib(32)) # => dur: 0.00015401840209960938
```
----
### Мемоизация. Haskell
```haskell
fib n = fiblist !! n
where
fiblist = 1 : 1 : ( zipWith (+) fiblist (tail fiblist) )
```
*Question*: Как данный пример работает?
```haskell
fiblist = 1 : 1 : ( zipWith (+) fiblist
................................(tail fiblist) )
fiblist = 1 : 1 : ( zipWith (+) [ 1 : 1 ...
................................[ 1 : ...
..................................\-> 2
fiblist = 1 : 1 : ( zipWith (+) [ 1 : 1 : 2...
................................[ 1 : 2 :...
......................................\-> 3
fiblist = 1 : 1 : ( zipWith (+) [ 1 : 1 : 2 : 3 ...
................................[ 1 : 2 : 3 ...
..........................................\-> 5
```
---
## Ссылочная прозрачность
An expression is called **referentially transparent** if it can be replaced with its corresponding value (and vice-versa) without changing the program's behavior.

Одинаково $\rightarrow$ взаимозаменяемо.
----
### Проверка на равенство. Разночтения
- `=` compares only numbers, regardless of type.
- `eq` compares symbols. Two objects are `eq` if they are actually the same object in memory. Don't use it for numbers and characters.
- `eql` compares symbols similarly to `eq`, numbers (type sensitive) and characters (case sensitive)
- `equal` compares more general objects. Two objects are `equal` if they are `eql`, strings of `eql` characters, bit vectors of the same contents, or lists of `equal` objects. For anything else, `eq` is used.
- `equalp` is like `equal`, just more advanced. Comparison of numbers is type insensitive. Comparison of chars and strings is case insensitive. Lists, hashes, arrays and structures are `equalp` if their members are `equalp`. For anything else, `eq` is used.
это Common Lisp. JS — всё понятно. А в Python?
----
#### Равенство в Python
```python
x = 0
y = 0
while True:
x += 1
y += 1
print(x, y, x is y, x == y)
if x is not y:
break
# => 1 1 True True
# => 2 2 True True
# => ...
# => 256 256 True True
# => 257 257 False True
```
----
#### Замыкания в Python
```python
def create_functions():
funcs = []
for i in range(4):
funcs.append(lambda: i)
return funcs
funcs = create_functions()
for f in funcs:
print(f())
```
Ожидаемый вывод?
```python
def create_functions_fixed():
funcs = []
for i in range(4):
funcs.append(lambda i=i: i)
return funcs
funcs = create_functions_fixed()
for f in funcs:
print(f())
```
----
#### Примеры эквивалентности
при ссылочной прозрачности
```python
a = [1, 2, 3]
b = [1, 2, 3]
c = {:a 1 :b 2}
d = {:a 1 :b 2}
```
```python
a = [1, 2, 3]
b = a
c = {:a 1 :b 2}
d = c
```
```python
a = f(x)
b = f(x)
c = a + b
```
```python
a = f(x)
c = a + a
```
Подстановка выражение, убирание дублирования.
----
```c
for (int x = 0; x < 100; x++) {
delete(x);
}
```
```c
for (int x = 0; x < 100; x += 5 ) {
delete(x);
delete(x + 1);
delete(x + 2);
delete(x + 3);
delete(x + 4);
}
```
Loop unrolling
```python
def foo(p, a, b):
if p(a):
return b
foo(is_even, x, bar(x))
```
```python
def foo(p, a, b):
if p(a):
return bar(x)
foo(is_even, x, x)
```
Reordering
----
```python
def double(x): return x + x
def quadruple(x): return double(double(x))
```
```python
def double(x): return 2 * x
def quadruple(x): return 2 * double(x)
```
```python
def quadruple(x): return 2 * 2 * x
```
```python
def quadruple(x): return 4 * x
print(quadruple(3)) # ==> 12
```
Лёгкий намёк на суперкомпиляцию.
----
И драматизация в рамках параллельного программирования:

----
### Сопоставление с образцом
```haskell
reverse xs = reverse' xs []
where
reverse' [] acc = acc
reverse' (x:xs) acc = reverse' xs (x:acc)
```
```text
reverse (1:2:[]) =>
reverse' (1:2:[]) [] =>
reverse' (2:[]) (1:[]) =>
reverse' (2:[]) (1:[]) =>
reverse' [] (2:1:[]) =>
(2:1:[])
```
```erlang
is_z_atom(z) -> true;
is_z_atom(_) -> false.
% is_z_atom(z). % => true
% is_z_atom(123). % => false
is_equal(X, X) -> true;
is_equal(_, _) -> false.
% is_equal(1, 2) % => false.
% is_equal(1, 1)% => true.
```
```clojure
(require '[clojure.core.match :refer [match]])
(doseq [n (range 1 101)]
(println
(match [(mod n 3) (mod n 5)]
[0 0] "FizzBuzz"
[0 _] "Fizz"
[_ 0] "Buzz"
:else n)))
```
----
```clojure
(def x (map #(throw %) (range 1000)))
; (prn x) ?
```
```clojure
(prn x) ; => Execution error ... (
; (prn x) ?
```
```clojure
(prn x) ; => ()
(def y (map #(if (= % 40) (throw %) %) (range 100)))
; (prn x)
; (prn x) ?
```
```clojure
(prn y) ; => Execution error ... (0 1 2 ...... 27 28 29 30
(prn y) ; => (0 1 2 3 4 5 6 7 8 9 10 11 ...... 27 28 29 30 31)
(let [z (map #(if (= % 2) (throw %) %) [1 2 3])]
(.start (Thread. #(doall z)))
(Thread/sleep 100)
z)
; Return or exception?
```
```clojure
; => ()
```