نحن نتقن لغات البرمجة الجديدة ، بالاعتماد على ما تم تعلمه بالفعل

مرحبا زملائي.



لقطة من جيني مارفين من Unsplash

قمنا اليوم بإعداد ترجمة لمقال عن أوجه التشابه الأساسية للعديد من لغات البرمجة باستخدام Ruby و C # كمثال. نأمل أن تساعد أفكار Severin Peres المحترمة العديد منكم على البدء بسرعة في تعلم لغة برمجة جديدة ، وستسير الأمور بمعنى وسرور حقيقيين.

ما لا يبتعد عن المبرمج - فهو لا يتوقف أبدًا عن التعلم. قد تكون لديك لغة مفضلة ، أو إطار عمل ، أو مكتبة ، ولكن ليس هناك شك في أنه لا يمكنك القيام بها حصريًا. قد ترغب في استخدام JavaScript ، ولكن المشروع الذي تعمل عليه حاليًا قد يتطلب Python. قد تكون بارعًا في Perl ، ولكن يمكن كتابة قاعدة التعليمات البرمجية في شركتك بلغة C ++. بالنسبة لمطور جديد ، قد تبدو فكرة تعلم لغة جديدة أمرًا شاقًا ، خاصة إذا كانت المواعيد النهائية قادمة. هذه أخبار سيئة. ومع ذلك ، هناك شيء جيد: تعلم لغة جديدة ليس بهذه الصعوبة عادة. إذا أخذنا كأساس النماذج العقلية الموجودة ، فسوف ترى أن تعلم لغة جديدة هو في الأساس امتداد للمعرفة الموجودة ، ولا يعمل من الصفر.

ما المشترك بينهما


تعتمد معظم لغات البرمجة بشكل أساسي على نفس مجموعة المبادئ الأساسية. يختلف التنفيذ ، ولكن لا توجد لغتان مختلفتان لدرجة أنه لن يكون من الممكن رسم أوجه شبه بينهما. من أجل تعلم وفهم لغة جديدة ، فإن أهم شيء هو تحديد كيف يبدو أنك تعرف بالفعل ، ثم اكتساب معرفة جديدة ، وتوسيع فهمك لها عند / إذا لزم الأمر. خذ بعين الاعتبار ، على سبيل المثال ، أنواع البيانات والمتغيرات. في كل لغة ، هناك طريقة لتحديد وتخزين البيانات - بشكل موحد في جميع أنحاء البرنامج. لذلك ، عند تعلم لغة جديدة ، تحتاج أولاً إلى فهم كيفية تعريف المتغيرات واستخدامها هنا. خذ لغتين مختلفتين كمثال: روبي مفسر مع الكتابة الديناميكية و C # المترجمة مع الكتابة الثابتة.

my_int = 8 my_decimal = 8.5 my_string = "electron" puts "My int is: #{my_int}" puts "My float is: #{my_decimal}" puts "My string is: #{my_string}" 

مثال مشابه:

 using System; public class Program { public static void Main() { int myInt = 8; double myDecimal = 8.5; string myString = "electron"; Console.WriteLine("My int is: {0}", myInt); Console.WriteLine("My float is: {0}", myDecimal); Console.WriteLine("My string is: {0}", myString); } } 

لنفترض أنك مطور روبي متمرس وتريد تعلم C #. فيما يلي مقتطفات من التعليمات البرمجية ، يمكنك في أحدها التعرف على Ruby بسهولة. هناك تحتاج فقط إلى تحديد العديد من المتغيرات وعرضها في وحدة التحكم. الآن انتبه إلى الجزء الثاني. هل تتعلم أي شيء؟ بناء الجملة مختلف ، ولكن ليس هناك شك في أن الرمز الثاني يعمل تمامًا مثل الرمز الأول. واجه عامل التشغيل = عدة مرات ، وهو على الأرجح ملفت للنظر كرمز لعمليات التعيين. ثم يتم استدعاء وحدة تحكم معينة. Console.WriteLine() ، مما يعني أنه سيتم عرض القيم في وحدة التحكم. هناك أيضًا بضعة أسطر هنا يبدو أنها تستخدم الاستيفاء لتكوين الرسائل. من الناحية النظرية ، لا يوجد شيء مثير للدهشة هنا - المهمة ، الاستيفاء ، الإخراج إلى وحدة التحكم ، كل هذه العمليات معروفة لك بالفعل في العمل مع Ruby.

قد تفهم الجزء الثاني من الكود دون أي معرفة بـ C # ، ولكن هناك بالتأكيد مجال لتوسيع نموذجك العقلي. على سبيل المثال ، لماذا يلف الجميع أنفسهم في طريقة Main() ؟ ما هذه الكلمات الأساسية int double string ؟ مع ذلك ، يبدأ التدريب. أنت تفهم بالفعل بشكل عام ما يحدث هنا (المهمة ، الاستيفاء ، الإخراج) ، حان الوقت الآن للانتقال إلى التفاصيل:

  • Main() : بعد التعمق أكثر في الموقف ، نجد أن طريقة Main() هي نقطة الدخول التي يبدأ منها البرنامج. الآن نعلم أنه في جميع برامج C # نحتاج إلى طريقة Main ().
  • المتغيرات: في الجزء الأول من الجزء الخاص بنا في C # ، يحدث بالتأكيد نوع من التخصيص. بالنظر إلى التسمية ، ربما تخمن أن الكلمة الأساسية تعني متغيرًا صحيحًا ، double هو رقم فاصلة عائمة مزدوج الدقة ، والسلسلة متغير سلسلة. على الفور تقريبًا ، تدرك أنه في C # ، على عكس روبي ، مطلوب كتابة ثابتة للمتغيرات ، حيث يتم تعريف المتغيرات بشكل مختلف لأنواع البيانات المختلفة. بعد قراءة الوثائق ، سوف تفهم مدى اختلافها.
  • Console.WriteLine() : أخيرًا ، عند تشغيل البرنامج ، سترى أن Console.WriteLine() يعرض القيم في وحدة التحكم. من Ruby ، ​​تعلم أن الوضع هو طريقة للكائن $stdout ، وإذا نظرت إلى وثائق Console.WriteLine() ، ستتعلم أن Console هي فئة من مساحة اسم System ، و WriteLine() هي الطريقة المحددة في هذا الفصل الدراسي. هذا لا يشبه إلى حد كبير الوضع فحسب ، بل يشير أيضًا إلى أن C # ، مثل روبي ، هي لغة موجهة للكائنات. هنا لديك نموذج عقلي آخر سيساعدك على تتبع أوجه الشبه الجديدة.

المثال أعلاه بسيط للغاية ، ولكن يمكن استخلاص عدد من الاستنتاجات المهمة منه. لقد تعلمت بالفعل أن برنامج C # يتطلب نقطة دخول محددة جيدًا ، وأن هذه اللغة مكتوبة بشكل ثابت (على عكس روبي) وموجهة نحو الكائن (مثل روبي). لقد اكتشفت ذلك ، لأنك تتخيل بالفعل ما هي المتغيرات والأساليب ، ثم قمت بتوسيع هذه النماذج العقلية ، وإثرائها بظاهرة التصنيف.

ابحث عن الاختلافات


البدء في تجربة كيفية قراءة التعليمات البرمجية وكتابتها بلغة جديدة ، أول شيء تحتاجه لمعرفة الأشياء المعروفة بالفعل والتي يمكن أن تكون بمثابة أساس للتعلم. بعد ذلك ، انتقل إلى الاختلافات. دعونا نعود إلى انتقالنا من روبي إلى C # ونلقي نظرة على شيء أكثر تعقيدًا.

 particles = ["electron", "proton", "neturon"] particles.push("muon") particles.push("photon") particles.each do |particle| puts particle end 

في جزء Ruby هذا ، نحدد مصفوفة تسمى particles ، والتي ستحتوي على عدة أسطر ، ثم نستخدم Array#push لإضافة بضعة أسطر أخرى إليها ، و Array#each لتكرارها عبر الصفيف وإخراج كل سطر فردي إلى وحدة التحكم. ولكن كيف تفعل الشيء نفسه في C #؟ القليل من googling ، اكتشفنا أن C # قد كتب صفائف (يجب ألا تفاجئك الكتابة بعد الآن ، مع مراعاة ما تعلمته سابقًا) ، وهناك أيضًا طريقة SetValue التي تشبه قليلاً push ، ولكنها تأخذ القيم والموضع في الفهرس كمعلمات. في هذه الحالة ، قد تؤدي المحاولة الأولى لإعادة كتابة كود Ruby في C # إلى ما يلي:

 using System; using System.Collections.Generic; public class Program { public static void Main() { string[] particles = new string[] { "electron", "proton", "neturon" }; particles.SetValue("muon", 3); //    ( 11):      particles.SetValue("photon", 4); foreach (string particle in particles) { Console.WriteLine(particle); } } } 

لسوء الحظ ، سوف يرمي هذا الرمز Run-time exception عند محاولة استخدام SetValue لإضافة قيمة جديدة إلى الصفيف. مرة أخرى ، ننظر إلى الوثائق ونكتشف أن المصفوفات في C # ليست ديناميكية ، ويجب أن تتم تهيئتها إما على الفور مع جميع القيم ، أو مع إشارة إلى الطول. مرة أخرى تحاول إعادة إنتاج كود روبي ، فكر في هذا واحصل على الخيار التالي:

 using System; using System.Collections.Generic; public class Program { public static void Main() { string[] particles = new string[] { "electron", "proton", "neturon", null, null }; particles.SetValue("muon", 3); particles.SetValue("photon", 4); foreach (string particle in particles) { Console.WriteLine(particle); } } } 

هذا المقتطف يعيد إنتاج جميع وظائف كود مصدر روبي ، ولكن مع امتداد كبير: فهو يعرض نفس القيم لوحدة التحكم. إذا قمت بفحص الجزأين بعناية ، فسيتم اكتشاف المشكلة بسرعة: في جزء في C # في مجموعة الجسيمات ، لا يمكن أن يكون هناك أكثر من 5 قيم ، بينما في جزء في روبي هناك أكبر عدد ممكن. ثم يصبح من الواضح أن المصفوفات في Ruby و C # مختلفة بشكل أساسي: الأول له حجم ديناميكي ، في حين أن الأخير لا. لإعادة إنتاج وظيفة مقتطف Ruby بشكل صحيح في C # ، تحتاج إلى المزيد من هذا الرمز:

 using System; using System.Collections.Generic; public class Program { public static void Main() { List<String> particles = new List<String>(); particles.Add("electron"); particles.Add("proton"); particles.Add("neutron"); particles.Add("muon"); particles.Add("photon"); foreach (string particle in particles) { Console.WriteLine(particle); } } } 

يستخدم هذا بنية بيانات List لجمع القيم ديناميكيًا. في هذه الحالة ، نقوم بالفعل بإعادة إنتاج رمز Ruby الأصلي في C # ، ولكن الأهم من ذلك ، يمكننا هنا تقدير الاختلاف الرئيسي بين اللغتين. على الرغم من استخدام المصطلح "صفيف" في كلتا اللغتين ، وقد يبدو أن هذه المصفوفات واحدة ومتشابهة ، إلا أنها في الواقع مختلفة تمامًا. إليك أمر آخر يساعد على توسيع النموذج العقلي ، لفهم ماهية "المصفوفة" بشكل أفضل وكيفية ترتيبها. في C # ، قد تكون المصفوفة كبنية بيانات مناسبة أو لا تكون مناسبة في المواقف التي قد تلجأ فيها إلى Ruby في المصفوفات ؛ نحن نتحدث عن المواقف التي يكون فيها تغيير الحجم الديناميكي لصفيف أمر بالغ الأهمية. الآن عليك الاهتمام بذلك مسبقًا والتفكير في التعليمات البرمجية وفقًا لذلك.

العودة إلى المبادئ الأساسية


من السهل جدًا بدء تعلم لغات جديدة ، واستكشاف أوجه التشابه والاختلاف بينها مقارنة باللغات المعروفة بالفعل ؛ ومع ذلك ، في بعض الحالات يكون من الأفضل البدء بمبادئ عالمية. أعلاه ، استنتجنا منطقيا أن C # كانت لغة موجهة للكائنات عندما عملنا مع فصل مدمج وإحدى طرقه ، System.Console.WriteLine() ، والتي قمنا بتنفيذ إجراء بشأنها. من المنطقي أن نفترض أنه في C # ، كما هو الحال في اللغات الأخرى الموجهة للكائنات ، هناك آلية لتحديد فئة ونسخ الكائنات منها. هذا هو المبدأ الأساسي للبرمجة الموجهة للكائنات ، بحيث لا يكون لديك أدنى شك حول صحة افتراضنا. أولاً ، دعونا نلقي نظرة على كيفية ظهور هذه العملية بلغة روبي المألوفة.

 class Element attr_accessor :name, :symbol, :number def initialize(name, symbol, number) self.name = name self.symbol = symbol self.number = number end def describe puts "#{self.name} (#{self.symbol}) has atomic number #{self.number}." end end hydrogen = Element.new("Hydrogen", "H", 1) hydrogen.describe 

هنا لدينا فئة Element بسيطة ، حيث توجد طريقة مُنشئ لقبول القيم وتعيينها للكائنات الفورية ، ومجموعة من طرق الوصول لتعيين القيم وتلقيها ، وأيضًا طريقة مثيل لإخراج هذه القيم. في هذه الحالة ، المفاهيم الرئيسية هي فكرة الطبقة ، وفكرة طريقة المنشئ ، وفكرة الحروف / المستوطنين ، وفكرة طريقة المثيل. بالعودة إلى أفكارنا حول ما يمكن فعله باللغات الموجهة للكائنات ، سنلقي نظرة على كيفية القيام بنفس الشيء في C #.

 using System; public class Program { public static void Main() { Element hydrogen = new Element("Hydrogen", "H", 1); hydrogen.Describe(); } public class Element { public string Name { get; set; } public string Symbol { get; set; } public int Number { get; set; } public Element(string name, string symbol, int number) { this.Name = name; this.Symbol = symbol; this.Number = number; } public void Describe() { Console.WriteLine ( "{0} ({1}) has atomic number {2}.", this.Name, this.Symbol, this.Number ); } } } 

بعد دراسة هذا الجزء في C # ، نرى أنه في الواقع لا يختلف كثيرًا عن إصدار Ruby. نحدد فئة ، باستخدام المُنشئ نحدد كيف ستقوم الفئة بنسخ الكائنات ، وتحديد الحروف / المستوطنين ، وتحديد طريقة المثيل التي سنستدعيها في الكائنات التي تم إنشاؤها. بطبيعة الحال ، يختلف الجزءان تمامًا في المظهر ، ولكن ليس بالطريقة غير المتوقعة. في إصدار C # ، نستخدم this للإشارة إلى الكائن الفوري ، بينما في Ruby نستخدم self لذلك. يتم كتابة نسخة C # على مستوى الطريقة وعلى مستوى المعلمة ، بينما في روبي ليست كذلك. ومع ذلك ، على مستوى المبادئ الرئيسية ، فإن كلا الجزأين متطابقان تقريبًا.

بتطوير هذا الموضوع ، يمكننا أن نفكر في فكرة الميراث. من المعروف أن الوراثة والفئات الفرعية هي النقاط الرئيسية للبرمجة الموجهة للكائنات ، لذلك من السهل أن نفهم أنه في C # يتم ذلك بنفس النجاح كما في Ruby.

 class Element attr_accessor :name, :symbol, :number def initialize(name, symbol, number) self.name = name self.symbol = symbol self.number = number end def describe puts "#{self.name} (#{self.symbol}) has atomic number #{self.number}." end end class NobleGas < Element attr_accessor :category, :type, :reactivity def initialize(name, symbol, number) super(name, symbol, number) self.category = "gas" self.type = "noble gas" self.reactivity = "low" end def describe puts "#{self.name} (#{self.symbol}; #{self.number}) is a #{self.category} " + "of type #{self.type}. It has #{self.reactivity} reactivity." end end argon = NobleGas.new("Argon", "Ar", 18) argon.describe 

في إصدار Ruby ، ​​نحدد فئة فرعية من NobleGas ترث من فئة Element بنا ؛ يستخدم مُنشئ الكلمة الأساسية الفائقة ، والتي توسع مُنشئ الفئة الأصل ثم تلغي طريقة describe الحالة لتعريف السلوك الجديد. يمكن فعل الشيء نفسه في C # ، ولكن باستخدام بنية مختلفة:

 using System; public class Program { public static void Main() { NobleGas argon = new NobleGas("Argon", "Ar", 18); argon.Describe(); } public class Element { public string Name { get; set; } public string Symbol { get; set; } public int Number { get; set; } public Element(string name, string symbol, int number) { this.Name = name; this.Symbol = symbol; this.Number = number; } public virtual void Describe() { Console.WriteLine ( "{0} ({1}) has atomic number {2}.", this.Name, this.Symbol, this.Number ); } } public class NobleGas : Element { public string Category { get; set; } public string Type { get; set; } public string Reactivity { get; set; } public NobleGas(string name, string symbol, int number) : base(name, symbol, number) { this.Category = "gas"; this.Type = "noble gas"; this.Reactivity = "low"; } public override void Describe() { Console.WriteLine ( "{0} ({1}; {2}) is a {3} of type {4}. It has {5} reactivity.", this.Name, this.Symbol, this.Number, this.Category, this.Type, this.Reactivity ); } } } 

للوهلة الأولى ، عندما لا نزال لا نعرف أي شيء عن C # ، قد تبدو هذه القائمة الأخيرة مخيفة. بناء الجملة غير مألوف ، بعض الكلمات والشفرات الغريبة غير منظمة كما اعتدنا. ومع ذلك ، إذا نظرنا إلى هذا الرمز من وجهة نظر المبادئ الأساسية ، فإن الفرق ليس مهمًا جدًا: هنا لدينا فقط تعريف فئة ، ومجموعة من الأساليب والمتغيرات وسلسلة من القواعد لنسخ واستخدام الكائنات.

TL ؛ د


يمكن أن يكون تعلم لغة جديدة أمرًا صعبًا للغاية إذا قمت بذلك من الصفر. ومع ذلك ، فإن معظم لغات البرمجة تعتمد على نفس المبادئ الأساسية ، والتي يسهل من خلالها رسم أوجه التشابه ، وملاحظة الاختلافات المهمة وتطبيقها في العديد من اللغات. بتجربة لغة جديدة على النماذج العقلية الموجودة ، يمكنك العثور على مكان لا يختلف فيه عن اللغات المدروسة بالفعل ، وحيث تحتاج حقًا إلى توضيح شيء ما. بدراسة المزيد والمزيد من اللغات بمرور الوقت ، تقوم بتوسيع نماذجك العقلية ، ومثل هذه التحسينات مطلوبة أقل وأقل - سوف تتعرف على عمليات التنفيذ المختلفة بلغات مختلفة من الورقة.

Source: https://habr.com/ru/post/ar422869/


All Articles