الجمعة، 19 أغسطس 2011

سكالا … الدرس الأول


ما هي سكالا (Scala) ؟

Scala مشتقة من Scalable , ومعناها أن اللغة قادرة على التوسع و النمو بسبب بساطة ال syntax الأولى الخاص بها وذلك لأنها مصممة بشكل جيد جداً على قواعد قليلة , و لكنها تسمحُ بتمدد اللغة و خلق أدوات جديدة لغرض خاص لم تكن موجودة فى اللغة من قبل (أمثلة على ذلك لاحقاً) .
Scala أيضاً تستطيع التعامل مع كود java بكفاءة شديدة فتستطيع استيراد java libraries و ترث من java classes وهكذا .. وهي تعتمد عل java virtual machine (JVM)فى عملها لذلك فهى تكاد تضاهي لغة الجافا فى السرعة.
تتميز لغة سكالا (Scala) بالعديد من الملامح المميزة لها و التي سيتم شرحها بالتفصيل مثل: (Type Safe – Object-Oriented – Functional Language – …).
مصمم لغة سكالا (Scala) ومطور Generic Java هو مارتن أودرسكي Martin Odersky (باحث في معهد EPFL – متخصص في تصميم لغات البرمجة كما أنه مدير مشروع مدير مشروع LAMP!!)
البرمجة فن قبل أن تكون علم !!
عبارة دائماً ما كنت أقرأها و أكتبها على صفحات المدونات و المنتديات و لكني لم أشعر بمعناها الحقيقي بهذا القدر إلا عندما بدأت دراسة لغة سكالا (Scala) والتعمق في المصطلحات البرمجية الفلسفية و البحث عن أصل الأفكار و الثوابت … و لم كانت في الأصل ثوابت ؟!
إذا كنت لا تفضل البداية بالتعمق في بعض المصطلحات و المفاهيم, أو كنت في اشتياق الي بداية كتابة الكود… يمكنك تخطي هذة الفقرة :) !!
دعنا ننعش الذاكرة بمناقشة بعض المفاهيم العلمية الخاصة بالبرمجة و التي نادراً ما تجد أحد من المطورين الجدد و أحياناً المحترفين ليسوا على دراية كافية بها :
1- Static Types و Dynamic Types :

Dynamic Type Static Type
التعريف في حالة ال Dynamic Type يتم التعرف تلقائيا على نوع القيمة أو المتغير وقت التنفيذ و ذلك عن طريق نوع القيمة المسندة الي المتغير لحظة استخدامه او لحظة اسناد القيمة. في حالة اللغات البرمجية ال Static Type يتم تعريف القيم و المتغيرات في وقت كتابه الكود من قبل المبرمج و يتم تلقين المفسر – Compiler نوع القيمة المخزنة في المتغيرات.
المميزات - حجم كود أقل و بالتالي سرعة أكبر في كتابة الكود
- قدر أكبر من الامان في الكود – Type Safety
- سرعة في التنفيذ.
العيوب
- أقل بكثير من حيث Type Safety
- بطئ التنفيذ بعض الشئ نتيجة للجهد المبذول في استنتاج نوع القيم وقت التنفيذ.
-حجم كبير للكود !!
4- Mutable و Immutable :

Immutable Mutable
التعريف تطلق على أنواع المتغيرات التي لا يمكن تغيير قيمتها لاحقا.بمجرد تمرير قيمة للمتغير … لا يمكن تغير قيمته في أي جزء من الكود. تطلق على أنواع المتغيرات التي يمكن تغيير قيمتها في أي جزء من أجزاء الكود.
5- Statement و Expression :

Expression Statement
التعريف هي التعليمات البرمجية التي تنتج قيمة أو تعادل قيمة منطقية.مثلX > 5فهي ترجع قيمة منطقية إماTrue OR False و هي التعليمات البرمجية التي لا ينتج عنها قيمة أو لا تعادل قيمة بحد ذاتهامثل For Statement , If Statement.في لغات مثل Java, C++, C# .
إذا كنت قد قرأت الفقرة السابقة… أعتقد انه يدور في بالك أحد هذة التعليقات الثلاثة:
  1. “جميل جدا !”
  2. “ما علينا… كمل :s”
  3. “كلام فارغ !!”
في الحالات الثلاثة أعدك بأن تكون سكالا – Scala في الفقرة القادمة… أكثر تشويقا !!

سكالا (Scala)

أعتقد ان لغة البرمجة سكالا (Scala) قد وجدت إستجابة للكثير من شكاوى المبرمجين على وجه الكرة الأرضية :), فهي كما سنرى تجمع العديد من المزايا المختلفة التي كان يعتقد مسبقاً أنها لا تجتمع في أداه برمجية واحدة.
يمكننا الآن الدخول مباشرة في كتابة بعض الكود والبدء بعبارة :
1
2
Scala> println("Hello, world!")
Hello, world!
بالرغم انه سطر واحد إلا انه يوجد العديد من الاسئلة :
  1. أين يتم كتابة هذا الكود؟
  2. هل سكالا (Scala) لغة compiled أم interpreted ؟
أخبرتكم من قبل أن سكالا (Scala) تجمع العديد من المزايا المدهشة التي لا تجتمع في أي لغة تقليدية أخرى!!.
سكالا (Scala) لغة تقبل العمل بنظام Compiled كما انه يمكن استخدامها في إطار Interpreted.
لذلك يمكنك كتابة هذا الكود في ملف منفصل أو يمكنك معالجة هذا الامر من خلال سطر الاوامر ، و سيتم استخدام سطر الاوامر في شرح هذا الدرس.
اذا بعد ما انتهينا من المثال التاريخي Hello World دعنا نتعمق أكثر و أكثر!!
ولكن أولاً قبل التعمق لنجرب حيلة صغيرة و هي إستخدام سطر الاوامر كآلة حاسبة… يمكنك كتابة :
1
2
Scala> 1+2
res0: Int =3
الحقيقة انا لم اذكر هذا الامر بغرض إستخدام الآلة الحاسبة… فأنت لن تتعلم لغة برمجة لإجراء حاصل جمع رقمين لكن هناك ملحوظة أود ان أوضحها وهي:
1
res0: int = 3
يقوم سطر الاوامر بحجز متغير و تعيين له اسم –res0- و يضع به ناتج الامر السابق ، اي انه يمكنك إستخدام الناتج الخاص بالعملية السابقة بالشكل التالي:
1
2
Scala> res0 * 3
res1: Int =9

المتغيرات

إذا كنت صاحب خلفية برمجية فأنت معتاد على مفهوم المتغيرات و التي سوف نراجع طريقة التعامل معها في سكالا (Scala) .
أما ان كانت هذة أولى الاسطر البرمجية لك فموضوع المتغيرات يمكن التعبير عنه ببساطة أنها أماكن في الذاكرة ينسب لها أسماء معينة حتى يسهل التعامل معها أو مع القيم المخزنة بها و تختلف فيما بينها حسب نوعها و طبيعتها كما سنرى لاحقا.
يمكنك الإعلان عن متغير معين بالشكل التالي:
1
Scala> val msg = "Hello, world!"
في هذا السطر تم الإعلان عن متغير اسمة “msg” عن طريق الامر val و تم وضع القيمة “Hello world” به
لقد تكلمنا سابقا عن ان سكالا – Scala تتبع نظام Static Types في معالجة المتغيرات و لكننا لم نعلن نوع المتغير – راجع تعريف المصطلحات –
من أهم مزايا سكالا – Scala هو نظام Type Inference: وهو نظام مسؤل عن استنتاج نوع المتغير تلقائيا وقت كتابة الكود و يتم تعيين نوعه وقت التصميم و ليس أثناء التنفيذ ، وبذلك تكون سكالا – Scala قد جمعت بين تقليل حجم الكود و ال Type Safety .!
وبالرغم من وجود نظام Type Inference إلا ان سكالا – Scala لم تحرمنا من امكانية الاعلان عن المتغيرات بشكل مباشر و صريح حيث يمكنك الإعلان عن المتغير بالشكل التالي:
1
2
Scala> val msg3: String = "Hello yet again, world!"
msg3: String = Hello yet again, world!
حيث String هو نوع المتغير
الان يمكننا تجربة شئ آخر مع المتغيرات…
ما رأيك بالسطر التالي
1
2
Scala> var msg3: String = "Hello yet again, world!"
msg3: String = Hello yet again, world!
هل تلاحظ اين الاختلاف … نعم انه var بدلا من val … و لكنها نفس النتيجة!! ما الفائدة إذا !!؟
تحدثنا سابقا عن مصطلحين Mutable , Immutable !!
تستخدم val لتعريف متغيرات من نوع immutable و التي لا يمكن نغييرها بعد وضع قيمة بها!
اما var فهي للأنواع ال Mutable و التي يمكنك تغيرها لاحقا !!
مثال :
1
2
3
4
5
6
7
8
9
10
11
12
Scala> val msg: String = "Hello yet again, world!"
msg: String = Hello yet again, world!
 
Scala> msg = "Goodbye cruel world!"
6: error: reassignment to val
msg = "Goodbye cruel
 
Scala> var greeting = "Hello, world!"
greeting: java.lang.String = Hello, world!
 
Scala> greeting = "Leave me alone, world!"
greeting: java.lang.String = Leave me alone, world!

الدوال Functions

كيفية التصريح بدالة how to declare a function ؟
فلنتأمل هذا المثال:
1
2
3
Scala> def max(x:Int, y:Int):Int = {
    | if(x > y)return x else return y }
max: (Int,Int)Int
عند طلب الدالة Function call :
1
2
Scala> max(4,5)
res0: Int = 5
ملاحظات :-
  1. لو لا تقوم بارجاع قيمة اقدر احذف = و تسمي unit مش دالة
  2. كتابة نوع ال return value ليس ضروري يعني ممكن لا يكتب و يفهمه ال type inference
  3. كتابة نوع ال parameters ضروري .
  4. يمكن الاستغناء عن كلمة return
  5. يمكن الاستغناء عن {} فى حالة اذا كان جسم الدالة سطر واحد
بالتالى يمكن كتابة الدالة كالاتي:
1
Scala> def max(x:Int, y:Int)=if(x > y) x else y
6. اذا كانت الدالة لا تاخذ parameters يمكن حذف () الفارغين اثناء الطلبcall وبالتالى تقدر تعاملها كـمتغير
1
2
3
4
5
6
7
8
Scala> def teet()=5
teet: ()Int
 
Scala> teet
res3: Int = 5
 
Scala> teet()
res4: Int = 5
7. اذا كانت الدالة يمكن ان ترجع اكثر من نوع من القيم حسب احتمالات مختلفة يقوم ال type inference بجعل نوع القيمة ِAnyVal حتي يسمح باي قيمة
مثال:
1
2
Scala> def func(x:Int)=if(x > 0)1 else 0.1
func: (Int)AnyVal
لانه يمكن ان ترجع Int أو double .
8. طريقة اخري للتصريح بدالة Literal function
هذه الطريقة تسمح بالتصريح بدالة بدون اسم (ع الطاير) و سيكون لها استخدامات كثيرا مستقبلا فى higher order functions
1
2
Scala> (x:Int) => x+x
res9: (Int) => Int =

اللاجمل

تعودنا فى لغات البرمجة على عنوان كبير فى اي كتاب ألا و هو الجمل او Statements و تشمل if conditions و loops مثل for و while
لكن هنا فى سكالا يوجد اختلاف فى المعاملة فسكالا تعامل تقريبا كل شئ كتعبير expression وليس كجملة statement
لذلك علينا أولا معرفة ما الفرق بينهما ؟؟
  • الجملة: هي كل ما يفعل شئ و ليس له قيمة فى حد ذاته مثل conditions التى تحدد ما تقوم به بناء على شروط سابقة و loops التي تكرر ما تفعله لمده معينة محددة بشروط ايضا
  • التعبير: هو كل ماله نتيجة اي يمكن ان يوضع على الجانب الايمن لعلامة = (assign operator) اي يمكن تخزين قيمته فى متغير
في سكالا كل شئ تعبير يمكن ان يرجع قيمه
مثال:
1
2
Scala> val x=if(5 >6)11 else 12
x: Int = 12
فى هذا المثال تم تعيين القيمة x لناتج الجملة/التعبير if

LOOPS

مثال على while loop تطبع ال arguments التي تكتب بجانب اسم البرنامج فى سطر الاوامر:
1
2
3
4
5
Scala> var i = 0
while (i < args.length) {
println(args(i))
i += 1
}
نلاحظ هنا loop عادية جدا ولكن كما قلنا سابقا ان في functional programming يفضل عدم استخدام المتغيرات و loops فما الحل ?
  • هذا حل مبدأي تخلصنا فيه من المتغير و لكن بقيت loop
1
Scala> for (arg println(arg)
  • هذا الحل النهائي يخلصنا من المتغير و ال loop بأن نستخدم داله foreach تاخذ argument عبارة عن دالة اخري تاخذ كل عنصر من عناصر args على حده كـ argument وتطبق عليه println
1
Scala> args.foreach(arg => println(arg))
  • و يمكن اختصارها ايضا هكذا :
1
Scala> args.foreach(println)
حيث ان دالة foreach لا ترجع الا قيمة واحدة و هى عناصر args .

المجموعات Collections

اولا الصفوف Arrays :-
  • التصريح بصف جديد:
1
2
Scala> val GreetStrings = new Array[String](3)
GreetStrings: Array[String] = Array(null, null, null)
  • طريقة اخري للتصريح بصف :
1
2
Scala> val x=Array("ahmed","mohamed")
x: Array[String]= Array(ahmed, mohamed)
  • استرجاع قيمه معينة وفي هذه الحالة null :
1
2
Scala> GreetStrings(0)
res0: String = null
ملاحظة مهمة جدا : الحقيقة ان الـ syntax السابق هو اختصار لـ
1
2
Scala> GreetStrings.apply(0)
res3: String = null
وهذا يعني ان عملية استرجاع قيمة من صف ما هي الا دالة function ولكن يمكن حذف اسمها تبعا لقواعد الـ syntax الخاص بسكالا و مثل تلك القواعد البسيطة هو ما يعطي لسكالا قدرتها على التوسع و التشكل.
  • تعيين قيمة فى صف :
1
Scala> GreetStrings(0)="Hello"
كالعاده سيكون اختصار لداله و هي :
1
Scala> GreetStrings.update(0,"Hello)
مثال اخر على قدرة سكالا على التشكل:
1
2
3
4
Scala> for (i <- 0 to 2) println(GreetStrings(i))
Hello
null
null
هذا الـ genetartor هو ايضا اختصار لدالة و هي :
1
2
Scala> 0.to(2)
res7: Range.Inclusive = Range(0, 1, 2)
القاعدة: هي ان فى سكالا عند طلب دالة call a function يمكن حذف النقطة و استبداله بمسافة space .
و يمكن ايضا حذف الاقواس () اذا كان لها parameter واحد فقط و استبدالهم بمسافة
ثانيا : القوائم Lists :
  • التصريح بقائمة :
1
2
Scala> val x=List(1,2,3,4)
x: List[Int] = List(1, 2, 3, 4)
  • الفرق بين الصف و القائمة :
  1. الصف mutable و القائمة immutable .
  2. الصف عدد عناصره ثابت اما فى القائمة فعدد العناصر قابل للزيادة.
  • الاضافة للقائمة :
فلنتأمل الامثلة التالية
1
2
3
4
5
6
7
8
9
10
11
Scala> val onetwo=List(1,2)
onetwo: List[Int] = List(1, 2)
 
Scala> val threefour=List(3,4)
threefour: List[Int] = List(3, 4)
 
Scala> onetwo :: threefour
res22: List[Any] = List(List(1, 2), 3, 4)
 
Scala> onetwo ::: threefour
res23: List[Int] = List(1, 2, 3, 4)
سنلاحظ الفرق بين :: و :::
- :: بياخذ قبله اي نوع و بعده قائمة و يضع ما فبله كـ عنصر اول فى القائمة و لذلك يمكن ان يستخدم فى التصريح بقائمة عن طريق اضافة عنصر لقائمة فارغة.
1
2
Scala> 1::List
res24: List[Int] = List(1)
أو يمكن استخدام Nil لتعبير عن قائمة فارغة
1
2
Scala> 1::Nil
res27: List[Int] = List(1)
- ::: يأخذ قبله قائمة و بعده قائمة و يقوم باضافتهم لبعضهم البعض concatenation
ملاحظة هامة جدا :
نلاحظ هنا ان :: و ::: ما هم الا دوال و اننا نستخدم نفس القاعدة الخاصة باستبدال النقط و الاقواس بمسافات مع قاعدة اخري صغيرة ألا و هي : -
  • اذا انتهي اسم الدالة بـ : تُنفذ من اليمين لليسار بمعني اخر فى حالة
1
Scala> 1::Nil
تعتبر Nil هى parameter الاولى للدالة و 1 هو ال parameter الثانية.
بعض الدوال الخاصة بالقوائم :
1
2
Scala> val x=List(1,2,3,4)
x: List[Int] = List(1, 2, 3, 4)
سنلاحظ هنا اننا نقوم بعمل دالة ع الطاير تحوي اختبار معين كـ argument لدالة تانية لتقوم بتنفيذ هذا الاختبار بطريقة معينة
تطبق هذا الاختبار علي كل عناصر القائمة و اذا كان موجود ترجع true
1
2
Scala> x.exists(s => s==3)
res32: Boolean = true
ترجع قائمة بها العناصر التى تنطبق مع الاختبار فقط
1
2
Scala> x.filter(s=> s>3
res33: List[Int] = List(4)
تقوم بتنفيذ مهمة ما على كل عناصر القائمة
1
2
3
4
5
6
7
8
Scala> x.map(s=> s*2)
res34: List[Int] = List(2, 4, 6, 8)
 
Scala> x.isEmpty
res36: Boolean = false
 
Scala> x.length
res37: Int = 4
تحويل القائمة لـ string و وضع بين عناصرها الفاصل المطلوب و فى هذه الحالة مجرد مسافة
1
2
Scala> x.mkString
res39: String = 1 2 3 4
ترجع اول عنصر فى القائمة
1
2
Scala> x.head
res40: Int = 1
ترجع باقى عناصر فى القائمة بدون العنصر الاول
1
2
Scala> x.tail
res41: List[Int] = List(2, 3, 4)
ترجع اخر عنصر فى القائمة
1
2
Scala> x.last
res43: Int = 4
ترجع true فقط اذا كان الاختبار true على كل عناصر القائمة
1
2
3
4
5
Scala> x.forall(s=> s>3)
res42: Boolean = false
 
Scala> x.reverse
res44: List[Int] = List(4, 3, 2, 1)
تحذف اول عنصرين فى القائمة
1
2
Scala> x.drop(2)
res47: List[Int] = List(3, 4)
تحذف اخر عنصرين فى القائمة
1
2
Scala> x.dropRight(2)
res48: List[Int] = List(1, 2)
ثالثا : Tuples
تشبه القوائم و لكنها تختلف فى انها يمكن ان تحتوي على أنواع مختلفة من القيم على عكس القوائم التى تحتوي على نوع واحد فقط ولها حد اقصى من حيث عدد العناصر و هو 22
  • التصريح بـ Tupule
1
2
Scala> val pair=(99,"EgyptAir)
pair: (Int, java.lang.String) = (99,EgyptAir)
  • استرجاع قيمة
1
2
3
4
5
Scala> pair._1
res49: Int = 99
 
scala> pair._2
res50: java.lang.String = egypt
رابعا : المجموعات Sets :
هي عبارة عن قائمة ذات عناصر فريدة أي لا تسمح بالتكرار
  • التصريح بها:
1
2
Scala> val test=Set(1,1,3,4,5,6,6)
test: scala.collection.immutable.Set[Int] = Set(5, 3, 1, 6, 4)
  • هي ايضا immutable و لكن يمكن جعلها mutable عن طريق تنفيذ هذا الأمر قبل التصريح بها
1
2
3
4
Scala> import scala.collection.mutable.Set
import scala.collection.mutable.Set
Scala> val test=Set(1,1,3,4,5,6,6)
test: scala.collection.mutable.Set[Int] = Set(5, 3, 1, 6, 4)
خامسا : Maps :
  • التصريح به :
1
2
Scala> val x=Map("name"->"ahmed", "age"->55)
x: scala.collection.immutable.Map1 = Map(name -> ahmed, age -> 55)
  • استرجاع القيمة :
1
2
Scala> x("name")
res51: Any = ahmed

خريطة سكالا


و هكذا نصل الي نهاية اليوم الأول و أرجو أن أكون قد وُفِقت فى نقل معلومة جديدة و بشكل جديد

ليست هناك تعليقات:

إرسال تعليق