English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
في هذا المقال، ستعلم من خلال الأمثلة ما هي الكلوزات، النحو، أنواع الكلوزات في Swift
في المقال حول الدوال في Swift، قمنا بإنشاء دالة باستخدام كلمة المفتاح func. ولكن هناك نوع خاص آخر من أنواع الدوال في Swift يُدعى الكلوزات، يمكن إنشاؤها دون استخدام كلمة المفتاح func أو اسم الدالة
تماما مثل الدوال، يمكن للكلوزات أن تأخذ المعاملات وتعيد القيم. إنها تحتوي أيضًا على مجموعة من الأوامر التي تنفذ عند الطلب، ويمكن تخصيصها كدوال وتسليمها إلى المتغيرات أو الثوابت
الكلوزات في Swift تشبه إلى حد كبير الكودات (blocks) في C و Objective-C وغيرها من لغات البرمجة الأخرى
الدوال العالمية والدوال المدمجة هي في الواقع كلوزات خاصة
أشكال الكلوزات هي:
الدوال العالمية | الدوال المدمجة | تعبيرات الدوال الحلقة |
الكلوزات المسمى لا يمكنها التقاط أي قيم | الكلوزات المسمى، يمكنها التقاط القيم من الدالة المحيطة | الكلوزات غير المسمى، باستخدام لغة بسيطة، يمكنها التقاط القيم من البيئة المحيطة |
يوجد العديد من الoptimizations في الكلوزات في Swift:
تقدير نوع المعاملات والنتائج بناءً على السياق
من خلال العودة التلقائية من الكلوزات المحددة على سطر واحد (إذ يمكن تجنب استخدام كلمة العودة return)
يمكن استخدام أسماء المعامل المبسطة، مثل $0, $1 (من 0، مما يعني المعامل i ...)
يقدم نحو الحلقة التالية (Trailing closure syntax)
يحدد هذا التعريف دالة الحلقة التي تأخذ معاملًا وتقوم بتقديم نوع معين:
{(parameters) -> return type in التعليمات }
let simpleClosure = { print("مرحبًا، عالم!") } simpleClosure()
نتيجة تنفيذ البرنامج أعلاه هي:
مرحبًا، عالم!
يحتوي هذا الشكل من الدوال الحلقة على إدخالين ويعود قيمة منطقية:
{(Int, Int) -> Bool in بند1 بند 2 --- بند n }
let simpleClosure: (String) -> (String) = { name in let greeting = "مرحبًا، عالم! " + "برنامج" return greeting } let result = simpleClosure("مرحبًا، عالم!") print(result)
نتيجة تنفيذ البرنامج أعلاه هي:
مرحبًا، عالم! برنامج
تعتبر تعبيرات الدوال الحلقة طريقة لإنشاء دوال داخلة باستخدام صيغة بسيطة. توفر تعبيرات الدوال الحلقة بعض تحسينات اللغة مما يجعل كتابة الدوال الحلقة أسهل وأكثر وضوحًا.
يقدم مكتبة Swift القياسية اسم sorted(by:) الطريقة، سترتب القيم في المجموعة النوعية المعروفة بناءً على الدالة الحلقة التي تقدمها لتصنيف.
بعد إكمال الترتيب، سيعود طريقة sorted(by:) مجموعة تحتوي على نفس حجم المجموعة الأصلية وتحتوي على نفس النوع من العناصر ولكن العناصر مرتبة بشكل صحيح. لن يتم تعديل المجموعة الأصلية بواسطة طريقة sorted(by:).
يحتاج إلى إدخال اثنين من المعلمات لـ sorted(by:)
مجموعة من الأنواع المعروفة
الدالة الحلقة، يجب أن تأخذ دالتين من نفس نوع عنصر اللوحة وتقوم بتقديم قيمة نوع منطقية لتوضيح ما إذا كانت القيمة الأولى تظهر قبل القيمة الثانية عند إنهاء الترتيب. إذا كانت القيمة الأولى تظهر قبل القيمة الثانية، يجب أن تعود دالة الترتيب الحلقة true، العكس يرجع false。
إدراج Cocoa let names = ["AT", "AE", "D", "S", "BE"] // استخدم الدوال العادية (أو الدوال المدمجة) لتقديم وظيفة الترتيب، يجب أن يكون نوع الدالة الحلقة (String, String) -> Bool. func backwards(s1: String, s2: String) -> Bool { return s1 > s2 } var reversed = names.sorted(by: backwards) اطبع(reversed)
نتيجة تنفيذ البرنامج أعلاه هي:
["S", "D", "BE", "AT", "AE"]
如果第一个字符串 (s1) 大于第二个字符串 (s2),backwards函数返回true,表示在新的数组中s1应该出现在s2前。 对于字符串中的字符来说,"大于" 表示 "按照字母顺序较晚出现"。 这意味着字母"B"大于字母"A",字符串"S"大于字符串"D"。 其将进行字母逆序排序,"AT"将会排在"AE"之前。
Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数。
إدراج Cocoa let names = ["AT", "AE", "D", "S", "BE"] var reversed = names.sorted( by: { $0 > $1 } ) اطبع(reversed)
$0和$1表示闭包中第一个和第二个String类型的参数。
نتيجة تنفيذ البرنامج أعلاه هي:
["S", "D", "BE", "AT", "AE"]
如果你在闭包表达式中使用参数名称缩写, 您可以在闭包参数列表中省略对其定义, 并且对应参数名称缩写的类型会通过函数类型进行推断。in 关键字同样也可以被省略.
实际上还有一种更简短的方式来撰写上面实例中的闭包表达式。
Swift 的String
类型定义了关于大于号 (>
) 的字符串实现,其作为一个函数接受两个String
类型的参数并返回Bool
类型的值。
而这正好与sort(_:)
方法的第二个参数需要的函数类型相符合。
因此,您可以简单地传递一个大于号,Swift可以自动推断出您想使用大于号的字符串函数实现:
إدراج Cocoa let names = ["AT", "AE", "D", "S", "BE"] var reversed = names.sorted(by: >) اطبع(reversed)
نتيجة تنفيذ البرنامج أعلاه هي:
["S", "D", "BE", "AT", "AE"]
尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。
func someFunctionThatTakesAClosure(closure: () -> Void) { // 函数体部分 } // 以下是不使用尾随闭包进行函数调用 someFunctionThatTakesAClosure({ // 闭包主体部分 }) // 以下是使用尾随闭包进行函数调用 someFunctionThatTakesAClosure() { // 闭包主体部分 }
إدراج Cocoa let names = ["AT", "AE", "D", "S", "BE"] //لفة متابعة var reversed = names.sorted() { $0 > $1 } اطبع(reversed)
الـ { $0 > $1} بعد sort() هو لفة متابعة.
نتيجة تنفيذ البرنامج أعلاه هي:
["S", "D", "BE", "AT", "AE"]
ملاحظة:
إذا كانت الدالة تحتاج إلى معامل تعبير لفة واحدة فقط، عند استخدام اللفة المتابعة، يمكنك حتى:()
تُنسى.
reversed = names.sorted { $0 > $1 }
يمكن لللفة إلتقاط ثوابت أو متغيرات في سياق تعريفها.
حتى لو لم يعد مجال إعلان هذه الثوابت والمتغيرات موجودًا، يمكن لللفة استخدام وتعديل هذه القيم داخل جسم الدالة الخاصة بها.
أبسط شكل للفة في Swift هو الدالة الداخلية، وهي دالة معينة داخل جسم دالة أخرى.
يمكن للدوال الداخلية أن تلتقط جميع المعاملات والثوابت المحددة داخل الدالة الخارجية.
انظر إلى هذا المثال:
func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount return runningTotal } return incrementor }
دالة makeIncrementor، لديها معامل من نوع Int ويكون اسم المعامل الخارجي forIncremet، مما يعني أنه يجب استخدام هذا الاسم الخارجي عند التطبيق. العائد هو-> Int
دالة.
في الجسم الخاص بالدالة، تم إعلان متغير runningTotal ودالة incrementor.
لا يحصل دالة incrementor على أي معامل، ولكنها تصل إلى متغيرات runningTotal وamount داخل الجسم الخاص بها. هذا لأنها تحقق من خلال إلتقاط متغيرات runningTotal وamount التي كانت موجودة داخل الجسم الخاص بالدالة التي تحتويها.
بسبب عدم تعديل متغير amount، يلتقط incrementor نسخة من المتغير ويحتفظ بها مع incrementor.
لذا، عند استدعاء هذه الدالة، سيتم التراكم:
إدراج Cocoa func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount return runningTotal } return incrementor } let incrementByTen = makeIncrementor(forIncrement: 10) // القيمة المعدة هي 10 اطبع(incrementByTen()) // القيمة المعدة هي 20 اطبع(incrementByTen()) // القيمة المعدة هي 30 اطبع(incrementByTen())
نتيجة تنفيذ البرنامج أعلاه هي:
10 20 30
في المثال السابق، incrementByTen هو ثابت، ولكن اللفات التي يشير إليها هذه الثوابت يمكن أن تزيد من قيمة المتغيرات التي تم إلتقاطها.
لأن الدوال واللفات هي أنواع مرجعية.
عندما تعين دالة أو لفة إلى متغير أو ثابت، فأنت في الواقع تعين قيمة المتغير أو الثابت إلى مرجع الدالة أو اللفة. في المثال السابق، يشير incrementByTen إلى مرجع اللفة وهو ثابت، وليس محتوى اللفة نفسها.
هذا يعني أيضًا أنه إذا قمت بتعيين قيمة اللفة إلى متغيرين أو ثابتين مختلفين، فإن كلاهما سيشيران إلى نفس اللفة:
إدراج Cocoa func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount return runningTotal } return incrementor } let incrementByTen = makeIncrementor(forIncrement: 10) // القيمة المعدة هي 10 incrementByTen() // القيمة المعدة هي 20 incrementByTen() // القيمة المعدة هي 30 incrementByTen() // القيمة المعدة هي 40 incrementByTen() let alsoIncrementByTen = incrementByTen // القيمة المعدة هي 50 print(alsoIncrementByTen())
نتيجة تنفيذ البرنامج أعلاه هي:
50