آموزش برنامه نویسی اسکالا (2)

حتما مطالب قبلی را مطالعه بفرمایید در ادامه بحث لیستها ، به معرفی چندین متد در لیست اشاره میکنیم این متد چی هست و کارش چی هست List() or Nil لیست خالی هستList("Cool", "tools", "rule") یک لیست جدید از جنس [List[Sringایجاد کرده و سه تا مقدار درداخل آن قرار داده استval thrill = "Will" :: "fill" :: "until" :: Nilیک لیست جدید از جنس [List[Sringایجاد کرده و سه تا مقدار درداخل آن قرار داده استList("a", "b") ::: List("c", "d") ترکیب دوتا لیست از جنس [List[String و ایجاد یک لیست جدید شامل "d","c","b","a"thrill(2)برگرداندن عنصر دوم که پایه آن 0 می باشد.و که مقدار "until" را بر می گرداند.thrill.count(s => s.length == 4)تعداد عناصر thrill را که طولشان 4 می باشد ، می شمارد. و مقدار 2 را برمیگرداند.thrill.drop(2)دوتا عنصر اول را برنمی گرداند و عنصر سوم را برمی گرداند .("List("until thrill.dropRight(2)دوتا عنصر از سمت راست را برمی دارد و یک عنصر بر میگرداند.("List("Willthrill.exists(s => s == "until")تمام عناصر موجود در thrill را که با مقدار "untill" یکی باشد true برمی گرداند.thrill.filter(s => s.length == 4)لیست از تمام عناصز از لیست راکه طول رشته انها چهار تا می باشد برمیگرداند.("List("Will", "fillthrill.forall(s =>s.endsWith("l"))نشان میدهد همه عناصر که انتهای انها به "I" ختم شده باشد true بر می گرداند.thrill.foreach(s => print(s))دستور print را بروی تمام عناصر آرایه اجرا میکند. "Willfilluntil" را چاپ میکند.thrill.foreach(print) این دستور نیز همان کار دستور بالا را انجام می دهد.thrill.head اولین عنصر از thrill را میدهد "Will"thrill.initلیستی از همه ، جز آخرین عنصر در را برمی گرداند. ("List("Will", "fillthrill.isEmptyنشان می دهد که thrill خالی هست یا نه و مقدار false را برمی گرداند.thrill.lastآخرین عنصر لیست را برمی گرداند "until"thrill.length تعداد عناصر در لیست را بر می گرداند که مقدار 3 می باشدthrill.map(s => s + "y") یک تناظر یک به یک به عناصر لیست که مقدار "y" را به تمام عناصر String اضافه میکند و لیست جدید می دهد.("List("Willy", "filly", "untilythrill.mkString(", ")یک رسته جدید با عناصر thrill که با " ," از هم جدا شده اند را می سازد. "Will, fill, until"thrill.remove(s => s.length == 4)لیستی از همه عناصری را برمیگرداند که رشته ای به طول 4 نباشد. ("List("untilthrill.reverseتمام عناصر را بصورت برعکس برمیگرداند. ("List("until", "fill", "Willthrill.sort( (s, t) =>s.charAt(0).toLower < t.charAt(0).toLower)یک لیست از تمام عناصر رابصورت مرتب برمبنای حروف الفبا و حروف کوچک بر میگرداند. List("fill", "until", "Will"))thrill.tail تمام عناصر بغیر از عنصر اول را بر می گرداند. ("List("fill", "untilاستفاده از tuplesیک object مفید دیگه tuple ها هستند.شبیه لیست میباشد.tuple ها غیر قابل تغییر هستند اما نه شبیه List ها.tuple ها می توانند شامل انوع type ها در عناصر باشند. در حالیکه یک لیست میتواند شامل [List[String و یا [List[Int باشد.در صورتیکه یک tuple میتواند در یک لحظه شامل Integer , String باشد.tuple ها خیلی مفید هستند . برای مثال اگر شما بخواهید چندین object از یک متد را برگردانید .در حالیکه در جاوا شما اغلب ایجاد میکنید یک JavaBean ، شبیه کلاس برای نگهداشتن چندین مقدار برای برگرداندن،در صورتیکه شما در اسکالا خیلی ساده با یک tuple بر می گردانید. و ان خیلی ساده است ، یک tuple به تعداد object هایی که می خواهیم ، ایجاد میکنیم فقط object ها را درون پارانتز ، و با کاما از هم جدا میکنیم.وقتی شما یکبار یک tuple را مقدار دهی میکنید.شما میتوانید به عناصر آن دسترسی داشته باشید بصورت با یک ._ با یک ایندکس از عناصر ، به مثال زیر دقت کنیم :ایجاد کردن و استفاده از tupleدر خط اول شما یک tuple تعریف کردین شامل عدد99 Integer که اولین عنصر می باشد و بعدی String که "Luftballons" میباشد و دومین عنصر می باشد.اسکالا استنتاج میکند این [Tuple2[Int, String و نوع متغیر را همچنین در pair قرار میدهدخط دوم شما دسترسی دارین به فیلد _1 که تولید شده برای عنصر اول یعنی مقدار 99 ، "." در اینجا نقش دسترسی به عناصر را به شما میدهد .و یا اینکه یک متدی را اجرا کنید.در این مورد شما دسترسی دارین به عناصر فیلد ها با _1 ، درصورت اجرای این اسکریپت شما به داده زیر دسترسی خواهید داشت. 99 Luftballonsنوع واقعی یک tuple به تعداد عناصر و نوع عناصر آن بستگی دارد.بنابراین نوع (99, "Luftballons") یک تاپل دوعنصری می باشد [Tuple2[Int, String و نوع این داده ('u', 'r', "the", 1, 4, "me') یک Tuple6[Char, Char, String, Int, Int, String] تایی می باشد. توجه داشته باشیم که هر نوع tuple رو می توانیم ایجاد کنیم که در اسکالا تا Tuple22 می توانید بسازید.دسترسی به عناصر یک Tuple : ممکن هست شما تعجب کنید که چرا نمی توانید به عناصر یک Tuple شبیه یک لیست دسترسی داشته باشید.از طریق "(pair(0" ، بخاطر اینکه یک لیست متد apply همیشه یک نوع برمی گردونه، اما یک Tuple می تواند انواع مختلفی برگرداند._1 یک نوع بر گرداند و _2 یک نوع دیگه بر می گرداند.بنابراین_N تعداد هر کدام می تواند انواع مختلفی داشته باشند و یک نوع واحد نداشته باشند.استفاده از sets و maps:از انجا که اسکالا هدفش کمک به شما در مورد برنامه نویسی تابعی و سلسه مراتبی هست.بنابراین مجموعه ای از کتابخانه هه ساخته شده که اشاره میکند به مجموعه های قابل تغییر و غیر قابل تغییر .برای مثال آرایه ها همیشه هستند قابل تغییر و لیست ها همیشه هستند غیر قابل تغییر، اسکالا فراهم کرده قابل تغییر و غیر قابل تغییر را برای set ها و map ها، اما از اسامی ساده یکسان برای هر دو نسخه استفاده می کند.برای setها وmap ها ، اسکالا از مدل تغییر پذیر در ساختار کلاس استفاده میکند.برای مثال ، api اسکالا یک خصوصیت (trait) پایه ای برای set ها در نظر گرفته است.جایی که یک trait شبیه یک interface در جاوا می باشد.اطلاعات بیشتر رو بعدا در این مورد صحبت خواهیم کرد.بعدا اسکالا دوتا subtrait برای set ها ایجاد کرده یکی برای قابل تغییر (mutable) و دیگری برای (immutable) که در شکل پایین می توانید ببینید.این سه تا ویژگی همه با یک اسم ساده Set مشترک هستند.با اینحال که نام کامل آنها کاملا از هم متفاوت هستند ، زیرا هر کدام در یک package متفاوت قرار دارد.کلاسهای set همچون کلاسهای hashset در اسکالا api میباشد که در شکل زیر می بینید. هردو trait قابل تغییر و غیر قابل تغییر از set توسعه داده شده است. اگرچه شما در جاوا interface ها رو پیاده سازی کرده اید، اما شما در اسکالا extend یا mix in کردین trait ها رو.ساختار سلسله مراتب set در اسکالابنابراین ، اگر شما از یک Hashset استفاده کنید.شما میتوانید بر مبنای نیاز خودقابل تغییر و یا غیر قابل تغییر استفاده کنید.برای تعریف کردن یک set به مثال زیر توجه کنید:ایجاد کردن و مقدار دهی اولیه کردن و استفاده کردن از set غیر قابل تغییر (immutable)در اولین خط کد شما تعریف کردین یک متغیر var بنام jetSet، و مقدار دهی کردین آن بصورت immutable ، شامل دو تا مقدار "Boeing" و "Airbus" ، در این مثال ما به شما نشان می دهیم که شما مثل لیست ها و ارایه ها می توانید set ها را نیز تعریف کنید.با استفاده از متد apply شما اینکار را روی companion object انجام دادید.شما apply را روی companion object برای scala.collection.immutable.Set ایجاد میکنید ( companion object : یعنی اینکه اگر بخواهیم فیلد یا خاصیتی ایجاد کنیم که در همه نمونه‌های کلاس قابل دسترسی باشد از کلمه کلیدی companion استفاده می‌کنیم ) که یک نمونه از یک Set immutable را بصورت پیش فرض بر می گرداند. مفسر اسکالا نوع jetSet رو از [immutable Set[String استنتاج میکند.برای اضافه کردن یک عضو به یک set از "+ " میتوانید استفاده کنید.هر دو مدل mutable و immutable از "+" استفاده میکنند.اما رفتار متفاوتی دارند.با اینحال mutable ها اضافه میکنند عناصر رو به خودشان و immutable ها وقتی یک عنصر جدید اضافه میکنند به یک لیست جدید می سازند و مقدار جدید نیز در داخل آن قرار می گیرد و بر می گرداند.شما با immutable set کار میکنید بنابراین "+" یک مجموعه جدید را فراخوانی و ایجاد میکند.با اینکه mutable set ها به خودشان مراجعه میکنند .ولی immutable ها اینکار را انجام نمی دهند.در این مورد ، در خط دوم کد “jetSet" += "Lear" اساسا فرم کوچک شده مورد زیر می باشد."jetSet = jetSet + "Lear ، در خط دوم شما مجدد مقدار jetSet رو با یک set جدید جایگذین میکنید.و در نهایت در خط آخر شما دنبال یک set که نیست و مقدار false رو چاپ می کند.اگر شما بخواهید یک set قابل تغییر (mutable) درست کنید ، با توجه به شکل زیر عمل میکنیم:ایجاد کردن و مقدار دهی اولیه کردن و استفاده کردن از set قابل تغییر (mutable) در خط اول شما کلاس مربوط به mutable را وارد می کنید.همانند جاوا، شما می توانید یک کلاس را وارد کنید و از نام آن استفاده کنید.همانند Set، بجای اینکه از کل آدرسش در برنامه استفاده کنیم.می توانیم کوچک شده ان را استفاده کنیم.دز خط دوم وقتی شما دارید Set رو انجام میدهید کامپایلر اسکالا scala.collection.mutable.Set را می فهمد.شما متغییر movieSet را با دوتا مقدار Hitch و Poltergeist مقدار دهی اولیه میکنید.در خط بعدی ما Sherk رو به Set با عملگر += اضافه می کنیم.همانطور که قبلا گفته شد، عملگر += برای Set قابل تغییر تعریف شده است.شما اگه بخواهید می توانید به جای movieSet += "Sherk" از دستور زیر نیز استفاده کنید: ("movieSet.+=("Shrek اگرچه پیاده سازی های پیش فرض برای Set برای روشهای قابل تغییر و غیر قابل تغییر بصورت factory تنظیم شده است ، برای بیشتر مواقع این تنظیمات کافی خواهد بود.گاهی اوقات شما ممکن هست یه کلاس set explicit لازم داشته باشید.خوشبختانه، نحوه نوشتن شبیه هم هست.به سادگی کلاسی که لازم داریدimport می کنید.و از متد factory روی آن object استفاده می کنید.اگه شما یک setغیرقابل تغییر لازم داشته باشید باید اینکار رو انجام بدهید:import scala.collection.immutable.HashSet val hashSet = HashSet(&quotTomatoes&quot, &quotChilies&quot) println(hashSet + &quotCoriander&quot)یک مجموعه مفید دیگه در اسکالا Map می باشد.با sets اسکالا mutable , imutable را برای map فراهم میکند.یک کلاس سلسه مراتبی استفاده می کنیم.ساختار سلسله مراتبی Mapکه اگر شکل رو نگاه کنیم این شکل خیلی شبیه به یکی از شکل های set می باشد.یک trait برای map در scala.collection وجود دارد.و دوتا subtrait برای maps وجود دارد: یک map قابل تغییر در scala.collection.mutable و یک map غیر قابل تغییر در scala.collection.imutable قرار دارد.پیاده سازی های Map ، همچون Hashmaps نشان داده شده در شکل بالا می تواند trait قابل تغییر و غیر قابل تغییر را توسعه می دهد .شما می توانید ایجاد کنید و مقداردهی کنید map را با استفاده از method factory شبیه arrays, lists ,sets برای مثال میتوانید ببینید:ایجاد میکنیم map و مقدار دهی میکنیم و استفاده میکینم از یک mutableدر خط اول شما mutable map رو اضافه کردین و سپس یک متغیر val از جنس map با دوتا پارامتر یکی int بعنوان کلیذ , یکی دیگه مقدار ، با نام treasureMap ایجاد کردیم.map خالی هست چون شما چیزی رو به factory method پاس نکردین.و () در Int,String] map] خالی میباشد.در 3 خط بعدی شما key/value رو با عملگرهای -> و += به treasureMap اضافه کردین.با توجه به توضیحات قبلی کامپایلر اسکالا یک عبارت عملگر دودیی Go to island." <- 1" تبدیل میشود به (1). -> ("Go to island.") بنابراین وقتی شما می نوسید 1 -> "go to island " یعنی شما یک متد -> را صدا میکنید که روی متغیر int با مقدار 1 و "go to island " را به آن انتصاب میکنید.شما از متد -> در هر جایی از برنامه های اسکالا میتوانید استفاده کنید.که یک tuple دو عنصری شامل key/value بر می گرداند.شما سپس این tuple دوعنصری را با متد =+ در متغییر treasureMap قرار می دهید.در نهایت در خط آخر مقدار کلید 2 را از متغیر treasureMap مطابقت داده و مقدار Find big X on ground. در خروجی چاپ می کند.اگر شما ترجیح بدهید یک map immutable استفاده کنید ،لزومی نداره import انجام بدهید.immutable در map بصورت پیش فرض می باشد.به مثال زیر توجه کنید :ایجادو مقدار دهی اولیه و استفاده ار map (غیر قابل تغییر)در این کد دیگه import انجام نمی دهیم.در خط اول وقتی شما Map رو صدا میکنید بصورت پیش فرض a scala.collection.immutable.Map میباشد.شما 5 تا tuples را به متد map factory نگاشت میکنید.که map immutable شامل جفت مقادیر عناصر می باشد را بر می گرداند.اگر کد بالا را اجرا کنیم IV را بر می گرداند.شناخت سبک یادگیری functionalهمانطور که پیشتر گفته شد، اسکالا به شما اجازه میدهد سبک کد نویسی خود رو بهتر کنید.و شما رو تشویق میکند که یک سبک پر کاربردی رو استفاده کنید.اگه شما از یک زبان برنامه نویسی دیگه به این سمت امده اید ،اگر شما یک برنامه نویس جاوا هستید ، یکی از اصلی ترین مشکل شما این است که چگونه به سبک functional برنامه بنویسید.ما به شما راهنمایی های خیلی زیادی را در این نوع برنامه نویسی به شما یاد خواهیم داد.این کار به بخشی از کار شما احتیاج دارد ، و ما شما را به تلاش برای انجام این کار تشویق می کنیم. اگر از قبل زمینه برنامه نویسی دارید ، ما معتقدیم یادگیری برنامه نویسی به سبک functional نه تنها شما را یک برنامه نویس بهتر Scala می نماید ،بلکه افق های شما را گسترش می دهد و به طور کلی شما را به یک برنامه نویس بهتر، تبدیل می کند.اولین قدم شناخت تفاوت بین دو سبک در برنامه نویسی است،اگه در برنامه خودتون مقدار زیادی vars وجود داشته باشد ، به احتمال زیاد یک مدل برنامه نویسی imperative است. اگر در کد خودتون به هیچ وجه vars وجود نداشته باشد.این یک سبک برنامه نویسی تابعی می باشد. بنابراین ، یک راه برای حرکت به سمت برنامه نویسی functional ، تلاش برای برنامه ریزی بدون vars است. اگر پس زمینه برنامه نویسی مانند Java ، C ++ یا C # هستید، ممکن است متغیر vars را به عنوان یک متغیر عادی و vals را به عنوان نوع خاصی از متغیر درنظر بگیرید. از طرف دیگر ، اگر چس زمینه برنماه نویسی تابعی شما مانند هاسکل ، OCaml یا Erlang استفاده می کنید ، ممکن استvals به عنوان یک متغیر عادی و var را متغیر وابسته بدانید. با این وجود ، دیدگاه Scala این است که Val و var فقط دو ابزار مختلف در جعبه ابزار شما هستند که هم مفید هستند و هم ذاتاً مشکل ساز نیستند. اسکالا شما را تشویق می کند که به سمت vals حرکت کنید ، اما در نهایت با توجه به شغل موجود ، به بهترین ابزار دسترسی خواهید یافت. حتی اگر با این فلسفه متعادل موافق باشید ، هنوز هم ممکن است در ابتدا این مسئله را چالش برانگیز بدانید که چگونه می توانید از شر کد های خود خلاص شوید. مثال حلقاه رو در نطر بگیرید که قبلا گفته شده بود در نطر می گیریم:که از vars استفاده کرده و یک سبک برنامه نویسی قدیمی میباشد.def printArgs(args: Array[String]): Unit = { var i = 0 while (i < args.length) { println(args(i)) i += 1 } }که با یک تغییر جزیی می توانیم به یه مدل تابعی تبدیل کنیمdef printArgs(args: Array[String]): Unit = { for (arg <args) println(arg) }ویا def printArgs(args: Array[String]): Unit = { args.foreach(println) }این مثال یک مزیت برنامه نویسی را با تعداد vars نشان می دهد.کد اصلاح شده (functional) واضح تر ، مختصر تر و دارای خطای کمتری نسبت به کد اصلی (معمولی) است. در حقیقت دلیل اینكه اسكالا یك سبك functional را تشویق می كند این است كه سبك functional به شما كمك می كند تا كدی ساده قابل فهم و خطای كمتری بنویسید.هرچند می توانید حتی فراتر بروید. دستور pringArgs را اگر refactored (بازبینی) کنیم این دستور صرفا functional نیست ، زیرا دارای عوارض جانبی(side effect) است - در این حالت ، اثر جانبی آن چاپ به جریان خروجی استاندارد است. علامت یک عملکرد با عوارض جانبی این است که نوع نتیجه آن Unit است. اگر یک تابع هیچ خروجی رو برنگرداند ،بدین معنی می باشد نوع خروجی از جنس Unit می باشد ، تنها راهی که این تابع می تواند تغییر ایجاد کند، استفاده از نوعی اثر جانبی است. یک روش کاربردی تر تعریف متدی است که آرگومان های منتقل شده را برای چاپ قالب بندی می کند ، اما فقط رشته فرمت شده را باز می گرداند ،تابع زیر را در نظر بگیریم :def formatArgs(args: Array[String]) = args.mkString(&quot\n&quot)یک تابع بدون side effect و بدون استفاده از متغیر vars، تابع mkString که شما می توانید انرا صدا کنید روی مجموعه تکرار ها (شامل arrays, lists, sets, و maps) که برمیگرداند یک String که شامل نتیجه صدا کردن تابع toString روی هر عنصر می باشد.که با یک جدا کننده این کار انجام می شود.بنابراین اگر ارگومان ورودی شامل عناصر "zero", "one", "two" باشد خروجی تابع formatArgs این خواهد بود "zero\none\ntwo" در حقیقت این تابع دستور print تابع printArgs که قبلا گفته شد را ندارد ، اما خیلی ساده می توانید انرا انجام بدهید.((println(formatArgs(args هر برنامه مفید به احتمال زیاد دارای عوارض جانبی به نوعی خواهد بود ، زیرا در غیر این صورت قادر به استفاده برای دنیای خارج نیست.ترجیح دادن روشها بدون side effect شما را به سمت طراحی برنامه هایی تشویق می کند که در آن کد اثر side effect به حداقل برسد.یکی از مزایای این روش این است که می تواند برنامه های شما را برای تست آسان تر ،کمک کند. به عنوان مثال ، برای تست هر یک از سه روش printArgs که در این بخش در ابتدا نشان داده شده است ، شما نیاز به تعریف مجدد println دارید ، خروجی منتقل شده به آن را ضبط کنید ، و مطمئن شوید که این همان چیزی است که انتظار دارید. در مقابل ، شما می توانید عملکرد formatArgs را به سادگی تست کنید.val res = formatArgs(Array(&quotzero&quot, &quotone&quot, &quottwo&quot)) assert(res == &quotzero\none\ntwo&quot)متد assert اسکالا متغیر Bollean را چک میکند اگر خروجی false باشد خطای AssertionError رو بر می گرداند و اگر خروجی true باشد چیزی را بر نمی گرداند.در مورد استثنا ها و خطا ها بعدا صحبت می کنیم.این رو در نظر داشته باشیم که هیج vars و side effect ای، ذاتا مشکل دار نیستند.اسکالا یک زبان تابعی صرف نیست که شما را مجبور کند که همه چیز را به سبک تابعی برنامه ریزی کنید.اسکالا یک زبان ترکیبی از تابعی(اعلانی) و سلسله مراتبی(دستوری) می باشد. شما ممکن است دریابید که در برخی شرایط یک سبک سلسله مراتبی(دستوری) مناسب تر برای حل مسئله است و در چنین مواقعی نباید از استفاده از آن دریغ کنید. ما به شما در یادگیری چگونگی برنامه نویسی بدون vars کمک خواهیم کرد ، با این حال ، ما نمونه های خاصی از کد ها را با vars و نحوه تبدیل آن را به شما بعدا خواهیم گفت.خواندن خطوط یک فایل اسکریپت هایی که کارهای کوچک و روزمره ای را انجام می دهند اغلب نیاز به پردازش خطوط در فایل ها دارند.در این مورد ، اسکریپتی خواهید ساخت ،که سطرها را از یک فایل می خواند و آنها را با تعداد کاراکترهای موجود در هر خط چاپ می کند.نحوه خواندن خطوط از یک فایل این اسکریپت با import کردن کلاسی با نام Source از package scala.io شروع کرده،سپس بررسی می شود که حداقل یک ارگومان ورودی داشته باشیم.اگر این چنین باشد ، اولین پارامتر را بعنوان نام فایل در نطر بگیریم و باز و پردازش کنیم.عبارت ((Source.fromFile(args(0 سعی میکند فایل را باز کند و یک object source برگرداند.,وقتی شما تابع getLines را صدا می کنید.تابع getLine یک [Iterator[String برمیگرداند.که یک خط برای هر تکرار را ، درست میکند.بدون اینکه دستور \n هر خط را برگرداند.برای عبارت iterators یکی طول خط و یک فضای خالی و مقدار دادهی اون خط را چاپ می کند.اگر وجود نداشته باشه ارگومانی در وردی در نهایت چاپ میکند که لطفا نام فایل رو وارد کنید.اگخ کد بالا را در یک فایل با نام countchars1.scala ذخیره کنیم و دوباره اجرا کنیم خواهیم داشت :$ scala countchars1.scala countchars1.scalaشما خروجی زیر درا خواهید داشت:22 import scala.io.Source 0 22 if (args.length > 0) { 0 51 for (line <Source. fromFile(args(0)).getLines()) 35 println(line.length +&quot &quot+ line) 1 } 4 else 46 Console.err.println(&quotPlease enter filename&quot)