آموزش برنامه نویسی اسکالا (1)
هدف از نوشتن این مطلب این بود که می خواستم دوستانی که دوست دارن با این زبان برنامه بنویسند کمکی کرده باشم. این زبان یک زبان تابعی می باشد .که ساختار پروژه های آن نیز تابعی می باشد یعنی اینکه یک پروژه می تواند در داخل یک پروژه دیگر صدا زده بشود .
اولین قدم در اسکالا :
قبل از اینکه شروع کنیم به بحث عمیق اسکالا لازم هست اطلاعاتی در مورد این زبان بدانیم و مثالهایی در این مورد انجام بدهیم.اولین کار نصب استاندارد اسکالا می باشد برای نصب باید به ادرس زیر مراجعه کنید http://www.scalalang.org/downloads و دانلود مربوط به پلت فرمی که لازم دارید را انجام بدهید و بعد از دانلود نصب بکنید می توانید از pluging های مربوط به eclipse ویا intellije استفاده کنید.
نحوه استفاده از scala Interpreter :
آسانترین راه کار کردن با اسکالا استفاده از scala interpreter می باشد.که بصورت پوسته "shell" برای نوشتن عبارات و برنامه ها اسکالا می باشد.نوع ساده ای از عبارات داخل مترجم هست و آن عبارت را ارزیابی کند و مقدار نتیجه را چاپ می کند . در محیط shell اسکالا خیلی ساده اسکالا را صدا میزند. و شما با نوشتن اسکالا در محیط terminal /command line از آن استفاده می کنید . در یک فرصت مناسب در مورد نحوه نصب آن در محیط ویندوز/لینوکس و مک در آینده ای نزدیک می نویسیم. برای شروع یک terminal باز کنید
➜ ~ scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_80).
Type in expressions for evaluation. Or try :help.
scala>
بعد از این شما عبارات خود را می نویسید مثل ۱+۲ و بعد فشار می دهید enter
scala> 1+2
res0: Int = 3
این خط شامل این موارد می باشد:
- بصورت خودکار تولید میشود یا نامی که توسط کاربر تعریف میشود برای مراجعه به مقدار محاسبه شده که به معنی نتیجه صفر است : res0
- یک کولن (:) و بدنبال آن نوع عبارت (Int)
- معادل نشانه یا علامت (=)
- مقدار نتیجه از ارزیابی عبارت
نوع Int نام کلاسی هست که در کلاس Int در package اسکالا می باشد.package ها در اسکالا شبیه package در جاوا هستند.آنها namespace ها رو جدا می کنند و یک مکانیزم برای پنهان کردن اطلاعات فراهم میکنند . مقادیر کلاس Int متناظر است با مقادیر Int کلاس جاوا. خیلی عمومی تر همه انواع type primative کلاسهای متناظری در package اسکالا دارند. برای مثال scala.Boolean متناظرش هست در جاوا boolean. یا scala.Float متناظرش در جاوا هست float و وقتی ما کد اسکالای خودمان را کمپایل می کنیم به byte code جاوا تبدیل میشود.اسکالا موقع کمپایل تا جایی که امکان دارد از primitive types های جاوا استفاده میکند تا جایی که ممکن هست بتواند کارایی بهتری رو types primitive می دهد.
شناسه resX در خط های بعدی ممکن هست استفاده بشود . برای نمونه ، از آنجا که res0 قبلا بود 3 , درنتیجه res0 * 3 نتیجه اش 9 خواهد بود.
scala> res0*3
res1: Int = 9
برای پرینت !Hello, world لازم است فقط در جلوی خط فرمان این دستور نوشته شود.
scala> println("Hello, world!")
Hello, world!
تابع پرینت string را برای خروجی استاندارد می نویسد و این شبیه System.out.print در جاوا می باشد .
دومین قدم :
تعریف کردن تعدادی متغییر :
اسکالا دو نوع متغییر vals و vars دارد . یک val است شبیه به یک متغیر final در جاوا . و فقط یک بار مقداردهی اولیه می شود. یک متغیر val نمی تواند هرگز مقدار دهی دوباره انجام شود. اما یک متغیر var برعکس اش می باشد . آن شبیه یک متغییر non final جاوا می باشد. یک متغییر var می توند در طور چرخه حیاتش دوباره مقدار دهی بشود . اینجا یک متغییر val تعریف شده است.
scala> val msg = "Hello, world!"
msg: String = Hello, world
این جمله نام msg را برای رشته "!Hello, world" تعریف میکند . نوع msg از نوع java.lang.String می باشد , زیرا رشته ها در اسکالا با کلاس String جاوا پیاده سازی شده اند . اگر شما از تعریف کردن متغییر در جاوا استفاده میکنید , شما متوجه یک اختلاف برجسته در اینجا خواهید شد.
نه java.lang.String و نه String در هیچ جایی از متغییر val ظاهر نمی شود. این مثال شرح میدهد type infrence یا نوع ادراکی . اسکالا این قابلیت را دارد که بتواند نوع متغییر هایی که شما استفاده می کنید را بدست بیاورد .در این مورد، شما msg را با یک رشته حرفی مقداردهی کرده اید . اسکالا نوع msg رو به String استنتاج میکند . وقتی مترجم اسکالا میتواند نوع را استنتاج کند . آن خیلی بهتر است که اجازه بدهیم اینکار را آن انجام بدهد تا اینکه با کلی کد تعریف کنیم که می توانیم لازم نداشته باشیم . حاشیه نویسی نوع صریح. شمای میتوانید حاشیه نویسی نوع صریح را مشخص کنید اگر دوست داشته باشید. احتمالا باید بعضی وقتها اینکار را انجام بدهید.حاشیه نویسی نوع صریح هردو می توانند اطمینان حاصل کنند که مترجم اسکالا type مورد نظر شما را استنتاج می کند.خوب مستندات مفید برای کسانی که بعدا کد را می خوانند بهتر خواهد بود.برخلاف جاوا شما نوع متغییر را قبل از نامش مشخص می کنید اما در اسکالا شما بعد از نام متغییر نوع اش را مشخص می کنید.و با یک : جدا می کنید.برای مثال :
scala> val msg2 : java.lang.String ="Hello again,world"
msg2: String = Hello again,world
شما می توانید نوع اش را مشخص کنید در مثال زیر نیز می توانید ببینید
برنامه ها به سادگی :
scala> val msg3: String = "Hello yet again, world!"
msg3: String = Hello yet again, world!
برمیگردیم به msg اصلی . حالا که آن متغییر تعریف شده است. شما میتوانید از آن استفاده کنید . به خروجی زیر نگاهی بیندازیم:
scala> println(msg)
Hello,world
حالا سوال این است که چه کاری را شما نمی توانید با msg انجام بدهید. میدانید که آن یک متغیر val میباشد نه یک var. پس بنابراین شما نمی توانید آن را دوباره مقداردهی کنید. برای مثال ببینید چطور مترجم اشکال میگیرد وقتی شما تلاش می کنید این کار رو انجام بدهید.
scala> msg = "Goodbye cruel world!"
<console>:12: error: reassignment to val
msg = "Goodbye cruel world!"
^
اگر شما میخواهید مقداردهی دوباره انجام بدهید.شما احتیاج دارید به استفاده از متغیر از جنس var و در اینصورت :
scala> var greeting = "Hello, world!"
greeting: String = Hello, world!
از انجا که greeting یک متغییر var هست نه val، بنابراین شما می توانید هر زمان لازم داشتید آن را مقدار دهی کنید.برای مثال شما میتوانید مقدار متغییر greeting را تغییر بدهید .
scala> greeting = "Leave me alone, world!"
greeting: String = Leave me alone, world!
برای وارد کردن دستوراتی به مترجم که شامل چندین خط می باشد فقط لازم هست | را در اولین خط و خط بعد قرار بدهید . اگر کد شما در یک خط کامل نیست. مترجم پاسخ میدهد . با یک enter | روی خط بعدی به این صورت استفاده میکنیم
scala> val multiLine =
| "This is the next line."
multiLine: String = This is the next line.
اگر متوجه باشید اشتباهی تایپ کرده اید ، اما مترجم هنوز منتظر ورودی بیشتر است ، می توانید با فشار دادن enter دو بار می توانید این مسئله رو حل کنید.
scala> var opps =
|
|
You typed two blank lines. Starting a new command.
در ادامه سعی می کنیم موقع نوشتن مثالهایی از این دست از pipeline(|) کمتر استفاده کنیم.
تعریف برخی توابع
حالا که شما دارید با متغیر های اسکالا کار می کنید. شما احتمالا بخواهید برخی توابع را بنویسید . خوب ببینیم اینجا شما چطور اینکاررا در اسکالا انجام میدهید:
scala> def max(x:Int , y:Int): Int ={
| if (x>y) x
| else y
| }
max: (x: Int, y: Int) Int
تعریف توابع با یک def شروع میشود . نام تابع در این مورد max است.و بدنبالش , لیستی از پارامترهای در داخل پارانتز ها قرار دارد.و هر پارامتر با با یک نوع annotation دنبال میشود با هر پارامتری که با یک : از هم جدا میشدند.زیرا در کامپایلر اسکالا ( و مترجم از این به بعد ما میگیم کامپایلر) نوع پارامترهای استنتاج نمیشود.در این مثال تابع max دوتا پارامتر x,y که هردو Int هستند را میگیرد .بعد از بستن پارانتز یک نوع دیگر نیز در اینجا “ : Int ” نوعی از annotation است که تعریف نوع نتیجه از تابع max می باشد. توابع result Type هست معادل برابری یا {} که بدنه تابع رو مشخص میکند . در این مورد در بدنه یک عبارت تک شرطی میباشد. که مشخص می کنید x یا y کدام یک بزرگتر هست را بر می گرداند. و در نتیجه نتیجه تابع max مشخص میشود. در اسکالا عبارت میتواند نتیجه را در یک مقدار بدهد و مشابه عملگرهای سه تایی جاوا کار می کند. برای مثال , در اسکالا عبارت “ if (x > y) x else y ” شبیه عبارت “ x > y) ? x : y) ” در جاوا رفتار می کند. علامت برابر در اینجا اشاره میکند به بدنه داخل {} که ما در توابع به انها اشاره میکنیم.یک تابع تعریف شده عبارت که نتیجه اش در یک مقدار هست.ساختار پایه ای یک تابع در شکل زیر توضیح داده شده است.
بعضی وقتها مترجم اسکالا از شما میخواهد نوع خروجی تابع را مشخص کنید. اگر تابع recursive باشد.برای مثال شما خیلی واضح نوع خروجی را مشخص می کنید . در مورد max هرچند ، شما ممکن نوع خروجی را ندهید و اجازه بدهید خود مفسر نوع خروجی را استنتاج کند . همچنین ، اگر یک تابع فقط شامل یک جمله باشد.شما میتوانید اکولاد ها را انتخاب کنید و حذف کنید . بنابراین . شما می توانید تابع max را مشابه زیر تعریف کنید :
scala> def max2(x: Int, y: Int) = if (x > y) x else y
max2: (x: Int,y: Int)Int
بعد از اینکه یک تابع را تعریف کردین شما می توانید انرا صدا بزنید بصورت زیر :
scala> max(3, 5)
res4: Int = 5
در اینجا است ما می توانیم یک تابع که هیچ پارمتری را نمی گیرد.و نتیجه ای برنمی گرداند. را تعریف کنیم مشابه نمونه زیر :
scala> def greet() = println("Hello, world!")
و نحوه برخورد مفسر با این نوع توابع :
greet: Unit()
وقتش ما تابع greet را تعریف می کنیم . مترجم برای تابع greet این پاسخ را می دهد "()greet:Unit “
یک توضیح اینکه ، در نام تابع ، () پارنتزها نشان میدهد که هیچ پارامتری را نمی گیرد و نوع خروجی تابع greet ، از نوع Unit می باشد.
نوع خروجی Unit نشان می دهد که تابع مقدار بر نمی گرداند. اسکالا یک نوع type دارد به نام Unit مشابه void درجاوا می باشد ، که هیچ مقداری برنمی گرداند. و در حقیقت هر متدی در جاوا از نوع void می باشد در اسکالا به Unit نگاشت می شود . که در اسکالا این نوع خروجی به متدهای Unit معرف است .بنابراین هدفشان اجرای برنامه و در نهایت یک خروجی برای ما می باشد . در مورد تابع ()greet هدف این بود که برای ما خروجی تابع را چاپ کند . در قدم بعدی. شما میخواهید کدهای اسکالا در یک فایل قرار بدهید و بصورت یک فایل اسکریپت انرا اجرا کنید . اگر شما بخواهید از مترجم خارج بشوید شما می توانید quit: یا q: را در جلوی مفسر بنویسید و خارج بشوید مشابه زیر :
scala> :quit
$
قدم چهارم : نوشتن تعدادی اسکریپت در اسکالا
اگرچه اسکالا برای کمک به برنامه نویسان برای ساختن سیستم هایی با مقیاس بزرگ طراحی شده است.و همچنین با مقیاس خوبی به نگارش در امده است.مفهوم یک اسکریپت فقط یک ترتیبی از جملات در یک فایل که بصورت ترتیبی شروع به اجرا میکند می باشد .خوب حالا میگذاریم ، کد زیر را داخل یک فایل با نام hello.scala ذخیره می کنیم .
// Say hello to the first argument
println("Hello, world, from a script!")
و بعد اجرا میکنیم.
$ scala hello.scala
و شما باید این را در خروجی ببینید:
Hello, world, from a script!
ارگومانهای خط فرمان (Command line args) در اسکریپت های اسکالا فعال می باشند و یک ارایه ای در اسکالا ایجاد می کنند. و بعنوان ارگومان تعریف میشوند. دراسکالا ارایه ها از ۰ شروع میشوند.و شما برای دسترسی به عناصر ارایه از ایندکس داخل پارانتز استفاده میکنید.بنابراین اولین عنصر در یک ارایه اسکالا step نامیده میشود که بصورت (steps(0 عناصر بدست می اید بصورت جاوایی مشابه این [step[0 بدست نمی اید. حالا سعی کینیم ببینیم چگونه می توانیم پارامتر را در خروجی ببینیم . کد زیر را در داخل یک فایل با نام helloarg.scala قرار میدهیم.
// Say hello to the first argument
println("Hello, "+ args(0) +"!")
و سپس اجرا میکنیم:
$ scala helloarg.scala planet
در این مثال planet بعنوان یک ارگومان بنام (0)args به عنوان ورودی سیستم در مقابل نام فایل به سیستم ارسال می گردد. بنابراین شما باید ببینید :
Hello, planet!
توجه کنیم که این اسکریپت شامل یک comment است.کامپایلر اسکالا کاراکترهای بین // و کاراکترهاییی که بین /* ---- */ قرار دارد را نایدیه می گیرد. این مثال نشان میدهد با یک داده از نوع String شروع میکنه و با یک + خودش رو به یک متغییر وصل می کند . همانطور که شما انتظار داشتید. عبارت "!Hello, "+"world" که نتیجه اش است "!Hello, world" یعنی اینکه عملگر + عمل ادغام رو انجام میدهد.
قدم بعدی : حلقه با while و شروط با if
برای اینکه ببینیم دستور while چگونه کار می کند کد زیر را در فایلی با نام printargs.scala ذخیره می کنیم.
var i = 0
while (i < args.length) {
println(args(i))
i += 1
}
نکته:
ما در اینجا مشخص نمی کنیم بهترین روش کد اسکالا در مورد while loops کدام است .اما در بحث های آینده از رویکرد اجتناب کردن در مورد فرایند تکرار در ارایه ها و ایندکسها صحبت خواهیم کرد.
حالا یک توضیح در مورد این برنامه :
از ابتدا ، این اسکریپت با یک متغیر تعریف میشود var i=0. نوعی داده را خود سیستم استنتاج می کند و نوع متغییر را از نوع scala.Int در نظر می گیرد . زیرا متغییر با مقدار صفر مقدار دهی اولیه شده است. ساختار while در خط بعدی ساختار block دارد.یعنی بین دوتا {} . و این تکرار میشود تا زمانیکه عبارت زیر false با شد .
"i < args.length "
تابع args.length به ما تعداد ارگومانهای ارسالی به سیستم را بصورت ارایه ای از ارگومانها هستند را اعلام می کند.داخل بلاک ما دوتا جمله اریم که هر کدام دو تا فضای خالی به جلو هستند.که این یک استایل در اسکالا می باشد.(indentation) اولین جمله چاپ ی کنه ارگومان ارسلی به برنامه رو در کنسول و دومین جمله i را یه دونه افزایش میدهد +i توجه داشته باشیم که اسکالا همانند جاوا increment نمی کند در جاوا شما برای اینکه incrment انجام بدین از ++i , i++ استفاده می کنید که این دستورات در اسکالا کار نمی کنند.شما در اسکالا از دو روش می توانید این کار را انجام بدهید i=i+1 یا i+=1 ، خوب حالا به اجرا کردن برنامه بر می گردیم :
$ scala printargs.scala Scala is fun
و ما باید این خروجی رو ببینیم :
Scala
is
fun
برای اینکه بتوانیم دیتای خیلی بیشتری رو از وردی بخوانیم می توانیم در یک فایل با نام echoargs.scala ، کد زیر را ذخیره کنیم :
var i = 0
while (i < args.length) {
if (i != 0)
print(" ")
print(args(i))
i += 1
}
println()
حالا ما این کد رو توضیح میدهیم : ما در این مثال از println برای نوشتن استفاده نکردیم بلکه از دستور print برای نوشتن استفاده کردیم برای اینکه بتوانیم ورئی ها بصورت یک سطر در خروجی داشته باشیم به ازای هر ارگومان یک فاصله چاپ می کنیم و بعئ مقدار داه شده رو در سیستم می نویسیم. حالا ما اگر بخواهیم این فایل رو اجرا کنیم :
$ scala echoargs.scala Scala is even more fun
نتیجه داده به این صورت خواهد بود :
Scala is even more fun
توجه داشته باشید که شما در اسکالا شبیه جاوا از عبارت boolean داخل پارانتز در حلقه و یا شرط (while/if)استفاده می کنید.ما در اسکالا نمی تونیم مثل زبان روبی بدون پارانتز کار کنیم if i<10 در اسکالا کار نمی کند.یک مورد دیگه که یک جمله شرطی داشته باشیم در جاوا در یک بلاک قرار می دیم اما در اسکالا می تویم بدون بلاک اینکار رو انجام بدیم.و اینکه در جاوا انتهای هر جمله رو با ; می بندیم اما در اسکالا می تونیم استفاده نکنیم.شما می وانید با یک مدل دیگه ایکار رو انجام بدین :
var i = 0;
while (i < args.length) {
if (i != 0) {
print(" ");
}
print(args(i));
i += 1;
}
println();
تکرار با foreach و for
شما شاید به این قضیه پی نبرده باشین ، اما شما در مثال قبلی از روش Imperative programming (برنامه نویسی دستوری) استفاده کردید. این همان روشی هست که شما با زبان جاوا یا c یا c++ انجام میدین.وقتی شما یک دستوری را در داخل حلقه تکرار مدهید این دستور می تواند یک متغیر عمومی را در کل تابع تغییر بدهد.اسکالا شما رو در برنامه نویسی دستوری کمک می کند .هرچقدر اسکالا را بهتر بشناسید ، به احتمال زیاد اغلب خود را با سبکی کاربردی تر برنامه نویسی خواهید کرد.یکی از اهداف اصلی در اینجا این است که شما خیلی راحت بتوانبد سبک برنامه نویسی functional با همان مدل برنامه نویسی (Imperative ) انجام بدهید.و یکی از کاراکترای اصلی زبانهای برنامه نویسی functional این است که خود توابع که می نویسید می تواند بصورت یه ساختار باشد و در اسکالا نیز این موضوع کاملا صحیح می باشد .با مثال دیگر این موضوع را بررسی می کنیم :یک مقدار دقیق تر به این موضوع نگاه کنیم برای چاپ ارگومانهای خط فرمان از این دستور استفاده می کنیم :
args.foreach(arg => println(arg))
در این مثال شما متد foreach رو برای ارگومانها صدا می زنید و مقدار ارگومان رو به یک تابع دیگه پاس می کتید . در این حالت ، شما مقدار ارگومان را به یک فانکش(function literal) پاس می کنید.بدنه خود فانکشن (println(arg میباشد.اگه ما کد بالا در یک فایل بنام pa.scala ذخیره کنیم و اجرا کنیم :
یک توضیح در مورد function literal این است که یک فانکشن به عنوان ورودی یک فانکشن دیگر استفاده می شود .
$ scala pa.scala Concise is nice
نتیجه که خواهیم داشت :
Concise
is
nice
در مثال بالا مفسر اسکالا نوع خروجی به نوع ارگومان (arg) که از نوع String بود refer کرد.از آنجا که String نوع ناصر آرایه که شما در foreach استفاده می کنید.اگر شما ترجیح بدین که خودتون نوعش رو مشخص کنید می توانید بصورت زیر در داخل پرانتز اینکار را انجام بدهید همانند مدل زیر :
args.foreach((arg: String) => println(arg))
اگر شما اینو رو اجرا کنید همانند قبلی رفتار خواهد کرد.اگر شما دوست دارید خیلی کمتر بنویسید به جار اینکه خیلی بیشتر توضیح بدهید . یکی از مزیت های اسکالا استفاده از کوتاه نویسی هست.اگر یک function literal فقط شامل یک جمله باشد.و یک پارمتر، نیازی ندارید برای آن یک نام متد و نوع پارامتر برای آن تعریف کنید.بنابراین می توانیم کد بالا بصورت زیر بنویسیم :
args.foreach(println)
خلاصه ، نحوه نوشتن یک تابع function literal بصورت لیستی از نام متغیر ها و یک فلش راست و در نهایت بدنه تابع می باشد.این شرح یک function literal هست.با این نکته شما شاید تعجب کنید چه اتفاقی برای اون حلقه هایی که شما در زبان جاوا و یا c استفاده می کردین ، افتاده است.سعی می کنیم راهنمایی کنم شما رو در function direction ، بنام توابع وابسته که بصورت یک عبارت در اسکالا صدا زده می شود.که ما اینها را بصورت مفصل در آینده صحبت خواهیم کرد. یه نگاه اجمالی بکنیم و یک فایل جدید بانام forargs.scala می سازیم :
for (arg <- args)
println(arg)
پارانتز بعد از "for" شامل arg <- args می باشد. که سیمبل "->" اشاره می کند به ارایه آرگومان ها. سمت راست این سیمبل اشاره می کند به متغیر arg که یک val می باشد.چون شما فقط می نویسید این متغیر رو و اون است یک val.از آنجا که arg ممکنه یک var به نظر برسه زیرا آن هر سری در داخل حلقه می تونه یه مقدار جدید برای خودش بگیرد.و به نطر میرسه باید var باشه، اما واقعیت این است که آن val هست ، بخاطر اینکه نمی توانید در داخل حلقه مقدار arg رو تغییر بدهید. در عوض ، برای هر عنصر از آرایه args ، مقدار جدیدی از جنس val باری متغییر arg ایجاد می شود و مقدار اولیه آن را مقدار دهی می کند ، و در داخل حلقهfor آنرا را اجرا می کند. اگر شما اسکریپت forargs.scala را با فرمان زیر اجرا کنید :
$ scala forargs.scala for arg in args
شما باید این مقادیر را در خروجی خواهید دید:
for
arg
in
args
اسکالا برای عبارات خیلی بهتر عمل می کنه،که برای شروع این مثال خوب می باشد ، که بعدها در این مورد بطور مفصل صحبت خواهیم کرد.
ارایه ها با انواع پارامتر ها
شما در اسکالا می توانید یک نمونه از یک object و یا یک کلاس را با یک new تعریف کنید.وقتی شما یک نمونه از یک object را ساختید می تونید با انواع مقادیر و پارمترها parameterize کنید. معنی patametrize در اینجا یعنی پیکربندی کردن وقتی که می خواهید انرا ایجاد کنید.شما پارمترایط می کنید یه نمونه از یک object را با مقادیر که پاس می کنیم به یک سازنده در با پارانتز مشخص شده است . برای مثال در کد اسکالای زیر ما یک نمونه از یک نوع java.math.BigInteger با new ایجاد کردیم و با مقدار 12345 مقدار دهی کردیم :
val big = new java.math.BigInteger("12345")
یه مدل پارامتریک کردن داده از یک نمونه با انواع مختلف داده استفاده از براکت می باشد که نوع داده در داخل براکت مشخص میشود.به مثال زیر دقت کنیم می بینیم :
val greetStrings = new Array[String](3)
در این مثال greetStrings یک مقدار از [Array[String یعنی "Array of String" که مقدار دهی اولیه شده با طول 3 و پارامترایز شده با مقدار 3 .به کد زیر نگاه کنیم :
val greetStrings = new Array[String](3)
greetStrings(0) = "Hello"
greetStrings(1) = ", "
greetStrings(2) = "world!\n"
for (i <- 0 to 2)
print(greetStrings(i))
اگه می خواستید خودتون نوع پارامتر رو مشخص کنید می توانستید اینکار رو انجام بدهید:
val greetStrings: Array[String] = new Array[String](3)
با توجه به استنباط خود اسکالا ، این خط کد معادل همان خط کد اول می باشد.اما این نمونه نشان میده یه قسمت نوع داده از پارمتر در داخل براکت و طول داده در داخل پارانتز قرار دارد.نوع متغییر greetString از نوع [Array[String می باشد نه از جنس (Array[String](3.در مثال بالا هر عنصر از ارایه greetString مقدار دهی اولیه شده است.
greetStrings(0) = "Hello"
greetStrings(1) = ", "
greetStrings(2) = "world!\n"
همانطور که قبلا گفته شد ، ارایه ها در اسکالا به عناصر خودش از طریق پارانتزها دسترسی پیدا می کنند اما در جاوا از طریق براکت این اتفاق می افتد.بنابراین عنصر صفرم بدینگونه میشود (greetStrings(0 ونه به این صورت [greetStrings[0 که در جاوا عناصر را بدست می آوردیم. این سه خط کد، مفهوم مهمی را برای درک در مورد Scala در مورد معنی Val نشان می دهد.وقتی شما یه متغییر از جنس val تعریف می کنید ، دیگر نمیتوان مقدار دهی دوباره کرد.اما object ای که به آن اشاره دارد می تواند هنوز هم تغییر کند.بنابراین در این حالت ، شما نمی توانید مجدد greetStrings را به مجموعه دیگری تغییر دهید. greetStrings همیشه به همان نمونه Array [String] که با آن آغاز شده است اشاره می کند. اما می توانید عناصر آن Array [String] را با گذشت زمان تغییر دهید ، بنابراین خود آرایه قابل تغییر است.در مثال بالا دوتا خط که ، به هر کدام از عناصر ارایه greetString اشاره میکند.
for (i <- 0 to 2)
print(greetStrings(i))
خط اول کد یک دیگر از قوانین کلی در اسمالا رو بیان میکند.اگر متد شما فقط یک پارمتر داشته باشد شما میتوانید بدون . و یا پرانتز انرا صدا بزنید.در این مثال واقعا متد یک پارامتر از جنس Int دارد.کد 0 تا 2 به فرخوانی متد تبدیل میشود بدین صورت (0).(to(2. توجه داشته باشید این نحوه فراخوانی زمانیکه خیلی واضح اعلام میکنید این دستور کار میکند.شما نمی توانید “println 10” را بنویسید.اما می توانید این دستور را اجرا کنید “Console println 10”.اسکالا از نظر فنی overloading ندارد بخاطر اینکه هیچ سنسی از عملگر را ندارد.بجار کاراکترهایی از نوع + - * / از نام عملگر ها استفاده میکند.بنابرانی وقتی شما تایپ می کنید 1+2 را در کنسول اسکالا ،شما در واقع از متدی بنام + استفاده میکنید روی object 1 از جنس Int و استفاده میکنید از 2 بعنوان پارامتر ورودی ان.شما در عوض می توانید 1+2 را با روش سنتی فراخوانی بکنیدو نحوه فرخوانی ان بصوزت .(2)+.(1) می باشد.ایده مهم دیگری که با این مثال نشان داده شده است به شما بینش می بخشد.به همین دلیل به آرایه ها با کمک پرانتز در Scala دسترسی پیدا می کنید.Scala موارد خاص کمتری نسبت به جاوا دارد.آرایه ها بصورت نمونه کلاسهای ساده ای هستند شبیه بقیه کلاسها تعریف شده در اسکالا می باشند.وقتی شما پارانتزها را اطراف مقادیر متغییر اعمال می کنید .Scala کد را به فراخوانی روشی به نام applay بر روی آن متغیر اعمال می کند.بنابراین (greetStrings(i تبدیل به (greetStrings.apply(i می شود. بنابراین دسترسی به عناصر یک ارایه خیلی ساده است شبیه صدا کرد یک متد دیگر می باشد.این اصل فقط محدود به آرایه ها نمی باشد.هر درخواستی از یک object که از تعدادی آرگومان که با پارانتز مشخص شده است تبدیل به صدا کردن متد apply میشود . البته این تنها در صورتی کامپایل می شود که آن نوع از شیء در واقع یک متد apply را تعریف کند. بنابراین این کار بصورت موردی نیست بلکه بصورت یک قانون کلی میباشد.
وقتی یک مقداری را به یک متغیر که با پارانتز مشخص شده قرار میدهند .کامپایلر آنرا تبدیل به متد update میکند.و مقدار ان را در سمت راست قرار میدهد.برای مثال : "greetStrings(0) = "Hello تبدیل میشود به این مقدار ("greetStrings.update(0, "Hello ، بنابراین موارد زیر از نظر معنایی با این کد برابری میکند :
val greetStrings = new Array[String](3)
greetStrings.update(0, "Hello")
greetStrings.update(1, ", ")
greetStrings.update(2, "world!\n")
for (i <-0.to(2))
print(greetStrings.apply(i))
اسکالا یک مفهوم ساده ای از رفتار هر چیزی را ارانه میدهد. از آرایه ها تا عبارات و از objectها با متدهایشان.
شما لازم نیست موارد خاص را بخاطر بسپارید مثل primitive و انواع دادههای دیگر و یا مثل آرایه ها و یا regular objects.ugh, علاوه بر این این یکنواختی هزینه قابل توجه ای در سرعت و کاایی سیستم ایجاد نمی کند.کامپایلر اسکالا از آرایه های جاوا و انواع primitive types و native arithmetic در هر جاییکه امکان استفاده در کد باشد ، استفاده میکند.اگرچه مثالهایی که شما تا اینجا دیدید خوب ، کامپایل و اجرا شده اند.اسکالا راههای ساده تر و دقیقتری را برای ایجاد و مقدار دهی اولیه ارایه ها که بصورت معمولی استفاده میکنیم ایجاد کرده است.در شکل زیر خواهید دید، این کد یه آرایه با طول 3 ایجاد میکند .که با مقادیر Stringمقداردهی شده "zero", "one", و "two".کامپایلر استنباط می کند نوع داده ها از جنس [Array[String می باشد.پراکه داه ای شما به ان اختصاص دادهاید از جنس String می باشد.
چه اتفاقی واقعا در شکل 3.2 می افتد.یک متد factory ،بنام apply صدا زده میشود، که ایجاد میکند و یک آرایه جدید ایجاد می کند. این متد apply تعداد متغییرها را می گیرد. و روی companion object ، Array را تعریف می کند. که در جلوتر در رابطه با companion objects صحبت مفصل خواهیم داشت.اگر یک برنامه نویس جاوا هستید ، می توانید به این روش فکر کنید یک متد static بنام apply ، در کلاس Array فراخوانی کنید. یک روش شفاف تر برای فراخوانی همان متد apply عبارت است از:
val numNames2 = Array.apply("zero", "one", "two")
استفاده از لیستها
یکی از ایده های بزرگ برنامه نویسی functional این است که روشها نباید تاثیر گذار باشن.یعنی اینکه یک متد فقط باید یک کار انجام بدهد و یک مقدار خروجی برگرداند.برخی از مزایایی که هنگام استفاده از این متد ها به دست می آورید اینست که متدها کمتر درگیر می شوند و بنابراین قابل اطمینان تر و قابل استفاده مجدد هستند.یکی دیگر از مزایای آن ، این است که همه چیزهایی که وارد و خارج از یک روش می شوند توسط یک نوع چک بررسی می شود ، بنابراین خطاهای منطقی احتمالاً خود را به عنوان خطاهای نوع جلوه می دهند. استفاده از این فلسفه کاربردی در دنیای اشیاء به معنای تغییرپذیری اشیاء است.همانطور که مشاهده کردیدیک array در اسکالا شامل یک مجموعه از دادهای پشت سر هم که از یک نوع می باشد.یک [Array[String فقط شامل کاراکتر می باشد.برای مثال ، اگرچه شما نمی توانید طول یک آرایه را بعد از آنکه تعریف کردید تغییر دهید ، اما می توانید مقادیر عنصر آن را تغییر دهید. بنابراین ، آرایه ها اشیاء قابل تغییر هستند.(mutable) ، اما برای دادهای پشت سرهم که غیر قابل تغییر باشد می توانیم از کلاس List اسکالا استفاده نماییم.همانند آرایه ها یک [List[String فقط شامل کاراکترها می باشد.scala.List با لیستی که در جاوا تعریف می شود java.util.List متفاوت میباشد . لیستی که در اسکالا تعریف میشود غیر قابل تغییر می باشد.اما هر جایی که شما از لیست جاوایی استفاده کنید قابل تغییر می باشد.بطور کلی لیست اسکالا کلا برای بزنامه نویسی functional طراحی شده است. خیلی ساده میتونیم یک لیست رو ایجاد کنیم.شکل زیر رو ببینیم: هم ایجاد شده و هم مقدار دهی شده است
در کد بالا یک مقدار جدید برای متغییر oneTwoThree مقدار دهی اولیه میکند که از جنس [List[Int و عناصر آن اعداد صحیح می باشد.از آنجا که لیست ها تغییر ناپذیر هستند ، آنها کمی شبیه به رشته های جاوا رفتار می کنند: وقتی شما متدی را در لیست فراخوانی می کنید که ممکن است نام آن به نظر برسد این لیست تغییر می یابد ، در صورتیکه لیستی جدید با مقدار جدید ایجاد می کند.برای مثال List یک متدی که کار اضافه کردن را به عهده دارد، ":::"، حالا نحوه استفاده کردن آز ان را در زیر بیان می کنیم:
val oneTwo = List(1, 2)
val threeFour = List(3, 4)
val oneTwoThreeFour = oneTwo ::: threeFour
println(oneTwo +" and "+ threeFour +" were not mutated.")
println("Thus, "+ oneTwoThreeFour +" is a new list.")
اگه ما اسکریپت بالا رو اجرا کنیم خواهیم داشت:
List(1, 2) and List(3, 4) این لیستها غیر قابل تغییر هستند
List(1, 2, 3, 4) و این لیست یک لیست جدید می باشد
شاید متداولترین عملگری که در لیست ها استفاده شود "::" باشد.که به معنی “cons” می باشد . cons وظیفه اش اضافه کردن یک عنصر به ابتدای یک لیست که موجود هست می باشد . به اسکریپت زیر توجه کنیم :
val twoThree = List(2, 3)
val oneTwoThree = 1 :: twoThree
println(oneTwoThree)
اگر اسکریپت بالا اجرا کنیم به این میرسیم :
List(1, 2, 3)
توجه داشت باشیم در عبارت 1:: twoThree عملگر :: یک متد عملگر راست هست .در لیست twoThree ممکن است شما شک کنید که بعضی چیزهای غیر مرتبط با متد :: ، اما این یک قانون ساده است.اگر یک متد استفاده بشود در یک عملگری شبیه به این در مجموعه ها : a*b ، متد این را یک عملگر چپ استفاده می کند.و به این شکل می باشد (a.*(b می باشد، مگر اینکه نام متد انتهایش با "." باشد .اگر یک "." در انتهای یک متد باشد آنیک عملوند راست می باشد.بنابراین در twoThree::1 ، "::" متد twoThree رو به عنوان یک عملوند سمت راست می پذیرد.و عدد 1 را به ان اضافه میکند به این صورت (twoThree.::(1، ارتباطات عملگرها رو بصورت مفصل در نوشته های بعدی خواهم گفت.باید توجه داشته باشیم یک راه کوتاه برای اینکهیک لیست خالی هسا استفاده از Nil می باشد.یک راه دیگر مقدار دهی لیستها با ترکیب عناصر لیست با عملگر "cons" می باشد که آخرین عنصر لیست را Nil قرار دهیم. برای مثال اسکریپت زیر همان خروجی را برای ما تولید می کند “(List(1, 2, 3”
val oneTwoThree = 1 :: 2 :: 3 :: Nil
println(oneTwoThree)
لیست ها در اسکالا بصورت متد های کاربری package بندی شده اند.بسیاری از انها را در زیر می آوریم و بعد ها در بخش های بعدی از قدرت لیست ها خواهم گفت،
سوال اینجاست که ما چرا append نداریم ؟
چرا کلاس list عملگر "append" را با استفاده از :+ ارائه می دهد.در این مورد ما شرح خواهیم داد بعدا، اما از این عملگر ما بندرت استفاده میکنیم. زیرا زمان لازم برای افزودن به یک لیست به طور خطی با اندازه آن لیست رشد می کند.در حالیکه ما از "::" متد استفاده میکنیم زمان آن ثابت می باشد.انتخاب شما برای انکه بخواهید یکلیست کاربردی بسازید با استفاده از append کردن بعد از اینکه انجام شد اگر بخواهید از دستور reverse و یا از دستور Listbuffer استفاده نمایید ، شما یک لیست قابل تغییر خواهید داشت.وقتی که از دستور toList استفاده بکنید. در مورد ListBuffer نیز در آینده صحبت خواهیم کرد.
یک تعداد از متدهای لیست رو در اینجا اشاره میکنیم
مطلبی دیگر از این انتشارات
آموزش اصولی کد نویسی تمیز - بخش دوم
مطلبی دیگر از این انتشارات
کامیتهای مخزن گیت خود را امضا کنید.
مطلبی دیگر از این انتشارات
آموزش اصولی کد نویسی تمیز - بخش اول