در مقالههای قبلی این مجموعه آموزشی، نحوهی تبدیل حلقههای نوشتهشده به سبک دستوری (Imperative) به سبک تابعی (Functional) را بررسی کردیم. در این مقاله میبینیم چگونه حلقهی foreach سبک دستوری را به سبک تابعی تبدیل کنیم. علاوه بر آن، نحوهی انتخاب گزینشی عناصر با استفاده از دستور if و تبدیل آن به سبک تابعی را هم یاد میگیریم.
جاوا ۵ سینتکس بسیار محبوب foreach را معرفی کرد. مثلاً برای حلقهزدن روی یک مجموعه از رشتههای String نمایانگر نامها، چیزی شبیه for(String name: names) مینویسیم. در سطح bytecode، foreach به استفاده از یک Iterator تبدیل میشود: تا زمانی که Iterator به ما بگوید عنصر بعدی وجود دارد، عنصر بعدی را برای پردازش دریافت میکنیم. به بیان دیگر، foreach یک سینتکس شکر (Syntactic Sugar) خوب و مختصر برای حلقهی while روی عناصر فراهمشده توسط Iterator است. تبدیل foreach به سبک تابعی کار بسیار سادهای است. بیایید ببینیم چگونه این کار را انجام دهیم.
در اینجا مثالی از حلقهی foreach روی مجموعهای از نامها را میبینیم:
List<String> names = List.of("Jack", "Paula", "Kate", "Peter");
for(String name: names) {
IO.println(name);
}
در هر قدم از حلقه، متغیر name به مقدار جدیدی متصل میشود، همانطور که حلقه از یک عنصر به عنصر بعدی در مجموعهی دادهشده پیشروی میکند. تبدیل foreach سبک دستوری به سبک تابعی، استفادهی مستقیم و سرراست از متد iterator داخلی forEach است. به آن iterator داخلی (Internal Iterator) گفته میشود چون حرکت به عنصر بعدی بهصورت خودکار و داخلی مدیریت میشود، نه بهصورت خارجی و صریح.
بیایید حلقه را با سبک تابعی بازنویسی کنیم:
List<String> names = List.of("Jack", "Paula", "Kate", "Peter");
names.forEach(name -> IO.println(name));
بسیار ساده بود: حلقهی for به فراخوانی متد forEach() روی مجموعه تبدیل شد. در هر قدم از حلقه، Lambdaای که بهعنوان آرگومان به forEach() پاس داده شده، با عنصر بعدی موجود در مجموعه فراخوانی میشود و اجرا میگردد.
نسخهی اندکی متفاوت از این حلقه با استفاده از stream() را هم ببینید:
List<String> names = List.of("Jack", "Paula", "Kate", "Peter");
names.stream()
.forEach(name -> IO.println(name));
متد forEach() هم روی Collection<T> و هم روی Stream<T> موجود است. اما متدهایی مثل filter() که بهزودی استفاده خواهیم کرد، فقط روی Stream<T> موجود هستند و روی Collection نیستند. این طراحی عمدی است تا کارایی لازم هنگامی که چندین عملیات میانی (Intermediate Operation) قبل از عملیات پایانی (Terminal Operation) مثل forEach()، findFirst() و... اجرا میشوند فراهم گردد.
فرض کنید در میانهی حلقه، میخواهیم بعضی مقادیر را از مجموعه بر اساس شرطی خاص انتخاب کنیم. مثلاً اگر فقط نامهایی با طول ۴ کاراکتر را بخواهیم چاپ کنیم؟ در سبک دستوری اینطور عمل میکنیم:
List<String> names = List.of("Jack", "Paula", "Kate", "Peter");
for(String name: names) {
if(name.length() == 4) {
IO.println(name);
}
}
در سبک تابعی، متد filter از Stream جایگزین مستقیم if سبک دستوری است. متد filter اجازه میدهد یک عنصر از مجموعه به مرحلهی بعدی pipeline تابعی عبور کند — به شرطی که Predicateای که بهصورت Lambda به filter() پاس داده شده، true برگرداند. در غیر این صورت، آن مقدار از پردازش بعدی حذف میشود و دور ریخته میشود.
بیایید کد قبلی را به سبک تابعی تبدیل کنیم:
List<String> names = List.of("Jack", "Paula", "Kate", "Peter");
names.stream()
.filter(name -> name.length() == 4)
.forEach(name -> IO.println(name));
متد filter() مانند یک دروازه (Gate) عمل میکند: باز میشود تا بعضی عناصر عبور کنند و بسته میشود تا بعضی عناصر را رد یا حذف نماید — همانطور که حلقه پیشروی میکند و عناصر یکییکی عبور میکنند.
در مقالههای قبلی این مجموعه، معادل تابعی حلقههای سنتی for را دیدیم. در این مقاله دیدیم که چگونه foreach سبک دستوری جاوا ۵ به سینتکس شیک و زیبای سبک تابعی تبدیل میشود. علاوه بر آن، شرط if درون حلقهی سبک دستوری به فراخوانی متد filter() از Stream API تبدیل میگردد.
هر جا حلقهی foreach دیدید، مستقیماً از متد forEach() روی مجموعه استفاده کنید. اگر بدنهی foreach شامل دستور if برای انتخاب گزینشی بعضی مقادیر است، آنگاه از API stream() به همراه فراخوانی متد filter() استفاده نمایید.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://dev.java/learn/