الفرق بين المراجعتين لصفحة: «Kotlin/multi declarations»

من موسوعة حسوب
أنشأ الصفحة ب'<noinclude>{{DISPLAYTITLE:تفكيك التصريحات (Destructuring Declarations) في لغة Kotlin}}</noinclude> == التصريح المُفكّك == قد تحتا...'
 
تعديل المصطلح الأساسي في كامل الصفحة
سطر 1: سطر 1:
<noinclude>{{DISPLAYTITLE:تفكيك التصريحات (Destructuring Declarations) في لغة Kotlin}}</noinclude>
<noinclude>{{DISPLAYTITLE:التصريحات بالتفكيك (Destructuring Declarations) في لغة Kotlin}}</noinclude>
== التصريح المُفكّك ==
== التصريح بالتفكيك ==
قد تحتاج في بعض الأحيان لتفكيك الكائن (object) إلى عددٍ من المتحولات، مثل:<syntaxhighlight lang="kotlin">
قد تحتاج في بعض الأحيان لتفكيك الكائن (object) إلى عددٍ من المتحولات، مثل:<syntaxhighlight lang="kotlin">
val (name, age) = person  
val (name, age) = person  
</syntaxhighlight>تٌسمَّى الصيغة السابقة بالتصريح المُفكَّك والذي يُنشِئ أكثر من متحولٍ بنفس الوقت (وهما المتحولان <code>name</code> و <code>age</code>) حيث يُسمح باستخدامهما بشكلٍ مستقلٍ تمامًا كما في الشيفرة الآتية:<syntaxhighlight lang="kotlin">
</syntaxhighlight>تٌسمَّى الصيغة السابقة بالتصريح بالتفكيك والذي يُنشِئ أكثر من متحولٍ بنفس الوقت (وهما المتحولان <code>name</code> و <code>age</code>) حيث يُسمح باستخدامهما بشكلٍ مستقلٍ تمامًا كما في الشيفرة الآتية:<syntaxhighlight lang="kotlin">
println(name)
println(name)
println(age)
println(age)
</syntaxhighlight>إذ يُترجَم التصريح المُفكَّك كما يلي:<syntaxhighlight lang="kotlin">
</syntaxhighlight>إذ يُترجَم التصريح بالتفكيك كما يلي:<syntaxhighlight lang="kotlin">
val name = person.component1()
val name = person.component1()
val age = person.component2()
val age = person.component2()
</syntaxhighlight>حيث تُعدُّ الدالتان <code>component1()</code>‎ و <code>component2()</code>‎ مثالًا عن الاصطلاحات الأساسيّة المُستخدَمة في لغة Kotlin (راجع المُعامِلات مثل <code>+</code> و <code>*</code> وحلقات <code>for</code> و... إلخ.)، ويُسمَح بوجود أيّ كائنٍ في الجانب الأيمن من التصريح المُفكَّك طالما أمكن استدعاء العدد المطلوب من دوال الأجزاء (component functions) عبره (قد تكون بالأسماء  <code>component3()</code>‎ و <code>component4()</code>‎ وهكذا) والتي يجب أن تُحدَّد بالكلمة المفتاحيّة <code>operator</code> للسماح باستخدامها في التصريح المُفكَّك.
</syntaxhighlight>حيث تُعدُّ الدالتان <code>component1()</code>‎ و <code>component2()</code>‎ مثالًا عن الاصطلاحات الأساسيّة المُستخدَمة في لغة Kotlin (راجع المُعامِلات مثل <code>+</code> و <code>*</code> وحلقات <code>for</code> و... إلخ.)، ويُسمَح بوجود أيّ كائنٍ في الجانب الأيمن من التصريح بالتفكيك طالما أمكن استدعاء العدد المطلوب من دوال الأجزاء (component functions) عبره (قد تكون بالأسماء  <code>component3()</code>‎ و <code>component4()</code>‎ وهكذا) والتي يجب أن تُحدَّد بالكلمة المفتاحيّة <code>operator</code> للسماح باستخدامها في هذا التصريح.


كما ويُستفاد من التصريح المُفكَّك في حلقة <code>for</code> بالشكل:<syntaxhighlight lang="kotlin">
كما ويُستفاد من التصريح بالتفكيك في حلقة <code>for</code> بالشكل:<syntaxhighlight lang="kotlin">
for ((a, b) in collection) { ... }
for ((a, b) in collection) { ... }
</syntaxhighlight>حيث تحصل المتحولات <code>a</code> و <code>b</code> على القيم من استدعاء الدالتين  <code>component1()</code>‎ و <code>component2()</code>‎ في عناصر المجموعة (collection).
</syntaxhighlight>حيث تحصل المتحولات <code>a</code> و <code>b</code> على القيم من استدعاء الدالتين  <code>component1()</code>‎ و <code>component2()</code>‎ في عناصر المجموعة (collection).
سطر 26: سطر 26:
// الآن.. استخدام الدالة
// الآن.. استخدام الدالة
val (result, status) = function(...)
val (result, status) = function(...)
</syntaxhighlight>ويُسمَح باستخدام التصريح المُفكَّك هنا لأنّ أصناف البيانات تعرِّف دوال الأجزاء <code>componentN()‎</code> تلقائيًا.
</syntaxhighlight>ويُسمَح باستخدام التصريح بالتفكيك هنا لأنّ أصناف البيانات تعرِّف دوال الأجزاء <code>componentN()‎</code> تلقائيًا.


'''ملاحظة:''' يمكن استخدام الصنف القياسي <code>Pair</code> لتعيد الدالة <code>function()</code>‎ النوع <code>Pair<Int, Status></code>‎، ولكن من الأفضل تسميةُ البيانات بما يتناسب مع استخدامها.
'''ملاحظة:''' يمكن استخدام الصنف القياسي <code>Pair</code> لتعيد الدالة <code>function()</code>‎ النوع <code>Pair<Int, Status></code>‎، ولكن من الأفضل تسميةُ البيانات بما يتناسب مع استخدامها.
سطر 42: سطر 42:
operator fun <K, V> Map.Entry<K, V>.component1() = getKey()
operator fun <K, V> Map.Entry<K, V>.component1() = getKey()
operator fun <K, V> Map.Entry<K, V>.component2() = getValue()
operator fun <K, V> Map.Entry<K, V>.component2() = getValue()
</syntaxhighlight>مما يجعل من السهل استخدام التصريحات المُفكَّكة في حلقات <code>for</code> بالاعتماد على maps (وكذلك كائنات مجموعات أصناف البيانات [collections of data class instances] وما يماثلها).
</syntaxhighlight>مما يجعل من السهل استخدام التصريحات بالتفكيك في حلقات <code>for</code> بالاعتماد على maps (وكذلك كائنات مجموعات أصناف البيانات [collections of data class instances] وما يماثلها).


== استخدام الشرطة السفليّة (_) للمتحولات غير المُستخدَمة (بدءًا من الإصدار 1.1) ==
== استخدام الشرطة السفليّة (_) للمتحولات غير المُستخدَمة (بدءًا من الإصدار 1.1) ==
إن لم يكن هناك حاجةٌ للمتحوّل في التصريح المُفكَّك فيمكن الاستغناءُ عن اسمه بالشرطة السفليّة مثل:<syntaxhighlight lang="kotlin">
إن لم يكن هناك حاجةٌ للمتحوّل في التصريح بالتفكيك فيمكن الاستغناءُ عن اسمه بالشرطة السفليّة مثل:<syntaxhighlight lang="kotlin">
val (_, status) = getResult()
val (_, status) = getResult()
</syntaxhighlight>وبهذه الطريقة لن تُستدعَى دالة الجزء (component function) الموافقةِ له.
</syntaxhighlight>وبهذه الطريقة لن تُستدعَى دالة الجزء (component function) الموافقةِ له.


== التفكيك في lambdas (بدءًا من الإصدار 1.1) ==
== التفكيك في lambdas (بدءًا من الإصدار 1.1) ==
تُستخدَم صيغةُ التصريحات المُفكَّكة في متحولات lambda أيضًا، وإذا كان متحول lambda من النوع <code>Pair</code> (أو من النوع <code>Map.Entry</code> أو أي نوعٍ آخر يحتوي على دالة جزءٍ [component function] مناسبةٍ) فيُمكن حينئذٍ استخدام أكثر من متحولٍ (تُحاط المتحولات بالأقواس <code>()</code>)، كما في الشيفرة الآتية:<syntaxhighlight lang="kotlin">
تُستخدَم صيغةُ التصريحات بالتفكيك في متحولات lambda أيضًا، وإذا كان متحول lambda من النوع <code>Pair</code> (أو من النوع <code>Map.Entry</code> أو أي نوعٍ آخر يحتوي على دالة جزءٍ [component function] مناسبةٍ) فيُمكن حينئذٍ استخدام أكثر من متحولٍ (تُحاط المتحولات بالأقواس <code>()</code>)، كما في الشيفرة الآتية:<syntaxhighlight lang="kotlin">
map.mapValues { entry -> "${entry.value}!" }
map.mapValues { entry -> "${entry.value}!" }
map.mapValues { (key, value) -> "$value!" }
map.mapValues { (key, value) -> "$value!" }
</syntaxhighlight>ويتَّضِح الفرق بين التصريح عن متحوِّلين والتصريح المُفكَّك عنهما، كما يلي:<syntaxhighlight lang="kotlin">
</syntaxhighlight>ويتَّضِح الفرق بين التصريح عن متحوِّلين والتصريح عنهما بالتفكيك، كما يلي:<syntaxhighlight lang="kotlin">
{ a -> ... } // متحول واحد
{ a -> ... } // متحول واحد
{ a, b -> ... } // متحولان
{ a, b -> ... } // متحولان
{ (a, b) -> ... } // تصريح مفكك بمتحولين
{ (a, b) -> ... } // تصريح مفكك بمتحولين
{ (a, b), c -> ... } // تصريح مفكك بمتحولين ومتحول ثالث مستقل
{ (a, b), c -> ... } // تصريح مفكك بمتحولين ومتحول ثالث مستقل
</syntaxhighlight>فإن كان أحد أجزاء التصريح المُفكَّك غير مستخدمٍ يُستعاض عنه بالشرطة السفليّة دون الحاجة لتسميته، مثل:<syntaxhighlight lang="kotlin">
</syntaxhighlight>فإن كان أحد أجزاء التصريح بالتفكيك غير مستخدمٍ يُستعاض عنه بالشرطة السفليّة دون الحاجة لتسميته، مثل:<syntaxhighlight lang="kotlin">
map.mapValues { (_, value) -> "$value!" }
map.mapValues { (_, value) -> "$value!" }
</syntaxhighlight>كما ويمكن تحديد النوع الإجماليّ لمتحولات التصريح المُفكَّك أو لمتحولٍ واحدٍ منها بشكلٍ منفصلٍ كما في الشيفرة الآتية:<syntaxhighlight lang="kotlin">
</syntaxhighlight>كما ويمكن تحديد النوع الإجماليّ لمتحولات التصريح بالتفكيك أو لمتحولٍ واحدٍ منها بشكلٍ منفصلٍ كما في الشيفرة الآتية:<syntaxhighlight lang="kotlin">
map.mapValues { (_, value): Map.Entry<Int, String> -> "$value!" }
map.mapValues { (_, value): Map.Entry<Int, String> -> "$value!" }


سطر 67: سطر 67:


== مصادر ==
== مصادر ==
* [https://kotlinlang.org/docs/reference/multi-declarations.html صفحة تفكيك التصريحات في التوثيق الرسميّ للغة Kotlin]
* [https://kotlinlang.org/docs/reference/multi-declarations.html صفحة التصريح بالتفكيك في التوثيق الرسميّ للغة Kotlin]
[[تصنيف:Kotlin]]
[[تصنيف:Kotlin]]
[[تصنيف:Kotlin Objects]]
[[تصنيف:Kotlin Objects]]

مراجعة 03:44، 18 مارس 2018

التصريح بالتفكيك

قد تحتاج في بعض الأحيان لتفكيك الكائن (object) إلى عددٍ من المتحولات، مثل:

val (name, age) = person

تٌسمَّى الصيغة السابقة بالتصريح بالتفكيك والذي يُنشِئ أكثر من متحولٍ بنفس الوقت (وهما المتحولان name و age) حيث يُسمح باستخدامهما بشكلٍ مستقلٍ تمامًا كما في الشيفرة الآتية:

println(name)
println(age)

إذ يُترجَم التصريح بالتفكيك كما يلي:

val name = person.component1()
val age = person.component2()

حيث تُعدُّ الدالتان component1()‎ و component2()‎ مثالًا عن الاصطلاحات الأساسيّة المُستخدَمة في لغة Kotlin (راجع المُعامِلات مثل + و * وحلقات for و... إلخ.)، ويُسمَح بوجود أيّ كائنٍ في الجانب الأيمن من التصريح بالتفكيك طالما أمكن استدعاء العدد المطلوب من دوال الأجزاء (component functions) عبره (قد تكون بالأسماء component3()‎ و component4()‎ وهكذا) والتي يجب أن تُحدَّد بالكلمة المفتاحيّة operator للسماح باستخدامها في هذا التصريح. كما ويُستفاد من التصريح بالتفكيك في حلقة for بالشكل:

for ((a, b) in collection) { ... }

حيث تحصل المتحولات a و b على القيم من استدعاء الدالتين component1()‎ و component2()‎ في عناصر المجموعة (collection).

تطبيقٌ عمليٌّ: إعادة قيمتين من الدالة

عند الحاجة إلى إعادة عنصرين من الدالة (لنقل مثلًا إعادة كائن النتيجة وحالةٍ ما) فإن الطريقة المُتَّفق عليها للقيام بذلك هي إنشاء صنفٍ للبيانات (data class) وإعادة كائنٍ منه، كما في الشيفرة الآتية:

data class Result(val result: Int, val status: Status)
fun function(...): Result {
    // بعض الحسابات
    
    return Result(result, status)
}

// الآن.. استخدام الدالة
val (result, status) = function(...)

ويُسمَح باستخدام التصريح بالتفكيك هنا لأنّ أصناف البيانات تعرِّف دوال الأجزاء componentN()‎ تلقائيًا.

ملاحظة: يمكن استخدام الصنف القياسي Pair لتعيد الدالة function()‎ النوع Pair<Int, Status>‎، ولكن من الأفضل تسميةُ البيانات بما يتناسب مع استخدامها.

تطبيقٌ عمليٌّ: التصريحات المُفكَّكة و Maps

إنّ الطريقة الأنسب للمرور بعناصر map هي كما في الشيفرة الآتية:

for ((key, value) in map) {
   // القيام بالتعليمات استنادًا إلى القيمة والمفتاح
}

وهنا يجب التقيد بما يلي:

  • تمثيل كائن map كتسلسلٍ (sequence) من القيم من خلال الدالة iterator()‎
  • تمثيل كلٍّ من العناصر كزوجٍ (pair) من خلال الدالتين ‎component1()‎ و component2()‎‎

إذ تزوِّد المكتبة القياسية بمثل هذه الإضافات:

operator fun <K, V> Map<K, V>.iterator(): Iterator<Map.Entry<K, V>> = entrySet().iterator()
operator fun <K, V> Map.Entry<K, V>.component1() = getKey()
operator fun <K, V> Map.Entry<K, V>.component2() = getValue()

مما يجعل من السهل استخدام التصريحات بالتفكيك في حلقات for بالاعتماد على maps (وكذلك كائنات مجموعات أصناف البيانات [collections of data class instances] وما يماثلها).

استخدام الشرطة السفليّة (_) للمتحولات غير المُستخدَمة (بدءًا من الإصدار 1.1)

إن لم يكن هناك حاجةٌ للمتحوّل في التصريح بالتفكيك فيمكن الاستغناءُ عن اسمه بالشرطة السفليّة مثل:

val (_, status) = getResult()

وبهذه الطريقة لن تُستدعَى دالة الجزء (component function) الموافقةِ له.

التفكيك في lambdas (بدءًا من الإصدار 1.1)

تُستخدَم صيغةُ التصريحات بالتفكيك في متحولات lambda أيضًا، وإذا كان متحول lambda من النوع Pair (أو من النوع Map.Entry أو أي نوعٍ آخر يحتوي على دالة جزءٍ [component function] مناسبةٍ) فيُمكن حينئذٍ استخدام أكثر من متحولٍ (تُحاط المتحولات بالأقواس ())، كما في الشيفرة الآتية:

map.mapValues { entry -> "${entry.value}!" }
map.mapValues { (key, value) -> "$value!" }

ويتَّضِح الفرق بين التصريح عن متحوِّلين والتصريح عنهما بالتفكيك، كما يلي:

{ a -> ... } // متحول واحد
{ a, b -> ... } // متحولان
{ (a, b) -> ... } // تصريح مفكك بمتحولين
{ (a, b), c -> ... } // تصريح مفكك بمتحولين ومتحول ثالث مستقل

فإن كان أحد أجزاء التصريح بالتفكيك غير مستخدمٍ يُستعاض عنه بالشرطة السفليّة دون الحاجة لتسميته، مثل:

map.mapValues { (_, value) -> "$value!" }

كما ويمكن تحديد النوع الإجماليّ لمتحولات التصريح بالتفكيك أو لمتحولٍ واحدٍ منها بشكلٍ منفصلٍ كما في الشيفرة الآتية:

map.mapValues { (_, value): Map.Entry<Int, String> -> "$value!" }

map.mapValues { (_, value: String) -> "$value!" }

مصادر