0:27 — введение в функциональное программирование
4:35 — блоки
9:04 — класс Proc и Lambda
18:09 — функциональный стиль
31:41 — отложенные вычисления (lazy-объект)
41:27 — цепочка вызовов методов
43:10 — метод map/collect
44:00 — метод select
45:09 — метод inject/reduce
46:55 — метод #tap
48:11 — метод #yield_self
49:37 — библиотека dry-rb
54:05 — примеры решения задач
58:18 — полезные особенности Ruby
59:16 — генерация методов с помощью define_method
1:01:10 — пример кода с неизвестными методами
1:04:00 — Domain Specific Language (DSL)
1:07:20 — область применения Ruby
1:08:52 — скрипты администрирования
1:10:28 — управление развертыванием приложений
1:13:33 — мониторинг сервисов в распределенной системе
1:14:17 — создание «настольных» приложений
1:14:53 — тестирование приложений
1:22:10 — научные вычисления
1:23:03 — прототипирование протоколов взаимодействия
1:23:51 — Ruby-подобные языки программирования
1:25:41 — полезные ссылки
name_lenght = ['bethoven', 'mike', 'mic', 'michel'].inject(0) do |accumulator, dog_name|
accumulator += dog_name.length
end
puts name_lenght
# 21
Code language: Ruby (ruby)
Ruby не является истинным языком функционального программирования
Он является — объектным, императивным с функциональным стилем
Императивный стиль — написание отдельного действия, которое тут же выполняется. Императив — приказ
В функциональном программировании, принципиально другой подход. Хранится не состояние, а функция.
Функция в основе всего
● Функция может быть аргументом другой функции
● Хранится не состояние программы, а функции и аргументы
● Значения неизменны. Новое значение – новое имя
● Функции должны быть чистыми
Достоинства функционального программирования:
● Компактный код
● Упрощение модульного тестирования
● Отложенные вычисления
● Распараллеливание без участия программиста
Одно из требований к языкам функционального программирование — возможность выполнить операции над функцией до того как были выполнены операции над значениями.
Пример. x2 / x
В императивном стиле. Если у нас значение 0 то мы должны честно возвести в квадрат и разделить на 0
В функциональном стиле есть функции есть возводящие в квадрат переменную и функция деления на 0. Мы можем сделать преобразования в этих функциях которые уберут необходимость делить на 0. То есть позволяет устранить проблемы до того как мы приступили к обработке данных.
Мы можем передать функцию (с условиями которые нам нужны) в качестве параметра другой функции или с помощью yield
Блок по сути экземпляры класса proc
Proc — сохраненный код, который можно вызывать где то еще
Объект как код для выполнения
a = Proc.new { |name| puts "Hello, #{name}!" }
a.call 'world' # => Hello, world!
Code language: Ruby (ruby)
Результат выполнения = результат последней
операции
b = Proc.new { |x| x * x }
c = b.call 4
puts c # 16
Code language: Ruby (ruby)
Proc:lambda
Proc:lambda — Возможность записать функцию в компактной форме (Объект как функция)
# Компактная форма записи
p a = ->(x) { x * x }
p a.call 2 # получим 4
# Традиционная форма записи
p b = lambda { |x| x + x }
p b.call 3 # получим 6
Code language: Ruby (ruby)
lambda vs Proc
# Proc – контейнер кода
def proc_test
Proc.new { puts 'proc'; return }.call
puts 'proc_test end' # сюда не попадаем никогда!
end
proc_test # => proc
# Код внутри lambda изолирован
def lambda_test
->{ puts 'lambda'; return }.call
puts 'lambda_test end'
end
lambda_test # => lambda
# lambda_test end
Code language: Ruby (ruby)
Когда мы вставляем в метод объект proc. Это равносильно если бы просто написали этот код в методе. Например если мы напишем в методе return то мы выйдем из этой ф-и, если мы передадим с помощью proc, то результат будет такой же.
Lambda не влияет на работу функции в которую она вставлена. Если мы передадим return с помощью lambda, то это вернет значение но не остановит выполнение остальной части ф-и в которую мы вставили эту lamdba
some_func = ->(x) do
x + 3
end # lambda
def d(just_param, x)
just_param.call(x) * just_param.call(x)
end
puts d(some_func, 7)
# 100
Code language: Ruby (ruby)
a = -> (x) {x * 10}
# #<Proc:0x00007fc4fba55178@(pry)
[1,2,3,4,5].map(&a)
# [10, 20, 30, 40, 50]
Code language: Ruby (ruby)
Lambda — выполняем эту функцию как отдельную, она не влияет на тот код который находиться вокруг
def g(x)
(yield x) * (yield x)
end
# анонимная ф-ия
puts g(7) {|x|x+3} # передали block в параметре
# передача ф-ии как блока
f1 = ->(x) {x + 3} # Определили lambda
puts g(7, &f1) # передали lambda в параметре
# & - ampersand operator
# Позволяет передать блок в параметре
# Преобразование method -> lambda
def f2(x)
x+3
end
puts (f2_l = method(:f2).to_proc).lambda? # true
puts g(7, &f2_l) # 100
Code language: Ruby (ruby)
Пример короткой записи изменения элементов в массиве в функциональном стиле
dd = ['ad','bb'].map(&:upcase)
# ["AD", "BB"]
Code language: PHP (php)
dogs = ['bethoven', 'mike', 'mic', 'michel'].select {|name| name if name.length <= 4}
puts dogs
# mike
# mic
Code language: PHP (php)
Пример кода с неизвестными методами
Перехват обращения к не существующему методу
Elements = {
html: ->(var) { "<html>#{var}</html>\n" },
head: ->(var) { "<head>#{var}</head>\n" },
title: ->(var) { "<title>#{var}</title>\n" },
body: ->(var) { "<body>#{var}</body>\n" },
div: ->(var) { "<div>#{var}</div>\n" },
green: -> (var) { "<span style='color: green'>#{var}</span>\n" },
br: -> { "<br/>" }
}
def method_missing(meth, *args, &block)
if (func = Elements[meth])
block ? func.call( block.call ) : func.call
else
# если не знаем, что это, вызываем обработчик предка
super.method_missing(meth, *args, &block)
end
end
result = html do
head { title { "Test page" } } +
body do
div do
"Uncolored string" + br + "test string"
green do
"Hello"
end
end
end
end
puts result
Code language: Ruby (ruby)
DSL
Пример Rspec
Полезные особенности Ruby
● Динамическое объявление методов
● Расширенный синтаксис без скобок
● Большое количество встроенных функций
● Наличие большого количества сторонних библиотек