يشير التفكيك إلى عملية استخراج عناصر مجموعة، مثل Array أو Hash. ويمكن بعد ذلك إسناد القيم المُفكَّكة إلى متغيرات في العبارة نفسها.
الإسناد المتعدد هو القدرة على إسناد قيم مُفكَّكة إلى عدة متغيرات في عبارة واحدة. وهذا يجعل الكود أكثر إيجازًا وقابلية للقراءة، ويتم بفصل المتغيرات المراد إسنادها بفاصلة، مثل first, second, third = [1, 2, 3].
كثيرًا ما يُستخدم عامل البسط (*) وعامل البسط المزدوج (**) في سياقات التفكيك. يمكن استخدام عامل البسط (*) لدمج عدة مصفوفات في مصفوفة واحدة عبر تفكيك كل منها إلى مصفوفة مشتركة جديدة. ويمكن استخدام عامل البسط المزدوج (**) لدمج عدة تجزيئات في تجزئة واحدة عبر تفكيك كل منها إلى تجزئة مشتركة جديدة. وهذا تركيب يميّز بين قبول عدة قيم كوسيط موضعي، وبين قبول أي عدد من الوسائط المفتاحية.
عند استخدام عامل البسط (*) بدون مجموعة، فإنه يحزم (أو يركّب) عددًا من القيم في مصفوفة واحدة. ويُستخدم هذا كثيرًا في الإسناد المتعدد لتجميع كل العناصر «المتبقية» التي ليس لها إسنادات فردية في متغير واحد.
من الشائع في Ruby استخدام سلوك التفكيك/التركيب هذا عند استخدام أو تعريف طرق تقبل عددًا عشوائيًا من الوسائط الموضعية أو المفتاحية. وكثيرًا ما سترى هذه الوسائط معرَّفة بالشكل def some_method(*arguments, **keyword_arguments) ومستخدمة بالشكل some_method(*some_array, **some_hash).
لا ينبغي الخلط بين *<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]
يُعرف هذا أيضًا باسم «الإسناد المتوازي»، ويمكن استخدامه لتجنّب متغير مؤقت.
إذا كان عدد المتغيرات أكبر من عدد القيم، فسيُسند 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
جرّب هنا، وستلاحظ أن النمط الأول هو الذي يفرض الشكل، لا القيم المتاحة في الطرف الأيمن. وخطأ الصياغة هذا لا علاقة له ببنية البيانات.
*)عند [تفكيك مصفوفة][decomposition] يمكنك استخدام عامل البسط (*) لالتقاط القيم «المتبقية».
وهذا أوضح من تقطيع المصفوفة (وهو أقل قابلية للقراءة في بعض الحالات).
على سبيل المثال، يمكننا استخراج العنصر الأول ثم إسناد القيم المتبقية إلى مصفوفة جديدة بدون العنصر الأول:
>> 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
التركيب هو القدرة على تجميع عدة قيم في مصفوفة واحدة تُسند إلى متغير. وهذا مفيد عندما تريد تفكيك القيم، وإجراء تغييرات، ثم تركيب النتائج مرة أخرى في متغير. كما يتيح إجراء عمليات دمج على مصفوفتين أو أكثر أو تجزئتين أو أكثر.
*)يمكن تركيب مصفوفة باستخدام عامل البسط (*).
وهذا سيحزم كل القيم في مصفوفة واحدة.
>> 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"]
**)يتم تركيب التجزئة باستخدام عامل البسط المزدوج (**).
وهذا سيحزم كل أزواج المفتاح/القيمة من تجزئة في تجزئة أخرى، أو يدمج تجزئتين معًا.
>> 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}
*) وعامل البسط المزدوج (**) مع الطرقعندما تنشئ طريقة تقبل عددًا عشوائيًا من الوسائط، يمكنك استخدام *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 وبعده للسماح بوسائط موضعية محددة.
وهذا يعمل بالطريقة نفسها التي يعمل بها تفكيك مصفوفة.
يجب ترتيب الوسائط بترتيب محدد:
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)
يمكنك استخدام عامل البسط (*) لفك مصفوفة من الوسائط في استدعاء طريقة:
def my_method(a, b, c)
p c
p b
p a
end
numbers = [1, 2, 3]
my_method(*numbers)
3
2
1
يمكنك أيضًا استخدام عامل البسط المزدوج (**) لفك تجزئة من الوسائط في استدعاء طريقة:
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