Конспект Евгения
Блоки
Блоки, процедуры и лямбды — замыкания (closures) — являются важным аспектом языка Ruby, с одной стороны, и малопонятным с другой. Малопонятным в основном из-за того, что Ruby подходит к замыканиям в значительной степени уникальным образом, к тому же в Ruby существуют разные способы использования замыканий.
Способ первый. Proc / Lambda
Любой кусок кода в руби можно выделить в особой тип объекта - блок. Блок кода явялется объектом класса Proc. для его создания необходимо выполнить
some_block = Proc.new { puts 'from some blcok' }
или
some_block = lambda do
puts 'from lambda'
end
Сохраненный таким образом код можно использовать по разному. можно передвать в функции в качестве параметров, можно просто вызвать. Для передачи параметров в блок нужно определить его принимающим параметры. Список переменных в |аргумент1| будет определять параметры блока.
some_block_with_parameter = lambda do |name|
puts 'from lambda'
end
для исполнения кода нужно вызвать метод .call объекта Proc, в зависимости от блока - с параметрами или без.
some_block.call
some_block_with_parameter.call :argument
а вот пример работы функции с блоков
def my_func(block)
puts 'before block called'
block.call
puts 'after block called'
end
some_block = Proc.new { puts 'from some blcok' }
my_func(block)
=>
before block called
from some blcok
after block called
Блок, переданный таким способом ничем не отличается от параметра, и значит мы можем передвать любое количество в любом порядке.
Способ второй. блоки
В отличие от от lambda/proc, блоки кода можно использовать только один раз, но и процедура их записи на порядок короче.
def my_func(&block)
puts 'before block called'
block.call
puts 'after block called'
end
# другой способ передачи блока
my_func do
puts 'code from block'
end
или с параметром
def my_func(&block)
puts 'before block called'
block.call 'some name'
puts 'after block called'
end
my_func do |name|
puts 'code from block ' + name
end
# можно и Proc передать, при необходимости
block = Proc.new { puts 'proc' }
my_func(&block)
В сокращенном виде это можно писать и так:
def my_func
# это проверка пришел ли блок
return unless block_given?
puts 'before block called'
yield 'some name'
puts 'after block called'
end
my_func { |name| puts 'called in function ' + name }
В завершении приведу примерную реализацию итератора по массиву
class Array
def my_each
i = 0;
while i < self.size
yield self[i]
i+=1
end
end
end
(1..10).to_a.my_each { |element| puts element }
Отметим, что замыкания широко используются в ruby, более того, ни одна программа не обходится без них.
Вопросы на дом
- найти отличия между lambda и proc
- разница между передачей блока как block и как &block
Модули. Классы
Наиболее очевидное применение модулей - в качестве неймспейсов.
module Mathic
def self.abs
puts 'calculate abs'
end
end
module Party
def self.abs
puts 'some drinking to absolute drinking state'
end
end
def abs
puts 'another method to_s'
end
abs
Mathic.abs
Party::abs
использование различных модулей позволяет разделить эти методы и переписать случайно какой либо из них.
Второе, и более частое использование модулей - в качестве примесей ( mixins ). Mixins - это механизм, с помощью которого в руби реализуется добавление общих методов классы. Механизм более удобный и простой по сравнению с наследованием, особенно множественным или многоуровневым.
module Movable
def move
puts self.name + ' moved'
end
end
class Tank
include Movable
def name
'tank'
end
end
class Ship
include Movable
def name
'ship'
end
end
Ship.new.move
Tank.new.move
Как видно, добавленеи модуля через include расширяет функциональность объекта класса. для расширения функциональности самого класса необходимо воспользоваться ключевым словом extend. Продемонстрирую разницу между extend/example:
module Foo
def foo
puts 'foo'
puts self.inspect
end
end
module Bar
def bar
puts 'bar'
puts self.inspect
end
end
class Unit
extend Foo
include Bar
end
Unit.foo
Unit.new.bar
А теперь более реальный пример. Расширение функциональности происходит в нужный момент в нужном классе.
module Movable
# метод вызывается момент include Movable
def self.included(base)
# расширяем функциональность класса методом acts_as_movable_object
base.send :extend, ClassMethods
end
module ClassMethods
def acts_as_movable_object
send :include, InstanceMethods
end
end
module InstanceMethods
attr_accessor :name
def move
puts self.name + ' moved'
end
def stop
puts self.name + ' stopped'
end
end
end
class Unit
include Movable
end
class Tank < Unit
# метод определен
acts_as_movable_object
end
a = Unit.new
a.name = 'unit'
a.move
a.stop
легко продолжить данный способ до любого количества модулей в меру своей фантазии.
class Unit
include Movable
include Shootable
include Flyable
include Swimable
end
class Ship < Unit
acts_as_shootable
acts_as_swimable
end
class Plane < Unit
acts_as_flyable
end
Такой код не совсем ruby-way:
while i < self.size
yield self[i]
i+=1
end
Я бы написал что-то типа (0..self.size).each { |i| yield self[i] }
Если мы переопределяем Array#each, это не значит, что мы не сможем использовать Range#each — он в них наследуется из Enumerable.
Вопрос к флэтсофту:
А можете на сайте сделать RSS еще и для комментариев?
меня на курсах сегодня просили кинуть ссылку на условие одного из вариантов заданий:
https://gist.github.com/731cb7b64a1b9f569d35
тема :)
выпускной когда уже?) кто с красным дипломом курсы окончил понятно уже у вас там?)
вот ссылка на мое тестовое задание:
https://github.com/Biserov/News
на heroku.com выложу позднее
так же пока отсутствуют тесты