Блоки — це маленькі анонімні функції, які можна передати в методи. Блоки укладено в оператор do / end або в фігурні дужки {}, і вони можуть мати кілька аргументів. Імена аргументів визначаються між двома вертикальними знаками ||, які ще називають трубками(tubes).
В попередніх уроках ми вже розглядали різні методи, де бачили використання блоків та аргументів в них
Приклад для блоків в один рядок:
Приклад для блоків в декілька рядків:
Блоки корисні, оскільки вони дозволяють зберегти трохи логіки (коду) і використовувати його пізніше. Це щось типу запису даних у файл, порівняння рівності одного елемента іншому або навіть друк повідомлення про помилку.
Що означає yield у Ruby?
yield — це ключове слово Ruby, яке викликає блок, коли ви його використовуєте.
Це як методи ВИКОРИСТОВУЮТЬ блоки! yield використовується всередині методів для виклику блоку, переданого у цей метод.
yield - (англ. "поступитися") буквально означає для рубі: як тільки побачиш це слово в методі, виконай блок коду, який передали в метод останнім аргументом і далі продовжуй виконувати код методу в звичайному режимі.
Приклад:
Це запускає будь-який блок, який був переданий в print_once, у результаті на екрані буде надруковано «Block is being run» (Блок виконується).
Ти знав, що yield можна використовувати кілька разів в одну блоці?
Кожного разу, коли ви викликаєте yield , блок запускатиметься, тому це схоже на повторний виклик того самого методу.
Приклад:
Ви можете передати будь-яку кількість аргументів для yield, ніби як для методів.
Потім ці аргументи стають аргументами блоку. У цьому прикладі число.
Якщо викликати метод з yield без блоку, то буде помилка.
Щоб дозволити використання методу як із блоком так і без нього, використовується метод block_given?, який є у Ruby.
Блоки можуть бути «явними» або «неявними». Явний означає, що ви даєте йому назву у своєму списку параметрів. Ви можете передати явний блок іншому методу або зберегти його у змінній для використання пізніше.
Приклад:
Зверніть увагу на параметр &block… Ось як ви визначаєте назву блоку!
Lambda (лямбда) - це спосіб визначення блоку та його параметрів за допомогою спеціального синтаксису. Ви можете зберегти цю лямбду у змінній для подальшого використання. Синтаксис для визначення лямбд виглядає так:
А також ви можете використовувати альтернативний синтаксис: lambda замість
->
.
Суть залишилася та сама, лише трішки синтаксис змінився.
Визначення лямбди не запускатиме код у ньому, так само як визначення методу не запускатиме метод, для цього вам потрібно використовувати метод call.
Приклад:
Існують інші способи виклику лямбди, добре знати, що вони існують, однак я рекомендую дотримуватися call для ясності.
Приклад:
Лямбда також може приймати аргументи, ось приклад:
Якщо ви передасте лямбді неправильну кількість аргументів, вона викличе виняток (exception), як і звичайний метод.
Proc (процедури) - мають дуже подібну концепцію... Одна з відмінностей полягає в тому, як ви їх створюєте.
Приклад:
Спеціального класу Lambda немає. Лямбда – це лише спеціальний об’єкт класу Proc. Якщо ви подивитеся на методи екземпляра з Proc, ви помітите, що існує метод lambda?
Процедура поводиться інакше, ніж лямбда, особливо коли йдеться про аргументи:
Ще одна відмінність між proc і lambda полягає в тому, як вони реагують на оператор return.
Лямбда повернеться нормально, як звичайний метод, а процедура спробує повернутися з поточного контексту. Ось що я маю на увазі:
Якби процедура була всередині методу, тоді виклик return був би еквівалентним поверненню з цього методу.
Давайте продемонструю в наступному прикладі, що я маю на увазі:
В цьому прикладі буде виведено Before proc, а після виклику процедури my_proc нам буде повернуто число 2 та вийде з методу call_proc.
Ось короткий список опису того, чим відрізняються процедура і лямбда:
-> {}
, а процедури — за допомогою Proc.new {}
. Поглянувши на цей список, ми можемо побачити, що лямбда набагато ближче до звичайного методу, ніж процедури.
Ruby процедури та лямбди також мають ще один спеціальний атрибут. Коли ви створюєте процедуру, вона має свою область виконання.
Ця концепція, яку іноді називають замиканням, означає, що процедура матиме з собою такі значення, як локальні змінні та методи з контексту, де вона була визначена.
Вони містять не фактичні значення, а посилання на них, тому, якщо змінні зміниться після створення процедури, процедура завжди матиме останню версію.
Прикдад:
У цьому прикладі ми маємо локальну змінну count, яка має значення 1.
У нас також є процедура під назвою my_proc і метод call_proc, який запускає (через метод call) будь-яку процедуру або лямбду, передану як аргумент.
Як ви думаєте, що надрукує ця програма?
Здавалося б, 500 є найбільш логічним висновком, але через ефект «замикання» буде виведе 1.
Це відбувається тому, що процедура використовує значення count з місця, де було визначено процедуру, і це поза визначенням методу.
Де процедури та лямбди зберігають цю інформацію про область виконання?
Давайте розповім вам трохи про клас Binding…
Коли ви створюєте об’єкт Binding за допомогою методу прив’язки, ви створюєте «якір» до цієї точки коду.
Кожна змінна, метод і клас, визначені на цьому етапі, будуть доступні пізніше через цей об’єкт, навіть якщо ви перебуваєте в зовсім іншій області.
Приклад:
foo доступний завдяки прив’язці, навіть якщо ми перебуваємо за межами методу, де його було визначено.
Якщо ви спробуєте надрукувати foo безпосередньо, ви отримаєте помилку:
Причина в тому, що foo не було визначено поза методом раніше/попредньо.
Іншими словами, виконання чогось у контексті зв’язувального об’єкта є таким самим, як якщо б цей код був у тому самому місці, де це зв’язування було визначено (пам’ятайте метафору «якоря»). Вам не потрібно безпосередньо використовувати об’єкти прив’язки, але все одно добре знати, що такі штуки існують 🙂
У цьому дописі ви дізналися, як працюють блоки, відмінності між процедурами та лямбдами, а також дізналися про ефект «замикання», який виникає щоразу, коли ви створюєте блок.
Тема є досить складна для розуміння, отже потребує практики!
Тому ДЗ на сьогодні:
Приклад що в нас є і що маємо отримати на виході:
Лінки на додаткові джерала по сьогоднішньому матеріалу:
Процедруи(Proc) та лямбди(lambda)
«Чітшіт» на все вище перераховане
Група в телеграмі: https://t.me/ruby4you
Автор курсу: Шкоропад Даниїл