Читать «Учись программировать (на Ruby)» онлайн - страница 42

Крис Пайн

'Мммм! Я так люблю пирожки с '+fruit+', а вы?' end

# Помните, что мы берём элементы массива с чётными номерами,

# все из которых оказываются нечётными числами; это

# просто потому, что мне захотелось создать подобные трудности.

[1, 2, 3, 4, 5].eachEven do |oddBall|

puts oddBall.to_s+' – НЕ чётное число!' end

Мммм!

Я так люблю пирожи

с яблоками,

а вы?

Мммм!

Я так люблю пирожи

с вишней, а

вы?

Н И

чётное число!

ЕН – 3

чётное число!

5 – НЕ

чётное число!

Итак, всё, что мы должны сделать, чтобы передать блок в метод eachEven, это «прилепить» блок после метода. Подобным же образом вы можете передать блок в любой метод, хотя многие методы просто проигнорируют блок. Чтобы заставить ваш метод не игнорировать блок, а взять его и превратить его в процедурный объект, нужно поместить имя процедурного объекта в конце списка параметров вашего метода и поставить перед ним амперсанд (&). Конечно, это немного мудрёно, но не слишком, и вам придётся сделать это только один раз (когда вы описываете метод). А затем вы можете использовать этот метод снова и снова точно так же, как и встроенные методы, принимающие блоки такие, как each и times. (Помните, 5.times do…?)

Если для вас это слишком запутанно, просто помните, что должен сделать eachEven: вызвать переданный ему блок для каждого чётного элемента в массиве. После того, как однажды вы написали метод и убедились, что он работает, вам уже не нужно думать о том, что в действительности делается «под капотом» («какой блок и когда вызывается??»). На самом деле, именно поэтому мы пишем подобные методы: чтобы нам никогда не приходилось снова думать о том, как они работают. Мы просто используем их.

Помню, один раз я захотел сделать, чтобы можно было измерять, сколько времени выполняются различные секции программы. (Это также известно как профилирование программного кода.) И я написал метод, который засекает время перед исполнением кода, затем выполняет его, в конце снова засекает время и вычисляет разницу. Сейчас я не могу найти этот метод, но мне он и не нужен; он, возможно, выглядел примерно так:

def profile descriptionOfBlock, &block # Описание блока и сам блок startTime = Time.now

block.call

duration = Time.now – startTime

puts descriptionOfBlock+': '+duration.to_s+' сек.' end

profile '25000 удваиваний' do number = 1

25000.times do

number = number + number end

puts number.to_s.length.to_s+' цифр' # Да, это число цифр в таком ГИГАНТСКОМ числе.

end

profile 'сосчитать до миллиона' do number = 0

1000000.times do

number = number + 1

end

end

7526 цифр

25000 удваиваний: 0.304966 сек. сосчитать до миллиона: 0.615216 сек.

Как просто! Как элегантно! Теперь с помощью этого крошечного метода я легко могу измерить время работы любой секции в любой программе, в какой только захочу: я просто закину код в блок и отправлю его методу profile. Что может быть проще? В большинстве языков мне понадобилось бы явно добавлять код для измерения времени (тот, что написан в profile) до и после каждой секции, которую я хотел бы захронометрировать. В то время как в Ruby я всё держу в одном-единственном месте и (что более важно) отдельно от всего остального!