توی این نوشته سعی میکنم در مورد sequence ها در کاتلین صحبت کنم که به نظرم میرسه یکی از بخش های جالبی هست که کمتر مورد توجه قرار گرفته.
سعی میکنم با یک مثال ساده جلو برم
فرض کنید لیست اعداد 1 تا 9 رو به این شکل داریم.
و حالا میخواهیم به این شکل عمل کنیم که :
نتیجه هم مشخصه . یک لیست به شکل زیر.
ولی بیاید با هم فکر کنیم برای این که به این لیست برسیم. چقدر هزینه دادیم.
اول کار یک لیست از 1 تا 9 داشتیم .
بعدش با فانکشن map ای که نوشته شد به یک لیست 9 تایی دیگه رسیدیم که همه ی اقلامش ضرب در 2 شدن. ( ساخته شدن یک لیست جدید و انجام عمل ضرب روی آیتم های قبلی و ریختن توی لیست جدید )
بعد دوباره روی این لیست جدید هم یک عملیات دیگه انجام دادیم که همه ی قلم ها یکی ازشون کم شد و به یک لیست دیگه رسیدیم . (ساخته شدن لیست جدید دوم و انجام عمل تفریق روی آیتم های قبلی و ریختن توی لیست جدید دوم )
و در انتها هم انتخاب سه تا آیتم ابتدای این لیست. (ساخته شدن یک لیست جدید سوم و اضافه شدن سه تا آیتم)
عکس زیر رو ببینید.
همونطور که مشخصه داریم هزینه ی زیاد و غیر ضروری ای رو پرداخت میکنیم.
برای مثال 2 * 8 یکی از اون عملیات هایی هست که هیچ نیازی بهش نداشتیم و توی نتیجه نهایی هم تاثیری نداشته.
حالا فرض کنید به جای روش بالا به این شکل عمل کنیم که برای 3 تا قلم اول ( (take(3 ) ، قلم اول رو انتخاب کنیم ، ضربدر 2 ، منهای یک . مجدد برای قلم دوم و به همین ترتیب ...
رفتاری شبیه به شکل زیر
یعنی به جای اینکه مراحل رو افقی بریم ؛ عمودی بریم.
نتیجه این میشه که با تعداد محاسبات کمتر به نتیجه ی یکسانی میرسیم.
اما حالا چطور میشه چنین کاری رو انجام داد ؟
توی کاتلین یه extension function هست که یک لیست رو میگیره و اون رو به sequence تبدیل میکنه. کد زیر رو ببینید.
این کد ، 2تا تفاوت با کد قبلی داره. متدهای ()asSequence و ()toList .
که این دو خط باعث ورود و خروج ما از دنیای تفکر sequence ای میشن!
البته اون متد های map و take هم که اون وسط وجود دارن ظاهرا تفاوتی با کد قبلی ندارن، ولی در واقع یک تفاوت اساسی دارن ، قبلی ها روی لیست ها یا دقیق تر بگم Iterable ها کار میکنن ولی این یکی ها ، روی Sequence ها . تعریف این دو تا متد رو ببینید.
به طور کلی 2 دسته اپراتور برای sequence وجود داره.
میشه گفت رفتاری که که sequence ها دارن یک رفتار lazy طور هست. به این معنی که شما اگر توی کد قبلی از map استفاده میکردین، در لحظه، کد اجرا میشد و خروجیش مشخص بود ولی توی sequence ها همه ی اپراتور های میانی توی یک لیست که operation ها رو نگه میداره اضافه میشن و تا زمانی که به یک اپراتور پایانی مثل ()toList برسیم اجرا نمیشن.
همیشه لازم نیست که یک لیست داشته باشیم و با متد ()asSequence بقیه ی عملیات هامون رو شروع کنیم. یعضی وقت ها میشه از همون اول کد sequence ای زد . این جا رو ببینید.
متد ()generateSequence با ورودی 1 ، از عدد 1 شروع میشه و برای محاسبه ی عدد بعدی اون رو +2 میکنه.
حالا ما میخواهیم که تا 5 بار این کار انجام بشه و در آخر یک لیست برگردونده میشه.
یک نکته ی جالب این کد داره ، به هر تعدادی که کاربر مشخص میکنه محاسبات انجام میشه. یعنی میشه گفت تا بی نهایت میتونه مقادیر رو حساب کنه .
این هایی که دیدیم تازه چندتا از متدهای ساده ای بود که توی پکیج sequence ها وجود داره. یه نگاه به لینک زیر بندازید.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/#functions
قطعا خیر! فرض کنید به جای 3 تا آیتم اول لیست همه ی آیتم ها رو میخواستیم.
take(9)
یعنی تعداد محاسباتی که لازم بود تا به جواب آخر برسیم تقریبا به اندازه ی همون حالت قبلی میشد به اضافه این که لازم بود توی حافظه آبجکت هایی برای نگهداری مراحل نگهداری بشه که این ها یعنی عملا به پرفورمنس بالاتری نمیرسیدیم.
میشه گفت با در نظر گرفتن بعضی از شرایط ، sequence ممکنه بهتر از collection عمل بکنه، فقط همین! البته اگر تعداد اقلام کم باشه خیلی تفاوتی بین استفاده از collection ها (روش اول) و sequence ها نیست. (مثل مثال ما که روی 9 تا آیتم بود).
خیلی خوشحال میشم در صورتی که این نوشته رو دوست داشتید ❤️ کنید یا نظرتون را از طریق کامنت برام بنویسید.