مسیر
/
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 این پیوند در پنجره یا زبانه‌ی جدیدی باز می‌شود

تخصیص چندگانه و تجزیه را یاد بگیرید