شرح مفاهيم الترميز الثابت باستخدام مقارنات بسيطة من الحياة الواقعية

كيفية شرح مفاهيم الترميز مثل التدفقات والوعود والفحص والبرمجة التصريحية لطفل يبلغ من العمر 5 سنوات

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

تحديث: هذه المقالة هي الآن جزء من كتابي "The Professional Programmer" . اقرأ النسخة المحدثة من هذا المحتوى ، والمزيد من النصائح حول البرمجة على jscomplete.com/pro-programmer .

سأبدأ بمفاهيم بسيطة وانتقل إلى مفاهيم أصعب. لنبدأ بالترميز نفسه. يمكن مقارنة التشفير بكتابة وصفات الطبخ. الوصفة في هذا التشبيه هي البرنامج والطباخ هو الكمبيوتر. الوصفة هي قائمة من الإرشادات التي يجب على الطباخ اتباعها والبرنامج عبارة عن قائمة من الإرشادات التي يجب على الكمبيوتر تنفيذها.

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

حتى أن بعض الوصفات تحتوي على عبارات إذا: إذا طهي لمدة 2 أو 4 أو 8! تحتوي بعض الوصفات على حلقات: استمر في ضرب هذا المزيج حتى ...

يعجبني هذا القياس أيضًا بسبب جميع العناصر والأدوات الجاهزة التي يمكنك استخدامها في وصفاتك - مثل مزيج الكيك الذي يمكنك استخدامه لصنع الكب كيك والمقلاة ذات الشكل الخاص التي تجعل من السهل جدًا صنع الكب كيك.

يشبه استخدام العناصر والأدوات الجاهزة تضمين واستخدام حزمة من التعليمات البرمجية كتبها آخرون في التعليمات البرمجية الخاصة بك.

// The making of a cupcake// First steps:
$ npm install cake-mix$ npm install cupcake-pan

NPM هو مدير الحزم لـ Node.js ، وهو إطار عمل شائع جدًا لكتابة تطبيقات JavaScript. في هذا القياس ، يشبه Node.js المطبخ نفسه. يسمح لك بتنفيذ الخطوط في وصفاتك باستخدام وحدات مدمجة مثل الفرن والمغسلة.

عند الحديث عن الطعام غير الصحي ، فإن هذا القياس التالي هو لتعلم كيفية الترميز ومقارنته بعادات الأكل. أنا أحب هذا القياس بشكل خاص وما تنقله لأنه يساعدني على البقاء على المسار الصحيح في رحلة تعلم الشفرة الخاصة بي. بالنسبة لي ، بدأ هذا في المدرسة الثانوية وسيستمر حتى يصل عقلي إلى تعليماته الأخيرة: يموت () ؛

تعلم البرمجة

تعلم البرمجة يشبه محاولة إنقاص الوزن. ينطبق هذا القياس على تعلم أي شيء حقًا ، لكن تعلم البرمجة هو تطابق خاص هنا.

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

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

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

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

التمرين للجسد والعقل.

المتغيرات

تستخدم المتغيرات في برامج الكمبيوتر لحفظ البيانات . هذا بيان مبسط للغاية وهو خاطئ من خلال العديد من المقاييس.

المتغيرات لا تحتفظ بالبيانات. هم فقط يشيرون إليها. يتم الاحتفاظ بالبيانات في ذاكرة الكمبيوتر. يمكنك مقارنة المتغيرات بالتسميات التي تضعها على رسائل البريد الإلكتروني (أو الملاحظات ، أو الملفات).

تمت كتابة جميع نماذج التعليمات البرمجية في هذه المقالة بلغة JavaScript. JavaScript هو وسيلة سهلة لتعلم لغة الكمبيوتر.

في Gmail ، يعتبر التصنيف مؤشرًا إلى رسالة بريد إلكتروني أو قائمة رسائل بريد إلكتروني. يمكن أن تشير العديد من التصنيفات إلى نفس البريد الإلكتروني. هذا مشابه لتخصيص متغير آخر لمتغير موجود:

let work = [email1, email2, email3];let important = work;

أصبح كل من العمل والمهم الآن تسميات تشير إلى نفس قائمة رسائل البريد الإلكتروني بالضبط.

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

const sent = [];
// You cannot change the meaning of sent now// But you can add more values to it:
sent.push(new Email());

الأخطاء والاستثناءات

تتمحور خبرة المبرمج بشكل كبير حول كيفية التعامل مع الأخطاء. يحب المبرمجون الخبراء الأخطاء لأن الأخطاء بالنسبة لهم تعني التقدم.

في بعض الأحيان نتوقع رؤية هذه الرسائل الحمراء الرائعة وإذا لم نكن نعلم أن الرمز خاطئ ببساطة!

أحب عبارة " استمع إلى التعليمات البرمجية الخاصة بك" لأنني أعتقد أن الكود يتطور من خلال التواصل معنا باستخدام الأخطاء.

هذا بالضبط مثل تربية الأطفال.

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

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

تساعد معظم الأخطاء التي تحدث أثناء التطوير المبكر للبرامج على تحسين هذه البرامج بحيث لا تحدث الأخطاء أبدًا. هذه هي الطريقة التي يتم بها تربية الأطفال الجيدين. إنهم لا يكررون سوء التصرف لأن لديهم الآن منطقًا جيدًا يوجههم في اتجاه جيد.

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

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

// Hey kidsif (stranger.offersYou(chocolate)) { doNotAccept(); doNotTalkTo(stranger); walkAway();}
if (stranger.triesToForceYouToDoSomething()) { screamFor(help); runAway(); call(911);}

البرمجة التفاعلية والجداول

Reactive programming is a popular method for writing code that is based on reacting to changes. It is inspired by our everyday life and how we take actions and communicate with others. When performing everyday life activities, we try to multitask when we can but the brain cannot multitask no matter how hard we try. The only way we humans can multitask is to switch tasks and split them efficiently during their lifetime. This makes more sense when the tasks that we need to do require some amount of waiting, which is almost always the case. We actually always switch-tasks, even when we are not aware of it.

Reactive programming is simply to program using, and relying on, events instead of the order of lines in the code. Usually, this involves more than one event, and those events happen in a sequence over time. We call this sequence of events a “stream”.

Think of events as anything that might happen in the future. For example, you know that Jane (a store owner) is always tweeting interesting things on Twitter. Every time she tweets something we call that an “event”. If you look at Jane’s Twitter feed, you have a sequence of “events” happening over time (a stream of events). Reactive programming is named so because we get to “react” to those events. For example, imagine that you are waiting for Jane to tweet a promotional code about something cool she sells in her store. You want to “react” to that tweet and buy the cool thing using the promotional code. In a simplified picture, that is exactly what Reactive programming is all about.

To be able to react to an event, we have to be monitoring it. If we do not track the event, we will never know when to react to it. On Twitter, to monitor the events of Jane tweeting, we follow Jane and set our phone to notify us every time she tweets. When she does, we look at the tweet and make a decision on whether we need to further react to it or not.

In reactive programming, the process of monitoring an event is known as listening or subscribing to the event. This is, in fact, very similar to subscribing to a newsletter. When you subscribe to a newsletter on the Web, you supply your email address. Every time there is a new issue of the newsletter your email address will be used as the way for you to get a copy of the issue. Similarly, we subscribe to an event stream with a function. Every time there is a new event, the stream will use the function to enable our code to react to the event. In this analogy, the newsletter platform is the event stream. Every issue of the newsletter is an event and your email is the function you use to subscribe to the event stream.

Now imagine a dynamic newsletter that allows you to select topics and send you only the news items that match your topics. You are basically filtering the newsletter issues to your liking and that is something we can do on event streams as well. Also, imagine that you have subscribed to several newsletters using different email addresses. You later decided that you want all issues of the newsletters to be sent to a new single email address. One easy thing you can do is to set an email rule that forwards any issues from any newsletter to the new email address. You are basically merging multiple newsletter issues into one email address, which is another thing we can do with event streams.

Another way to think about event streams is to compare them to regular arrays. They are actually very similar. Arrays are a sequence of values in space while event streams are a sequence of values over time. In reactive programming, all the functional operations that we can do on an array. Filtering, reducing, mapping, combining, piping can all be done on event streams. We can filter an event stream, reduce the values of an event stream, map an event stream to another, combine streams, and make one stream an input to another. These are all options that yield new streams of values over time.

Callbacks and Promises

Imagine you ask someone to give you something that needs some time to be prepared. They take your order and your name and tell you to wait to be called when your order is ready. After a while, they call your name and give you what you asked for.

The name you originally gave them is the callback function here. They called it with the object that was requested.

This is like when you order a latte from Starbucks (in the store, not in the drive-thru). They synchronously record your order and name and then you wait until your name is called. When that happens, you receive your latte:

starbucks.makeMeALatte({ type: 'Vanilla', size: 'Grande' }, Samer);
// "Samer" here is the callback function.// When the Latte is ready, the barista will call Samer // with the ready object// We define a function Samer to process the ready object
function Samer(readyLatte) { // drink readyLatte}

Now imagine you ask someone to give you something, but they give you something else. Let’s call it a mystery object. They promise you that this mystery object might eventually turn into the thing you originally asked for.

This promise mystery object can turn into one of two possible forms. One form is associated with success and the other with failure.

This is like when we ask a chicken for a chick and the chicken gives us an egg. That egg might successfully turn into a chick or it might die and be useless.

const egg = chicken.makeChick(); // It's a promise!
egg.then(chick => raiseChick()) // Success outcome .catch(badEgg => throwBadEgg()) // Fail outcome

Queues and Stacks

When we work with elements of data, there are two popular data structures to store and use these elements: A LIFO Stack and a FIFO queue.

LIFO stands for Last In First Out and FIFO stands for First In First Out.

The simplest analogy of a data stack is the stack of dirty dishes in your sink. When you are done using a dish, you stack it on top of the existing dirty dishes until you are ready to wash them.

When you are ready to wash them, you take the last dirty dish that you stacked and you wash that. In computer terminologies, we say you “popped” a dish.

The last dish you stacked is the first dish you washed. This is LIFO.

The simplest analogy of a data queue is the line of people that forms in front of a checkout or order station. When you are ready to pay for your groceries and take them home, you might need to queue yourself in a line until it is your turn.

The first person to arrive at that queue will be the first person to be done with it. This is FIFO.

Pair Programming

You can drive your car on your own when you go to familiar places, but when it is time to go somewhere far for the first time you use a GPS. If you have someone else in the car with you, a better option would be to have them navigate by giving you the instructions on where to turn next. If you do not follow the instructions and end up taking a bad turn, they will let you know immediately and advise you on how to correct it.

Having a navigator next to you when you drive is like having a pair-programmer. You are not driving alone. You are a team with the same goal: to arrive at your destination safely, without any problems, and with the least amount of time and effort.

You can probably do it yourself without a human navigator or a fancy GPS by using the old-school way and checking a map before you leave. If needed, you can check the map again. If you check the map while driving, you might accidentally hit a curb or put a dent in the car. If you stop to check the map, you will be losing time. Without that pair navigator, you are not as safe and/or the journey will take a lot longer.

The experience of your pair navigator might also teach you new things. They might know of a new shortcut that you do not and one that is not on the map. You learn from their relevant experience, and this is beyond valuable.

If you need to go to two destinations and you have two cars. You might be tempted to think that it would be faster to drive solo and do the destinations in parallel. This might be faster in the short term, but all things considered, time might not be the most important factor here. When it comes to computer programs, using one car and making sure it is dent-free at the end of both journeys might be a far more important factor. This why we love pair programming.

Linting and Task Automation

If you have to drive alone on that long trip, you can still make your journey safer by relying on tools. A map is a tool. The GPS is a better tool. Cruise control is another tool.

Tools that automatically warn you if you do something wrong while driving are similar to linting tools for coding. In JavaScript, the best linting tool today is ESLint. It will warn you about so many wrong things you should not be doing while coding. Best of all, it can do that even before you run your program.

Examples of tools that warn you while you are driving are evolving in modern cars. Cars can now warn you when you cross a lane line unexpectedly, or when you try to turn or change a lane while not seeing that hidden car in your blind spot. Additionally, they warn you when you drive over the speed limit, or when you are about to hit something while trying to park in a tight spot.

Linting tools also evolve to provide more accurate and helpful warnings. ESlint always surprises me with very accurate warnings. Additionally, its default recommendations are getting better with each upgrade.

Another analogy that I love in modern cars is automation. Any task that you repeat often should be automated once its purpose and value are clear. Instead of restarting that program every time you save the file, have a monitor process that automates that. Rather than running a format command on your code before you share it with others, have a command that automatically does that every time you commit your code to source control.

Modern cars automate so many things as well. The obvious example here is adaptive cruise control, but other subtle examples include automatic windshield wipers and automatic high beams at night (my favorite!).

Imperative vs Declarative Programming

When you need to do something, there is always the what and the how aspects of it. What exactly needs to be done and how do we do it.

Imperative programming is about the how. Declarative programming is about the what.

What? How? And why should you care?

An imperative approach represents a list of steps. Do this first, then do that, and after that do something else. For example: Go over a list of numbers one by one and for every one add its value to a running sum.

A declarative approach represents what we have and what we need. For example: We have a list of numbers and we need the sum of those numbers. The imperative language is closer to the computers of today because they only know how to execute instructions. The declarative language is closer to how we think and command. Get it done, please. Somehow!

The good news is computer languages have evolved. Computer languages offer declarative ways to do the needed imperative computer instructions. Just as cars have evolved from manual stick shift into automatic and self-driving ones!

Imperative programming is like driving a stick shift car. You need to do manual steps (press the clutch, depress it slowly, change gears incrementally, etc). Declarative programming is like driving an automatic car — you just specify the “what”: Park or Drive.

You cannot program declaratively unless you have the tools that enable you to do so. While you can imperatively drive an automatic car (by switching to manual mode) you cannot declaratively drive a stick shift car. If all you have is a stick shift car, imperative programming is your only obvious choice. This is unless you take the time to install an automatic gear shifter, which might be worth it in the long term. If you can afford a new car, you will probably go with an automatic one unless you are that true nerd who still likes to program with Assembly!

Assemblyis the original true imperative low-level computer language with pure instructions that directly translate into machine code.

Note that imperative programming might produce faster programs. Additionally, declarative programming requires less effort from you. In general, it will also require less effort to be maintained. Coding does not have to be one way or the other. Any non-trivial computer program will most likely have a little bit of both approaches. Also, knowing how to code declaratively is great, but it does not mean that you do not need to learn the imperative ways as well. You should simply be confident using both.

الأدوات التي تمكنك من البرمجة بشكل تصريحي تتطور إلى طرق أفضل وأسرع لتوصلك إلى حيث تتجه. التجربة التصريحية النهائية مع السيارات الحديثة هي القيادة الذاتية. يصبح "ماذا" الوجهة وستقوم السيارة بالباقي. هذا هو على الأرجح مستقبل البرمجة أيضًا. سيكون لدينا برامج تتفهم جميع الأهداف ويمكنهم فقط عمل سحرهم لتوليد المنطق لتوصيلنا إلى تلك الأهداف.

ما هو القياس المفضل لديك؟ اسمحوا لي أن أعرف في قسم الردود أدناه.

شكرا للقراءة!