Треки
/
Ruby
Ruby
/
Салабус
/
Множинне присвоєння та декомпозиція
Мн

Множинне присвоєння та декомпозиція у Ruby

1 вправа

Про концепцію Множинне присвоєння та декомпозиція

Декомпозиція означає вилучення елементів колекції, як-от Array або Hash. Декомпозовані значення потім можна присвоїти змінним у межах однієї інструкції.

Множинне присвоєння - це можливість присвоїти значення кільком змінним, декомпозуючи їх, у межах однієї інструкції. Це робить код лаконічнішим і зрозумілішим, і досягається це розділенням змінних, яким присвоюються значення, комою, як-от first, second, third = [1, 2, 3].

Оператор splat (*) і подвійний splat-оператор (**) часто використовуються в контексті декомпозиції. Оператор splat (*) можна використати, щоб поєднати кілька масивів в один масив, декомпозуючи кожен із них у новий спільний масив. Подвійний splat-оператор (**) можна використати, щоб поєднати кілька хешів в один хеш, декомпозуючи кожен із них у новий спільний хеш. Цей синтаксис потрібен, щоб відрізнити кілька значень, прийнятих як позиційні аргументи, від випадку, коли ми приймаємо будь-яку кількість ключових аргументів.

Коли оператор splat (*) використовується без колекції, він пакує (або компонує) кілька значень в один масив. Це часто використовується в множинному присвоєнні, щоб згрупувати всі «залишкові» елементи, які не мають окремих присвоєнь, в одну змінну.

У Ruby часто використовують таку поведінку декомпозиції та композиції, коли використовують або визначають методи, що приймають довільну кількість позиційних або ключових аргументів. Ми часто бачимо, як ці аргументи визначають як def some_method(*arguments, **keyword_arguments), а потім використовують як some_method(*some_array, **some_hash).

Caution

*<variable_name> і **<variable_name> не варто плутати з * і **. Хоча * і ** використовуються для множення та піднесення до степеня відповідно, *<variable_name> і **<variable_name> використовуються як оператори композиції та декомпозиції.

Множинне присвоєння

Множинне присвоєння дає змогу присвоїти значення кільком змінним в одному рядку. Щоб розділити значення, використаймо кому ,:

>> a, b = 1, 2
=> [1, 2]
>> a
=> 1

Множинне присвоєння не обмежується одним типом даних:

>> x, y, z = 1, "Hello", true
=> [1, "Hello", true]
>> x
=> 1
>> y
=> 'Hello'
>> z
=> true

Множинне присвоєння можна використати, щоб поміняти місцями елементи в масивах. Цей прийом досить поширений в алгоритмах сортування. Наприклад:

>> numbers = [1, 2]
=> [1, 2]
>> numbers[0], numbers[1] = numbers[1], numbers[0]
=> [2, 1]
>> numbers
=> [2, 1]
Note

Це також відомо як «паралельне присвоєння» і дає змогу уникнути тимчасової змінної.

Якщо змінних більше, ніж значень, зайвим змінним буде присвоєно nil:

>> a, b, c = 1, 2
=> [1, 2]
>> b
=> 2
>> c
=> nil

Якщо значень більше, ніж змінних, зайві значення буде проігноровано:

>> a, b, c = 1, 2, 3, 4
>> b
=> 2
>> c
=> 3

Декомпозиція

У Ruby можна декомпозувати елементи масивів/хешів в окремі змінні. Оскільки значення розташовані в масивах у порядку індексів, вони розпаковуються у змінні в тому самому порядку:

>> fruits = ["apple", "banana", "cherry"]
>> x, y, z = fruits
>> x
=> "apple"

Якщо якісь значення не потрібні, можна використати _, щоб позначити «зібрано, але не використано»:

>> fruits = ["apple", "banana", "cherry"]
>> _, _, z = fruits
>> z
=> "cherry"

Глибока декомпозиція

Декомпозиція та присвоєння значень із масивів, вкладених у масив (також відомий як вкладений масив), працює так само, як і поверхова декомпозиція, але потребує виразу декомпозиції з дужками (()), щоб уточнити контекст або позицію значень:

>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>> (a, b), (c, d) = fruits_vegetables
>> a
=> "apple"
>> d
=> "potato"

Також можна глибоко розпакувати лише частину вкладеного масиву:

>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>> a, (c, d) = fruits_vegetables
>> a
=> ["apple", "banana"]
>> c
=> "carrot"

Якщо в декомпозиції змінні розташовані неправильно та/або кількість значень неправильна, ми отримаємо синтаксичну помилку:

fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
(a, b), (d) = fruits_vegetables

syntax error, unexpected ')', expecting '.' or &. or :: or '['
((a, b), (d)) = fruits_vegetables

Поекспериментуймо тут, і ми помітимо, що все визначає перша схема, а не наявні значення праворуч. Синтаксична помилка не повʼязана зі структурою даних.

Декомпозиція масиву за допомогою одинарного оператора splat (*)

Коли ми [декомпозуємо масив][decomposition], можна використати оператор splat (*), щоб захопити «залишкові» значення. Це зрозуміліше, ніж зрізати масив (що в деяких ситуаціях менш читабельно). Наприклад, ми можемо вилучити перший елемент, а потім присвоїти решту значень новому масиву без першого елемента:

>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *last = fruits
>> x
=> "apple"
>> last
=> ["banana", "cherry", "orange", "kiwi", "melon", "mango"]

Також можна вилучити значення на початку й у кінці масиву, згрупувавши всі значення посередині:

>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *middle, y, z = fruits
>> y
=> "melon"
>> middle
=> ["banana", "cherry", "orange", "kiwi"]

Також можна використати * у глибокій декомпозиції:

>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>> (a, *rest), b = fruits_vegetables
>> a
=> "apple"
>> rest
=> ["banana", "melon"]

Декомпозиція Hash

Декомпозиція хеша трохи відрізняється від декомпозиції масиву. Щоб розпакувати хеш, спершу потрібно перетворити його на масив. Інакше декомпозиції не відбудеться:

>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory
>> x
=> {:apple=>6, :banana=>2, :cherry=>3}
>> y
=> nil

Щоб привести Hash до масиву, можна використати метод to_a:

>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> fruits_inventory.to_a
=> [[:apple, 6], [:banana, 2], [:cherry, 3]]
>> x, y, z = fruits_inventory.to_a
>> x
=> [:apple, 6]

Якщо потрібно розпакувати ключі, можна використати метод keys:

>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory.keys
>> x
=> :apple

Якщо потрібно розпакувати значення, можна використати метод values:

>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory.values
>> x
=> 6

Композиція

Композиція дає змогу зібрати кілька значень в один масив, який присвоюється змінній. Це корисно, коли ми хочемо декомпозувати значення, внести зміни, а потім скомпонувати результат назад у змінну. Це також дає змогу виконувати злиття двох або більше масивів/хешів.

Композиція масиву за допомогою оператора splat (*)

Скомпонувати масив можна за допомогою оператора splat (*). Це спакує всі значення в один масив.

>> fruits = ["apple", "banana", "cherry"]
>> more_fruits = ["orange", "kiwi", "melon", "mango"]

# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
>> combined_fruits = *fruits, *more_fruits

>> combined_fruits
=> ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]

Композиція хеша за допомогою подвійного splat-оператора (**)

Композиція хеша виконується за допомогою подвійного splat-оператора (**). Це спакує всі пари ключ/значення з одного хеша в інший хеш або поєднає два хеші разом.

>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> more_fruits_inventory = {orange: 4, kiwi: 1, melon: 2, mango: 3}

# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}

# then the pairs are packed into combined_fruits_inventory
>> combined_fruits_inventory
=> {:apple=>6, :banana=>2, :cherry=>3, :orange=>4, :kiwi=>1, :melon=>2, :mango=>3}

Використання оператора splat (*) і подвійного splat-оператора (**) з методами

Композиція з параметрами методу

Коли ми створюємо метод, що приймає довільну кількість аргументів, у визначенні методу можна використати *arguments або **keyword_arguments. *arguments використовується, щоб спакувати довільну кількість позиційних (не ключових) аргументів, а **keyword_arguments - щоб спакувати довільну кількість ключових аргументів.

Використання *arguments:

# This method is defined to take any number of positional arguments
# (Using the single line form of the definition of a method.)

>> def my_method(*arguments)= arguments

# Arguments given to the method are packed into an array

>> my_method(1, 2, 3)
=> [1, 2, 3]

>> my_method("Hello")
=> ["Hello"]

>> my_method(1, 2, 3, "Hello", "Mars")
=> [1, 2, 3, "Hello", "Mars"]

Використання **keyword_arguments:

# This method is defined to take any number of keyword arguments

>> def my_method(**keyword_arguments)= keyword_arguments

# Arguments given to the method are packed into a dictionary

>> my_method(a: 1, b: 2, c: 3)
=> {:a => 1, :b => 2, :c => 3}

Якщо у визначеному методі немає жодних визначених параметрів для ключових аргументів (**keyword_arguments або <key_word>: <value>), то ключові аргументи буде спаковано в хеш і присвоєно останньому параметру.

>> def my_method(a)= a

>> my_method(a: 1, b: 2, c: 3)
=> {:a => 1, :b => 2, :c => 3}

*arguments і **keyword_arguments також можна використовувати разом:

def my_method(*arguments, **keyword_arguments)
  p arguments.sum
  for (key, value) in keyword_arguments.to_a
    p key.to_s + " = " + value.to_s
  end
end


my_method(1, 2, 3, a: 1, b: 2, c: 3)
6
"a = 1"
"b = 2"
"c = 3"

Також можна писати аргументи до та після *arguments, щоб дозволити конкретні позиційні аргументи. Це працює так само, як декомпозиція масиву.

Caution

Аргументи потрібно розташовувати в певному порядку:

def my_method(<positional_arguments>, *arguments, <positional_arguments>, <key-word_arguments>, **keyword_arguments)

Якщо не дотримуватися цього порядку, то ми отримаємо помилку.

def my_method(a, b, *arguments)
  p a
  p b
  p arguments
end

my_method(1, 2, 3, 4, 5)
1
2
[3, 4, 5]

Можна писати позиційні аргументи до та після *arguments:

>> def my_method(a, *middle, b)= middle

>> my_method(1, 2, 3, 4, 5)
=> [2, 3, 4]

Також можна поєднати позиційні аргументи, *arguments, ключові аргументи та **keyword_arguments:

>> def my_method(first, *many, last, a:, **keyword_arguments)
     p first
     p many
     p last
     p a
     p keyword_arguments
     end

>> my_method(1, 2, 3, 4, 5, a: 6, b: 7, c: 8)
1
[2, 3, 4]
5
6
{:b => 7, :c => 8}

Написання аргументів у неправильному порядку призведе до помилки:

def my_method(a:, **keyword_arguments, first, *arguments, last)
  arguments
end

my_method(1, 2, 3, 4, a: 5)

syntax error, unexpected local variable or method, expecting & or '&'
... my_method(a:, **keyword_arguments, first, *arguments, last)

Декомпозиція у виклики методів

Можна використати оператор splat (*), щоб розпакувати масив аргументів у виклик методу:

def my_method(a, b, c)
  p c
  p b
  p a
end

numbers = [1, 2, 3]
my_method(*numbers)
3
2
1

Також можна використати подвійний splat-оператор (**), щоб розпакувати хеш аргументів у виклик методу:

def my_method(a:, b:, c:)
  p c
  p b
  p a
end

numbers = {a: 1, b: 2, c: 3}
my_method(**numbers)
3
2
1
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці

Вивчити концепцію Множинне присвоєння та декомпозиція