> ## Content Index
> Fetch the complete content index at: https://arabroot.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# المتغيرات Variables والشروط والحلقات Loops والدوال Functions
- URL: https://arabroot.io/articles/اساسيات-البرمجة-المتغيرات-والشروط-والحلقات-والدوال/
- Published: 2026-10-30T22:44:48.000Z
- Updated: 2026-10-30T23:44:19.000Z
- Description: نبني برنامجاً صغيراً لمصاريف الشهر بلغتي Python وJavaScript جنباً إلى جنب، ونتعلم المتغيرات والأنواع والشروط والحلقات والدوال، ثم نقرأ أشهر أخطاء المبتدئين كما تظهر فعلاً.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, أساسيات الحاسب والبرمجة

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

وهذه الأدوات هي موضوع هذا المقال: المتغيرات Variables، والأنواع Types، والشروط Conditions، والحلقات Loops، والدوال Functions. وسوف نبني برنامج المصاريف خطوة بخطوة، وفي كل خطوة نكتب الكود بلغتي Python وJavaScript جنباً إلى جنب ونشغله ونعرض المخرج الحقيقي، والسبب أن رؤية نفس الفكرة بلغتين تعلمك أن الفكرة هي المهمة وأن الكتابة تفاصيل، وتريك أيضاً أين تختلف اللغتان اختلافاً يسبب أخطاء حقيقية. وفي آخر المقال سوف نرتكب أشهر أخطاء المبتدئين عمداً ونقرأ رسائلها كما تظهر بالضبط. وقد جهزنا البيئة التي تشغل فيها هذه الأمثلة في مقال [بيئة البرمجة للمبتدئين](https://arabroot.io/articles/%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-vs-code-%D9%88%D8%A7%D9%84%D8%B7%D8%B1%D9%81%D9%8A%D8%A9/)، وتعلمنا كيف نفكر في المسألة قبل كتابتها في مقال [كيف تفكر كمبرمج](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D9%81%D9%83%D8%B1-%D9%83%D9%85%D8%A8%D8%B1%D9%85%D8%AC-%D8%A7%D9%84%D8%AA%D9%81%D9%83%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8%D9%8A/).

وسوف نناقش في هذا المقال ما يلي:

- كيف تشغل الأمثلة، ولماذا Python وJavaScript معاً.
- المتغيرات Variables والأنواع Types: أين يحفظ البرنامج القيم، ولماذا يهم نوع كل قيمة.
- العمليات Operators، وكيف تختلف اللغتان عندما تخلط نصاً برقم.
- الشروط Conditions والقيم المنطقية Boolean، وأهمية حد الشرط.
- القوائم Lists والحلقات Loops بنوعيها، والحلقة التي لا تنتهي.
- الدوال Functions والمعاملات Parameters والقيمة المعادة Return، والفرق بين الطباعة والإعادة.
- البرنامج الكامل بلغتين، ثم أخطاء المبتدئين الحقيقية وكيف تقرأ رسالة كل خطأ.
- جدول مقارنة سريع بين اللغتين، وقائمة تحقق، وتدريب عملي.

## كيف تشغل الأمثلة؟ Python و Node جنباً إلى جنب

كل مثال في هذا المقال ملفان: ملف `.py` تشغله بالأمر `python3`، وملف `.js` تشغله بالأمر `node`. وإذا كانت اللغتان مثبتتين على جهازك فهذا يكفي. أما نحن فشغلنا الأمثلة داخل Container واحد فيه Python 3.13 وNode 26 معاً، حتى تكون المخرجات متطابقة عند كل قارئ، وبنيناه بالملف التالي:

```dockerfile
FROM node:26-slim AS node
FROM python:3.13-slim
# the official Node binary needs libatomic1, which python:3.13-slim does not ship
RUN apt-get update && apt-get install -y --no-install-recommends libatomic1 && rm -rf /var/lib/apt/lists/*
COPY --from=node /usr/local/bin/node /usr/local/bin/node
ENV PYTHONUNBUFFERED=1
WORKDIR /lab
```

ولاحظ التعليق في السطر الثالث، فهو مشكلة حقيقية ظهرت لنا في أول تشغيل: نسخنا ملف `node` من الـ Image الرسمية لـ Node إلى Image الخاصة بـ Python، فرفض العمل برسالة `error while loading shared libraries: libatomic.so.1: cannot open shared object file`، لأن البرنامج يحتاج مكتبة نظام موجودة في Image الخاصة بـ Node وغير موجودة في Image الخاصة بـ Python. وهذا نفس نوع المشكلة الذي شرحناه في مقال [Hello World](https://arabroot.io/articles/%D8%B1%D8%AD%D9%84%D8%A9-hello-world-%D9%85%D9%86-%D9%84%D9%88%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D9%81%D8%A7%D8%AA%D9%8A%D8%AD-%D8%A7%D9%84%D9%89-%D8%A7%D9%84%D8%B4%D8%A7%D8%B4%D8%A9/) عن المكتبات المشتركة. ثم شغلنا كل الأمثلة بسكربت واحد:

```bash
docker build -t first-steps .
docker run --rm -v "$PWD":/lab -w /lab first-steps sh run.sh > run.out 2>&1
```

وأول سطر في المخرج يؤكد الإصدارين: `python: Python 3.13.17 node: v26.13.0`. وقد يتساءل البعض: لماذا نتعلم لغتين معاً بدل أن نركز على واحدة؟ والإجابة أننا لا نطلب منك أن تحفظ اللغتين، فاختر واحدة وركز عليها، ولكن ضع الأخرى بجانبها وأنت تقرأ، لأن كل فكرة تراها مكتوبة بطريقتين تفهمها كفكرة وليس كسطر محفوظ. وPython الأسهل في القراءة للمبتدئ وتستخدم في التحليل والأتمتة والذكاء الاصطناعي، وJavaScript لغة المتصفح التي لا مفر منها في تطوير الويب، والانتقال من إحداهما إلى الأخرى بعد هذا المقال سوف يكون أسهل بكثير مما تتوقع.

## البرنامج تعليمات تنفذ بالترتيب Statements

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

وفي أي سطر يمكن أن تكتب تعليقاً Comment يتجاهله المفسر تماماً، ويبدأ بعلامة `#` في Python وبعلامتي `//` في JavaScript. والتعليق مكتوب لك ولزملائك وليس للحاسب، والتعليق الجيد يشرح لماذا كتبت السطر بهذا الشكل وليس ماذا يفعل، فالكود نفسه يقول ماذا يفعل. وسوف ترى في مثال الـ Dockerfile أعلاه تعليقاً من هذا النوع: لا يقول «ثبت libatomic1» وإنما يقول لماذا احتجنا تثبيتها، وهذا ما ستحتاج أن تعرفه بعد سنة عندما تتساءل هل يمكن حذف هذا السطر.

## المتغيرات Variables: أسماء تضعها على القيم

البرنامج يحتاج أن يتذكر أشياء: الميزانية، وما صرفته، واسمك. والمتغير Variable هو اسم تختاره وتربطه بقيمة، ثم تستخدم الاسم بدل القيمة في بقية البرنامج. والمثال التالي أول خطوة في برنامج المصاريف:

```python
name = "سارة"
budget = 3000
spent = 1250.5
paid_rent = True
print(name, budget, spent, paid_rent)
print(type(name), type(budget), type(spent), type(paid_rent))
remaining = budget - spent
print("remaining:", remaining)
budget = budget + 500
print("new budget:", budget)
```

```javascript
const name = "سارة";
let budget = 3000;
const spent = 1250.5;
const paidRent = true;
console.log(name, budget, spent, paidRent);
console.log(typeof name, typeof budget, typeof spent, typeof paidRent);
const remaining = budget - spent;
console.log("remaining:", remaining);
budget = budget + 500;
console.log("new budget:", budget);
```

والمخرج من اللغتين كما يلي، Python أولاً ثم JavaScript:

```text
سارة 3000 1250.5 True
<class 'str'> <class 'int'> <class 'float'> <class 'bool'>
remaining: 1749.5
new budget: 3500

سارة 3000 1250.5 true
string number number boolean
remaining: 1749.5
new budget: 3500
```

في الكود والمخرج أعلاه لاحظ التالي:

- علامة `=` ليست مساواة كما في الرياضيات، وإنما إسناد Assignment: احسب ما على اليمين، ثم اربط الاسم الذي على اليسار بالنتيجة. ولذلك السطر `budget = budget + 500` منطقي تماماً في البرمجة ومستحيل في الرياضيات: خذ القيمة الحالية 3000 وأضف 500، ثم اجعل الاسم يشير إلى 3500.
- في JavaScript تعرف المتغير أول مرة بكلمة `let` إذا كانت قيمته سوف تتغير، أو `const` إذا لن تتغير، ولذلك عرفنا `budget` بـ `let` لأننا غيرناه. أما Python فلا تحتاج كلمة تعريف، فأول إسناد ينشئ المتغير.
- الاسم `paid_rent` في Python صار `paidRent` في JavaScript، وهذا ليس شرطاً في اللغة وإنما عرف يتبعه أغلب المبرمجين: الكلمات تفصل بشرطة سفلية في Python كما يوصي دليل الأسلوب [PEP 8](https://peps.python.org/pep-0008/?ref=arabroot.io)، وبحرف كبير في JavaScript. والتزم بعرف لغتك لأن من يقرأ كودك يتوقعه.
- القيمة المنطقية تكتب `True` بحرف كبير في Python و`true` بحرف صغير في JavaScript، ولو كتبت `true` في Python لظهر خطأ أن الاسم غير معرف.

والصورة التالية تبين ما يحدث في الذاكرة بشكل مبسط: كل اسم يشير إلى قيمة، وعندما أسندنا قيمة جديدة إلى `budget` تحرك الاسم إلى القيمة 3500 وبقيت 3000 بلا اسم، فتتخلص منها اللغة لاحقاً. وإذا أردت أن تعرف أين تعيش هذه القيم فعلاً في الذاكرة وكيف تتخلص اللغة منها فقد شرحنا ذلك في مقال [الذاكرة للمبرمجين](https://arabroot.io/articles/%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86-stack-%D9%88-heap-%D9%88%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA/).

![أربعة أسماء name وbudget وspent وpaid_rent تشير بأسهم إلى قيمها: النص سارة، والعدد الصحيح 3000 الذي انتقل منه سهم برتقالي إلى 3500 بعد الإسناد الجديد، والعدد العشري 1250.5، والقيمة المنطقية True، مع أسماء الأنواع في اللغتين: str وstring، وint وfloat في Python مقابل number واحد في JavaScript، وbool وboolean، ومخرج type وtypeof من التجربة](https://arabroot.io/content/images/2026/10/programming-first-steps-01-variables.webp)

الاسم يشير إلى قيمة، والإسناد الجديد يحرك الاسم ولا يغير القيمة القديمة

ولأسماء المتغيرات قواعد بسيطة: تبدأ بحرف أو شرطة سفلية وليس برقم، ولا تحتوي مسافات، ولا تكون كلمة محجوزة في اللغة مثل `if` أو `for`، والحروف الكبيرة والصغيرة مختلفة، فـ `Budget` و`budget` متغيران مختلفان. واللغتان تسمحان تقنياً بأسماء عربية، لكن لا تفعل ذلك، والسبب أن أدوات كثيرة ولوحات مفاتيح زملائك وكل المكتبات التي ستستخدمها تفترض أسماء إنجليزية، والنصوص العربية مكانها داخل علامات التنصيص كقيم. والأهم من القواعد أن تختار أسماء تشرح نفسها: `remaining` أفضل من `r`، و`paid_rent` أفضل من `flag2`، لأنك سوف تقرأ هذا الكود بعد شهر ولن تتذكر ما كان في رأسك.

## الأنواع Types: لماذا يهم نوع القيمة؟

لاحظ في المخرج أن كل قيمة لها نوع Type، والدالة `type` في Python والكلمة `typeof` في JavaScript تخبرانك به. والأنواع الأساسية التي سوف تستخدمها كل يوم أربعة:

- النص String: أي حروف بين علامتي تنصيص، مثل `"سارة"`، واسمه `str` في Python و`string` في JavaScript.
- العدد الصحيح Integer: رقم بلا كسور مثل 3000، واسمه `int` في Python.
- العدد العشري Float: رقم بكسور مثل 1250.5، واسمه `float` في Python. أما JavaScript فلا تفرق بين الاثنين، وكل الأرقام عندها من نوع واحد اسمه `number`، وهذا الفرق يظهر في نتائج القسمة كما سنرى.
- القيمة المنطقية Boolean: إما صح أو خطأ، `True` و`False` في Python، و`true` و`false` في JavaScript، وهي نتيجة أي مقارنة.

وهناك نوع خامس يعني «لا توجد قيمة»: اسمه `None` في Python، وفي JavaScript اثنان هما `undefined` و`null`، وسوف تقابله في قسم الدوال. ولماذا يهم النوع؟ لأن نفس العملية تعني أشياء مختلفة بحسب نوع القيم، والسطر التالي من نفس المثال يوضح ذلك:

```python
print(7 / 2, 7 // 2, 7 % 2, 2 ** 10)
print("10" + "5", int("10") + 5, str(10) + "5")
```

```javascript
console.log(7 / 2, Math.floor(7 / 2), 7 % 2, 2 ** 10);
console.log("10" + "5", Number("10") + 5, String(10) + "5");
```

```text
3.5 3 1 1024
105 15 105
```

والمخرج متطابق في اللغتين. والسطر الأول يبين عمليات الحساب: القسمة العادية `/` تعطي 3.5، والقسمة الصحيحة تعطي 3 وهي عامل `//` في Python ودالة `Math.floor` في JavaScript، وباقي القسمة `%` يعطي 1، والأس `**` يعطي 1024\. أما السطر الثاني فهو الأهم: `"10" + "5"` أعطى `105` وليس 15، لأن `+` بين نصين تعني اللصق Concatenation وليس الجمع، والنص `"10"` ليس الرقم 10 حتى لو بدا مثله. ولكي تجمع يجب أن تحول النص إلى رقم أولاً بـ `int` أو `Number`، وهذا التحويل Type Conversion سوف تحتاجه كثيراً، لأن كل ما يأتي من المستخدم أو من ملف أو من الإنترنت يصل نصاً حتى لو كان رقماً.

وهنا تبدأ اللغتان في الاختلاف، والجدول التالي يجمع نتائج حقيقية من التجربة لعبارات تكتبها في أسبوعك الأول:

![جدول نتائج نفس العبارات في Python وJavaScript: 7 / 2 تعطي 3.5 في اللغتين، والقسمة الصحيحة 3، و"10" + "5" تعطي 105، ونص مع رقم يعطي TypeError في Python ونصاً ملصقاً في JavaScript، و"5" + 2 تعطي 52 و"5" - 2 تعطي 3 في JavaScript، وتحويل 120 ريال يعطي ValueError في Python وNaN في JavaScript، والقسمة على صفر ZeroDivisionError في Python وNaN أو Infinity في JavaScript، و0.1 + 0.2 تعطي 0.30000000000000004 في اللغتين](https://arabroot.io/content/images/2026/10/programming-first-steps-02-types.webp)

Python ترفض خلط الأنواع، وJavaScript تحول بدلاً منك

والقاعدة التي تلخص الجدول: Python لغة صارمة في الأنواع، فإذا طلبت منها أن تجمع نصاً ورقماً توقفت وقالت لك إنها لا تعرف ماذا تقصد، أما JavaScript فتحاول أن تخمن ما تقصده، فتحول الرقم إلى نص عند الجمع، وتحول النص إلى رقم عند الطرح، فيعطي `"5" + 2` النتيجة `52` ويعطي `"5" - 2` النتيجة `3`. وهذا السلوك موصوف بدقة في المواصفة وفي [دليل JavaScript على MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide?ref=arabroot.io)، لكنه مصدر أخطاء لا تنتهي للمبتدئين، والسبب أن البرنامج لا يتوقف وإنما يكمل بقيمة خاطئة. وآخر سطر في الجدول، `0.1 + 0.2`، يعطي نفس النتيجة الغريبة في اللغتين لأن السبب ليس في اللغة وإنما في طريقة تخزين الكسور في الحاسب، وقد شرحنا ذلك في مقال [الأرقام العشرية Floating Point من الداخل](https://arabroot.io/articles/%D8%A7%D9%84%D8%A3%D8%B1%D9%82%D8%A7%D9%85-%D8%A7%D9%84%D8%B9%D8%B4%D8%B1%D9%8A%D8%A9-floating-point-%D9%85%D9%86-%D8%A7%D9%84%D8%AF%D8%A7%D8%AE%D9%84/)، والخلاصة لبرنامج المصاريف أن تقرب المبالغ عند العرض بـ `round` أو `toFixed`.

ومن المفاجآت التي ظهرت لنا في التجربة وتهم القارئ العربي تحديداً: الدالة `int("١٢٠")` في Python أعطت 120، لأنها تقبل الأرقام العربية الهندية كما تقبل الأرقام الغربية بحسب [وثائق الدوال المدمجة](https://docs.python.org/3/library/functions.html?ref=arabroot.io)، أما `Number("١٢٠")` في JavaScript فأعطت `NaN`، أي «ليس رقماً» Not a Number. فإذا كان برنامجك يستقبل أرقاماً يكتبها مستخدمون عرب فلا تفترض أن اللغتين تتصرفان بنفس الطريقة، وجرب بنفسك.

## العمليات Operators: المقارنة والمنطق

قبل أن نكتب أول شرط نحتاج أدواته. فالمقارنة Comparison بين قيمتين تعطي دائماً قيمة منطقية، والعوامل في اللغتين متشابهة: `<` أصغر، و`>` أكبر، و`<=` و`>=`، و`!=` لا يساوي. والفرق في المساواة: Python تستخدم `==`، أما JavaScript ففيها `==` و`===`، والأولى تحول الأنواع قبل المقارنة والثانية لا تحول. ونتيجة التجربة توضح الفرق:

```javascript
console.log("5" == 5, "5" === 5, 0 == "", null == undefined);
```

```text
true false true true
```

فالنص `"5"` يساوي الرقم 5 بـ `==`، والصفر يساوي النص الفارغ، وهذا نادراً ما يكون ما تقصده. ولذلك القاعدة في JavaScript بسيطة: استخدم `===` و`!==` دائماً، وانس وجود `==` إلا إذا كنت تعرف لماذا تحتاجها، والتفاصيل كاملة في صفحة [المقارنة والمساواة على MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality%5Fcomparisons%5Fand%5Fsameness?ref=arabroot.io). أما العوامل المنطقية Logical Operators التي تجمع شرطين فهي كلمات في Python: `and` و`or` و`not`، ورموز في JavaScript: `&&` و`||` و`!`، والمعنى واحد.

## الشروط Conditions: البرنامج يقرر

الآن نجعل برنامج المصاريف يقرر: إذا تجاوزت الميزانية يحذرك، وإذا بقي أقل من 10% منها ينبهك، وإلا يطمئنك. والشرط Condition يعني أن جزءاً من الكود ينفذ فقط إذا كانت عبارة منطقية صحيحة:

```python
budget = 3000
spent = 2700

remaining = budget - spent
if remaining < 0:
    print("تجاوزت الميزانية بمقدار", -remaining)
elif remaining < budget * 0.1:
    print("انتبه: بقي أقل من 10% من الميزانية:", remaining)
else:
    print("وضعك جيد، المتبقي:", remaining)

is_weekend = True
has_coupon = False
if is_weekend and not has_coupon:
    print("لا خصم اليوم")
print(remaining < 500, remaining == 300, remaining != 300)
```

```javascript
const budget = 3000;
const spent = 2700;

const remaining = budget - spent;
if (remaining < 0) {
  console.log("تجاوزت الميزانية بمقدار", -remaining);
} else if (remaining < budget * 0.1) {
  console.log("انتبه: بقي أقل من 10% من الميزانية:", remaining);
} else {
  console.log("وضعك جيد، المتبقي:", remaining);
}

const isWeekend = true;
const hasCoupon = false;
if (isWeekend && !hasCoupon) {
  console.log("لا خصم اليوم");
}
console.log(remaining < 500, remaining === 300, remaining !== 300);
```

```text
وضعك جيد، المتبقي: 300
لا خصم اليوم
True True False
```

والمخرج في JavaScript نفسه مع `true true false` بحروف صغيرة. وفي الكود أعلاه لاحظ التالي:

- اللغتان تفحصان الشروط بالترتيب من الأعلى، وأول شرط صحيح ينفذ ما تحته ثم يتجاوز البرنامج كل البقية، و`else` تنفذ فقط إذا لم يصح أي شرط. والكلمة `elif` في Python هي `else if` في JavaScript.
- Python تحدد الكود الذي يتبع الشرط بالإزاحة Indentation، أي المسافات في أول السطر، وبالنقطتين في آخر سطر الشرط. أما JavaScript فتحدده بالأقواس المعقوفة `{ }` وتضع الشرط نفسه بين قوسين. ولذلك الإزاحة في Python جزء من معنى الكود وليست تجميلاً، وسوف ترى في قسم الأخطاء ماذا يحدث إذا نسيتها.
- المتبقي كان 300 بالضبط، وعشر الميزانية 300 أيضاً، فلم يظهر التنبيه لأن 300 ليست أصغر من 300، فوصلنا إلى `else`.

والنقطة الأخيرة تستحق وقفة، والصورة التالية تبين المسار الذي سلكه البرنامج:

![مخطط مسار الشرط: المتبقي 300، الشرط الأول remaining أصغر من 0 خاطئ، والشرط الثاني remaining أصغر من budget * 0.1 خاطئ لأن 300 ليست أصغر من 300، فيصل البرنامج إلى else ويطبع وضعك جيد المتبقي 300، مع ملاحظة أن حد الشرط مهم وأن أصغر من يختلف عن أصغر من أو يساوي](https://arabroot.io/content/images/2026/10/programming-first-steps-03-conditions.webp)

طريق واحد فقط ينفذ، وحد الشرط يقرر أي طريق

هل كان يجب أن يظهر التنبيه عندما يكون المتبقي 10% بالضبط؟ هذا ليس سؤالاً برمجياً وإنما قرار في فهم المسألة، والكود سوف ينفذ ما كتبته حرفياً، فإذا أردت أن يشمل التنبيه الحد نفسه فاكتب `<=`. وأغلب أخطاء الشروط في البرامج الحقيقية ليست شرطاً خاطئاً تماماً، وإنما شرط صحيح إلا عند حده، ولذلك عندما تختبر أي شرط جرب القيمة التي على الحد بالضبط، وقيمة أقل منها بقليل، وقيمة أكبر منها بقليل.

## القوائم Lists والحلقات Loops: كرر الخطوة على كل مصروف

مصاريف الشهر ليست رقماً واحداً وإنما قائمة، والقائمة List في Python أو المصفوفة Array في JavaScript تحفظ عدة قيم بالترتيب تحت اسم واحد، وتكتب بين قوسين مربعين. وكل عنصر له رقم موقع Index يبدأ من صفر وليس من واحد، فالعنصر الأول موقعه 0\. والحلقة Loop تكرر نفس الخطوة على كل عنصر، أو تكرر حتى يتحقق شرط، والمثال التالي يجمع المصاريف ويعرضها ثم يجيب عن سؤال: كم مصروفاً أستطيع أن أدفع قبل أن أتجاوز 1500؟

```python
expenses = [450, 120.5, 80, 900, 35]
print("count:", len(expenses), "first:", expenses[0], "last:", expenses[-1])

total = 0
for amount in expenses:
    total = total + amount
print("total:", total)

for i in range(len(expenses)):
    print(i, expenses[i])

budget = 1500
spent = 0
day = 0
while spent + expenses[day] <= budget:
    spent += expenses[day]
    day += 1
print("stopped before item", day, "spent", spent)

big = []
for amount in expenses:
    if amount < 100:
        continue
    big.append(amount)
print("100 or more:", big)
```

```javascript
const expenses = [450, 120.5, 80, 900, 35];
console.log("count:", expenses.length, "first:", expenses[0], "last:", expenses[expenses.length - 1]);

let total = 0;
for (const amount of expenses) {
  total = total + amount;
}
console.log("total:", total);

for (let i = 0; i < expenses.length; i++) {
  console.log(i, expenses[i]);
}

const budget = 1500;
let spent = 0;
let day = 0;
while (spent + expenses[day] <= budget) {
  spent += expenses[day];
  day++;
}
console.log("stopped before item", day, "spent", spent);

const big = [];
for (const amount of expenses) {
  if (amount < 100) continue;
  big.push(amount);
}
console.log("100 or more:", big);
```

والمخرج متطابق في اللغتين إلا في شكل طباعة القائمة في آخر سطر:

```text
count: 5 first: 450 last: 35
total: 1585.5
0 450
1 120.5
2 80
3 900
4 35
stopped before item 3 spent 650.5
100 or more: [450, 120.5, 900]
```

في الكود والمخرج أعلاه لاحظ التالي:

- آخر عنصر في Python يمكن أن تصل إليه بالموقع `-1`، أما JavaScript فتحتاج `expenses[expenses.length - 1]`، لأن الموقع السالب لا يعني شيئاً فيها.
- الحلقة الأولى `for amount in expenses` في Python و`for (const amount of expenses)` في JavaScript تعطيك كل عنصر بنفسه، وهي الشكل الذي تستخدمه في أغلب الأحوال. ولاحظ أن `for ... in` في JavaScript شيء آخر يعطيك المواقع وليس القيم، وهذا فخ معروف لمن ينتقل من Python.
- الحلقة الثانية تعطيك الموقع `i` وتصل إلى العنصر من خلاله، وتحتاجها عندما يهمك الموقع نفسه. والدالة `range(len(expenses))` في Python تعطي الأرقام من 0 إلى 4، والحلقة الثلاثية في JavaScript تقول ابدأ من 0، واستمر ما دام `i` أصغر من الطول، وزد واحداً بعد كل دورة.
- الحلقة `while` لا تعرف مسبقاً كم مرة سوف تدور، وإنما تكرر ما دام الشرط صحيحاً، وتوقفت قبل العنصر الذي موقعه 3 لأن إضافة 900 كانت ستجعل المجموع 1550.5.
- الكلمة `continue` تتجاوز بقية الدورة الحالية وتنتقل إلى العنصر التالي، فلم يدخل 80 و35 في قائمة المصاريف الكبيرة. وأختها `break` تخرج من الحلقة كلها.

![جدولان لتتبع الحلقتين: الحلقة for تجمع المصاريف 450 ثم 120.5 ثم 80 ثم 900 ثم 35 حتى يصبح المجموع 1585.5، والحلقة while تضيف المصاريف ما دام المجموع لا يتجاوز 1500 فتنجح في 450 و570.5 و650.5 وتتوقف عند 1550.5، مع تنبيه أن الحلقة التي لا تنتهي طبعت 56,661 سطراً في ثانيتين](https://arabroot.io/content/images/2026/10/programming-first-steps-04-loops.webp)

for تمر على الكل، وwhile تتوقف عندما يصبح شرطها خاطئاً

وفي `while` خطر لا يوجد في `for`: إذا نسيت أن تغير المتغير الذي يعتمد عليه الشرط بقي الشرط صحيحاً إلى الأبد، وهذه الحلقة التي لا تنتهي Infinite Loop. وقد كتبنا عمداً حلقة تطبع اليوم دون أن تزيد العداد، وشغلناها مع الأمر `timeout 2` الذي يوقف أي برنامج بعد ثانيتين:

```python
i = 0
while i < 3:
    print("day", i)
```

```text
[exit 124]
lines printed in 2 seconds: 56661
day 0
day 0
day 0
```

طبعت الحلقة 56,661 سطراً في ثانيتين، كلها `day 0`، ثم أوقفها `timeout` برمز الخروج 124 الذي يعني أن الوقت انتهى. وعلى جهازك لن يوقفها أحد، فإذا رأيت برنامجك يطبع بلا توقف أو يبدو معلقاً فاضغط `Ctrl+C` في الطرفية، ثم ابحث عن الحلقة التي لا يتغير شرطها. وتفاصيل الحلقات في اللغتين في [دليل Python لأدوات التحكم](https://docs.python.org/3/tutorial/controlflow.html?ref=arabroot.io) و[دليل الحلقات على MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops%5Fand%5Fiteration?ref=arabroot.io).

## الدوال Functions: اكتب الخطوات مرة واستدعها كثيراً

جمع قائمة من الأرقام عملية سوف تحتاجها في أكثر من مكان: لمصاريف الطعام، ولمصاريف المواصلات، وللشهر كله. والحل البسيط أن تنسخ الحلقة في كل مكان، وهذا يعمل حتى تكتشف خطأ فيها فتحتاج أن تصلحه في خمسة أماكن، وتنسى واحداً. والحل الصحيح أن تضع الخطوات في دالة Function لها اسم، وتعطيها ما تحتاجه عبر المعاملات Parameters، وتعيد لك النتيجة بالكلمة `return`:

```python
def total(items):
    s = 0
    for x in items:
        s += x
    return s

def with_vat(amount, rate=0.15):
    return round(amount * (1 + rate), 2)

def show(amount):
    print("المبلغ:", amount)

food = [35, 22.5, 60]
print(total(food))
print(with_vat(100), with_vat(100, rate=0.05))
result = show(10)
print("show returned:", result)
print(total([]))
```

```javascript
function total(items) {
  let s = 0;
  for (const x of items) {
    s += x;
  }
  return s;
}

function withVat(amount, rate = 0.15) {
  return Math.round(amount * (1 + rate) * 100) / 100;
}

function show(amount) {
  console.log("المبلغ:", amount);
}

const food = [35, 22.5, 60];
console.log(total(food));
console.log(withVat(100), withVat(100, 0.05));
const result = show(10);
console.log("show returned:", result);
console.log(total([]));
```

```text
117.5
115.0 105.0
المبلغ: 10
show returned: None
0

117.5
115 105
المبلغ: 10
show returned: undefined
0
```

في الكود والمخرج أعلاه لاحظ التالي:

- الدالة تعرف بـ `def` في Python و`function` في JavaScript، والمعاملات بين القوسين أسماء مؤقتة تأخذ القيم التي ترسلها عند الاستدعاء، فداخل `total(food)` يصبح `items` هو قائمة الطعام.
- المعامل `rate` له قيمة افتراضية Default هي 0.15، فإذا لم ترسل غيرها استخدمت، وإذا أرسلت 0.05 استخدمت بدلاً منها. وطريقة التقريب مختلفة: Python أعطت `115.0` لأن `round` تعيد عدداً عشرياً، وJavaScript أعطت `115` لأن نوع `number` واحد ولا يطبع الكسر الصفري.
- الدالة `show` تطبع ولا تعيد شيئاً، فعندما حفظنا نتيجتها في `result` كانت `None` في Python و`undefined` في JavaScript. وهذا هو الفرق بين الطباعة والإعادة الذي يخلط فيه أغلب المبتدئين: `print` تعرض القيمة لك أنت على الشاشة، و`return` تعطي القيمة للكود الذي استدعى الدالة ليستخدمها.
- `total([])` أعطت 0، لأن الحلقة لم تدر أي دورة وبقي المجموع على قيمته الأولى، وهي النتيجة الصحيحة لمجموع قائمة فارغة.

![الدالة كآلة: المدخلات amount = 100 وrate = 0.15 تدخل الدالة with_vat التي تحسب amount * (1 + rate) وتعيد round بقيمة 115.0، مع ملاحظة القيمة الافتراضية، ومقارنة بين return الذي يعيد القيمة فيطبع 117.5، وprint بدل return الذي يجعل النتيجة None فيظهر TypeError عند إضافة 5، وفي JavaScript undefined فيصبح الناتج NaN بلا خطأ](https://arabroot.io/content/images/2026/10/programming-first-steps-05-functions.webp)

return تعطي القيمة لمن استدعى، وprint تعرضها لك فقط

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

وفكرة الدالة هي نفسها فكرة التفكيك التي رأيناها في [مقال التفكير الحاسوبي](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D9%81%D9%83%D8%B1-%D9%83%D9%85%D8%A8%D8%B1%D9%85%D8%AC-%D8%A7%D9%84%D8%AA%D9%81%D9%83%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8%D9%8A/): كل خطوة في شجرة التفكيك تصبح دالة باسم يشرح ما تفعله، والبرنامج الرئيسي يصبح سلسلة استدعاءات تقرؤها كأنها الكود الوهمي نفسه.

## البرنامج الكامل: القواميس Dictionaries وتقرير المصاريف الشهري

الآن نجمع كل ما سبق في برنامج واحد. ونحتاج أداة أخيرة: كل مصروف ليس رقماً فقط وإنما له اسم وفئة ومبلغ، ولذلك نستخدم القاموس Dictionary في Python أو الكائن Object في JavaScript، وهو مجموعة أزواج من مفتاح وقيمة، تصل إلى القيمة باسم المفتاح بدل رقم الموقع:

```python
"""Monthly expenses report: everything from the article in one small program."""

BUDGET = 3000

expenses = [
    {"item": "إيجار", "category": "سكن", "amount": 1500},
    {"item": "بقالة", "category": "طعام", "amount": 420.75},
    {"item": "مطعم", "category": "طعام", "amount": 180},
    {"item": "بنزين", "category": "مواصلات", "amount": 260},
    {"item": "اشتراك", "category": "ترفيه", "amount": 45},
    {"item": "مطعم", "category": "طعام", "amount": 210},
]

def by_category(items):
    sums = {}
    for e in items:
        sums[e["category"]] = sums.get(e["category"], 0) + e["amount"]
    return sums

def status(spent, budget):
    if spent > budget:
        return "تجاوزت الميزانية"
    if spent > budget * 0.9:
        return "قريب من الحد"
    return "ضمن الميزانية"

def main():
    sums = by_category(expenses)
    spent = sum(sums.values())
    for cat, amount in sorted(sums.items(), key=lambda kv: kv[1], reverse=True):
        share = amount / spent * 100
        print(f"{cat:<8} {amount:>9.2f}  {share:5.1f}%")
    print(f"المجموع  {spent:>9.2f}")
    print("الحالة:", status(spent, BUDGET), "| المتبقي:", round(BUDGET - spent, 2))

main()
```

```javascript
// Monthly expenses report: everything from the article in one small program.
const BUDGET = 3000;

const expenses = [
  { item: "إيجار", category: "سكن", amount: 1500 },
  { item: "بقالة", category: "طعام", amount: 420.75 },
  { item: "مطعم", category: "طعام", amount: 180 },
  { item: "بنزين", category: "مواصلات", amount: 260 },
  { item: "اشتراك", category: "ترفيه", amount: 45 },
  { item: "مطعم", category: "طعام", amount: 210 },
];

function byCategory(items) {
  const sums = {};
  for (const e of items) {
    sums[e.category] = (sums[e.category] ?? 0) + e.amount;
  }
  return sums;
}

function status(spent, budget) {
  if (spent > budget) return "تجاوزت الميزانية";
  if (spent > budget * 0.9) return "قريب من الحد";
  return "ضمن الميزانية";
}

function main() {
  const sums = byCategory(expenses);
  const spent = Object.values(sums).reduce((a, b) => a + b, 0);
  const rows = Object.entries(sums).sort((a, b) => b[1] - a[1]);
  for (const [cat, amount] of rows) {
    const share = (amount / spent) * 100;
    console.log(`${cat.padEnd(8)} ${amount.toFixed(2).padStart(9)}  ${share.toFixed(1).padStart(5)}%`);
  }
  console.log(`المجموع  ${spent.toFixed(2).padStart(9)}`);
  console.log("الحالة:", status(spent, BUDGET), "| المتبقي:", Math.round((BUDGET - spent) * 100) / 100);
}

main();
```

والمخرج من اللغتين متطابق حرفاً بحرف:

```text
سكن        1500.00   57.3%
طعام        810.75   31.0%
مواصلات     260.00    9.9%
ترفيه        45.00    1.7%
المجموع    2615.75
الحالة: ضمن الميزانية | المتبقي: 384.25
```

في البرنامج أعلاه لاحظ التالي:

- الدالة `by_category` هي نمط العد بالقاموس الذي رأيناه في مقال التفكير الحاسوبي، ولكنها تجمع المبالغ بدل أن تعد التكرار: لكل مصروف تأخذ مجموع فئته الحالي، أو صفراً إذا كانت الفئة جديدة، وتضيف المبلغ. والطعام ظهر ثلاث مرات فصار مجموعه 420.75 + 180 + 210 = 810.75.
- `sums.get(key, 0)` في Python و`sums[key] ?? 0` في JavaScript يقولان نفس الشيء: أعطني القيمة، أو صفراً إذا لم يوجد المفتاح. ولو كتبت `sums[key]` مباشرة لظهر خطأ `KeyError` في Python عند أول فئة جديدة، ولأعطت JavaScript `undefined` ثم `NaN` بهدوء.
- الدالة `status` تعيد نصاً ولا تطبعه، و`main` وحدها تطبع، فيمكن أن تختبر `status(3200, 3000)` بسهولة وتتأكد أنها تعيد «تجاوزت الميزانية» دون أن تشغل البرنامج كله.
- الترتيب من الأكبر إلى الأصغر يستخدم دالة صغيرة بلا اسم تخبر الترتيب ماذا يقارن، وهي `lambda` في Python والسهم `=>` في JavaScript، ولا تحتاج أن تفهمها الآن بالكامل، والمهم أنها تقول «رتب بحسب المبلغ».
- التنسيق `{amount:>9.2f}` في Python و`toFixed(2).padStart(9)` في JavaScript يعرضان الرقم بخانتين عشريتين في عرض ثابت، وهذا ما جعل الأعمدة متساوية، وهو أيضاً ما حل مشكلة الكسور العشرية عند العرض.
- `BUDGET` بحروف كبيرة عرف يعني «ثابت لا يتغير». وفي JavaScript تفرضه `const` فعلاً، أما Python فلا يوجد فيها ثابت حقيقي والحروف الكبيرة اتفاق بين المبرمجين فقط.

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

## أخطاء المبتدئين الحقيقية: اقرأ رسالة الخطأ Error Message قبل أن تبحث

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

### اسم مكتوب خطأ: NameError و ReferenceError

```python
total = 1250
print(totl)
```

```text
Traceback (most recent call last):
  File "/lab/errors/e01_name.py", line 2, in <module>
    print(totl)
          ^^^^
NameError: name 'totl' is not defined. Did you mean: 'total'?
[exit 1]
```

```text
/lab/errors/e01_name.js:2
console.log(totl);
            ^

ReferenceError: totl is not defined
    at Object.<anonymous> (/lab/errors/e01_name.js:2:13)
    at Module._compile (node:internal/modules/cjs/loader:1968:14)
    ...
Node.js v26.13.0
[exit 1]
```

أبسط خطأ وأكثره تكراراً: حرف ناقص في اسم متغير. ولاحظ أن Python لم تكتف بأن تقول إن الاسم غير معرف، وإنما اقترحت الاسم الصحيح `Did you mean: 'total'?`، وهذه الاقتراحات أضيفت في Python 3.10 كما تذكر [ملاحظات الإصدار](https://docs.python.org/3/whatsnew/3.10.html?ref=arabroot.io)، وهي سبب إضافي لأن تستخدم إصداراً حديثاً. أما Node فتعطيك الملف والسطر والعمود `2:13` وسهماً تحت الاسم، ثم قائمة أسطر تبدأ بـ `node:internal` هي من داخل Node نفسه ولا علاقة لها بكودك، فتجاهلها.

### نص مع رقم: TypeError في Python وصمت في JavaScript

```python
remaining = 300
print("المتبقي: " + remaining)
```

```text
Traceback (most recent call last):
  File "/lab/errors/e02_concat.py", line 2, in <module>
    print("المتبقي: " + remaining)
          ~~~~~~~~~~~~^~~~~~~~~~~
TypeError: can only concatenate str (not "int") to str
[exit 1]
```

Python تقول بالضبط ما المشكلة: يمكن لصق نص بنص فقط وليس برقم، والعلامة `^` تحت `+` تحدد العملية. والحل `str(remaining)`، أو الأفضل أن تستخدم الصيغة `f"المتبقي: {remaining}"` التي تحول بنفسها. أما نفس السطر في JavaScript فطبع `المتبقي: 300` دون أي شكوى، وفي نفس الملف طبع `"5" + 2, "5" - 2, "5" * "2"` النتائج `52 3 10`. وهنا يبدو أن JavaScript أريح، لكن تذكر أن نفس السلوك هو الذي يجعل `"120" + 80` تعطي `12080` في برنامج مصاريف يقرأ المبالغ من نموذج إدخال.

### الإزاحة Indentation والأقواس: IndentationError و SyntaxError

```python
spent = 3200
if spent > 3000:
print("تجاوزت الميزانية")
```

```text
  File "/lab/errors/e03_indent.py", line 3
    print("تجاوزت الميزانية")
    ^^^^^
IndentationError: expected an indented block after 'if' statement on line 2
[exit 1]
```

```text
  File "/lab/errors/e04_colon.py", line 2
    if spent > 3000
                   ^
SyntaxError: expected ':'
[exit 1]
```

هذان خطآن في شكل الكود وليس في منطقه، ولذلك لاحظ أن الرسالة لا تبدأ بـ `Traceback`: Python رفضت الملف كله قبل أن تنفذ أي سطر منه. والرسالة الأولى تقول إن السطر بعد `if` يجب أن يكون مزاحاً، والثانية تقول إن النقطتين ناقصتان، والسهم يشير إلى المكان الذي توقعتهما فيه. وفي JavaScript نسينا القوس المعقوف الذي يغلق الشرط، فكانت الرسالة `SyntaxError: Unexpected end of input` أي أن الملف انتهى والقوس ما زال مفتوحاً، والسطر الذي تشير إليه هو آخر سطر في الملف وليس السطر الذي نسيت فيه القوس، فابحث للأعلى. والمحرر الجيد يمنع أغلب هذه الأخطاء لأنه يلون الأقواس المتقابلة ويزيح الأسطر تلقائياً.

### علامة الإسناد Assignment بدل المقارنة: = بدل ==

```python
spent = 3000
if spent = 3000:
    print("وصلت الحد")
```

```text
  File "/lab/errors/e05_assign.py", line 2
    if spent = 3000:
       ^^^^^^^^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?
[exit 1]
```

```javascript
let spent = 2000;
if (spent = 3000) {
  console.log("وصلت الحد، spent =", spent);
}
```

```text
وصلت الحد، spent = 3000
[exit 0]
```

هذا أخطر خطأ في القسم كله، والسبب في المخرج الثاني. Python رفضت السطر واقترحت `==`. أما JavaScript فنفذته بنجاح: أسندت 3000 إلى `spent` بدل أن تقارن، ولأن نتيجة الإسناد 3000 وهي قيمة تعتبرها JavaScript صحيحة، دخل البرنامج الشرط وطبع أنك وصلت الحد، مع أن المصروف كان 2000، وغير قيمته إلى 3000 لبقية البرنامج. ورمز الخروج 0، أي أن البرنامج نفسه يقول إن كل شيء بخير. ولهذا السبب تحذر أدوات فحص الكود مثل ESLint من هذا السطر بالقاعدة [no-cond-assign](https://eslint.org/docs/latest/rules/no-cond-assign?ref=arabroot.io)، ولهذا السبب أيضاً يعتبر كثير من المبرمجين أن الخطأ الصاخب أرحم من الخطأ الصامت.

### تحويل نص إلى رقم: ValueError و NaN

```text
$ python3 e06_int.py
120
Traceback (most recent call last):
  File "/lab/errors/e06_int.py", line 3, in <module>
    amount = int("120 ريال")
             ~~~^^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: '120 ريال'
[exit 1]

$ node e06_number.js
120 NaN 120 NaN
NaN
```

السطر الأول في Python حول `"١٢٠"` بالأرقام العربية الهندية إلى 120 بنجاح، ثم توقف عند `"120 ريال"` لأن النص فيه كلمة ليست رقماً، والرسالة تعرض النص الذي فشل كما هو. وفي JavaScript أعطت `Number("120")` الرقم 120، و`Number("120 ريال")` أعطت `NaN`، أما `parseInt("120 ريال")` فأعطت 120 لأنها تقرأ الأرقام من أول النص وتتوقف عند أول حرف ليس رقماً، و`parseInt("ريال 120")` أعطت `NaN` لأن أول حرف ليس رقماً، و`Number("١٢٠")` أعطت `NaN`. والقيمة `NaN` خطيرة لأنها تنتشر: أي عملية حسابية فيها `NaN` نتيجتها `NaN`، فتصل إلى آخر البرنامج وتظهر في التقرير دون أن تعرف من أين جاءت. وتفاصيلها في صفحة [NaN على MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global%5FObjects/NaN?ref=arabroot.io).

### القائمة الفارغة والموقع الخارج: ZeroDivisionError و IndexError

```text
$ python3 e07_zero.py
Traceback (most recent call last):
  File "/lab/errors/e07_zero.py", line 2, in <module>
    average = sum(expenses) / len(expenses)
              ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
ZeroDivisionError: division by zero
[exit 1]

$ node e07_zero.js
NaN Infinity -Infinity
```

متوسط مصاريف قائمة فارغة يعني قسمة صفر على صفر، وهي الحالة الخاصة نفسها التي أسقطت أول نسخة في مقال التفكير الحاسوبي. Python توقفت، وJavaScript أعطت `NaN`، وأعطت `1 / 0` القيمة `Infinity`. ثم الخطأ الأشهر في الحلقات، وهو أن تمشي خطوة أبعد من آخر القائمة، والسبب غالباً أنك تذكرت أن القائمة فيها 3 عناصر ونسيت أن مواقعها 0 و1 و2:

```python
expenses = [450, 120.5, 80]
for i in range(len(expenses) + 1):
    print(i, expenses[i])
```

```text
0 450
1 120.5
2 80
Traceback (most recent call last):
  File "/lab/errors/e08_index.py", line 3, in <module>
    print(i, expenses[i])
             ~~~~~~~~^^^
IndexError: list index out of range
[exit 1]
```

وفي JavaScript كتبنا الحلقة بـ `<=` بدل `<`، وهو نفس الخطأ بخطوة واحدة Off-by-one، فطبعت السطر الرابع `3 undefined` دون أي خطأ، ثم عندما حاولنا أن نستخدم ذلك العنصر في السطر التالي ظهرت الرسالة:

```text
0 450
1 120.5
2 80
3 undefined
/lab/errors/e08_index.js:5
console.log(expenses[3].toFixed(2));
                        ^

TypeError: Cannot read properties of undefined (reading 'toFixed')
    at Object.<anonymous> (/lab/errors/e08_index.js:5:25)
    ...
Node.js v26.13.0
[exit 1]
```

ولاحظ الفرق المهم بين اللغتين هنا: Python أوقفت البرنامج في نفس السطر الذي حدث فيه الخطأ، أما JavaScript فأعطت `undefined` وأكملت، وظهر الخطأ في سطر لاحق. وفي برنامج حقيقي قد يكون هذا السطر اللاحق في ملف آخر بعد مئة سطر، فتقضي وقتاً تبحث في المكان الخطأ. وعندما ترى `Cannot read properties of undefined` فاسأل: من أين جاءت هذه `undefined`؟ وارجع خطوة خطوة إلى الخلف.

### الدالة التي تطبع ولا تعيد Return، والمقارنة بين نص ورقم

```text
$ python3 e09_return.py
30
Traceback (most recent call last):
  File "/lab/errors/e09_return.py", line 8, in <module>
    print(result + 5)
          ~~~~~~~^~~
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
[exit 1]

$ node e09_return.js
30
NaN
```

الدالة في هذا المثال تحسب المجموع وتطبعه بـ `print` بدل أن تعيده، فظهر 30 على الشاشة، لكن المتغير `result` أصبح `None`، فلما أضفنا إليه 5 قالت Python إنها لا تعرف كيف تجمع `NoneType` مع `int`. وكلمة `NoneType` في أي رسالة خطأ إشارة شبه مؤكدة إلى دالة نسيت فيها `return`. أما JavaScript فأعطت `NaN` بهدوء. ثم المقارنة بين نص ورقم، وهي تحدث كثيراً عندما تقرأ مبلغاً من مدخلات المستخدم وتنسى تحويله:

```text
$ python3 e13_compare.py
Traceback (most recent call last):
  File "/lab/errors/e13_compare.py", line 6, in <module>
    print(check("3200"))
          ~~~~~^^^^^^^^
  File "/lab/errors/e13_compare.py", line 2, in check
    if spent > 3000:
       ^^^^^^^^^^^^
TypeError: '>' not supported between instances of 'str' and 'int'
[exit 1]

$ node e13_compare.js
over ok false true
```

وهذه أول رسالة فيها مستويان، والصورة التالية تبين كيف تقرؤها مع رسالة Node:

![تشريح رسالة الخطأ: في Python الـ Traceback يقرأ من الأسفل، آخر سطر TypeError والسبب مقارنة نص برقم، وفوقه السطر 2 داخل الدالة check حيث حدثت المقارنة، وفوقه السطر 6 الذي استدعى الدالة بالنص 3200، وفي Node الـ Stack Trace يقرأ من الأعلى: الملف والسطر 5 مع سهم تحت المكان، ثم TypeError Cannot read properties of undefined، والأسطر التي تبدأ بـ node:internal من داخل Node وتتجاهل](https://arabroot.io/content/images/2026/10/programming-first-steps-06-traceback.webp)

Python: اقرأ من الأسفل. Node: اقرأ من الأعلى، وتجاهل node:internal

الـ Traceback في Python يعرض سلسلة الاستدعاءات من الأقدم إلى الأحدث، ولذلك يقول في أوله `most recent call last`: السطر 6 في البرنامج الرئيسي استدعى `check("3200")`، وداخل `check` في السطر 2 حدثت المقارنة التي فشلت. فالخطأ ظهر في السطر 2، لكن سببه الحقيقي في السطر 6 الذي أرسل نصاً بدل رقم، وهذا يحدث كثيراً: المكان الذي يظهر فيه الخطأ ليس دائماً المكان الذي تصلحه. أما JavaScript فأعطت `over` لأنها حولت النص `"3200"` إلى رقم وقارنت، وأعطت `check("abc")` النتيجة `ok` لأن `"abc"` تحولت إلى `NaN` وأي مقارنة مع `NaN` خاطئة. والأغرب `"10" > "9"` التي أعطت `false`، لأن المقارنة بين نصين حرفاً بحرف كالقاموس، والحرف `"1"` يأتي قبل `"9"`، فـ «10» أصغر من «9» كنص. وقد شرحت [وثائق Python عن الأخطاء والاستثناءات](https://docs.python.org/3/tutorial/errors.html?ref=arabroot.io) كيف تقرأ هذه الرسائل وكيف تتعامل معها في الكود، وقد خصصنا لتصحيح الأخطاء مقال [قراءة رسالة الخطأ والتصحيح Debugging](https://arabroot.io/articles/%D9%82%D8%B1%D8%A7%D8%A1%D8%A9-%D8%B1%D8%B3%D8%A7%D9%84%D8%A9-%D8%A7%D9%84%D8%AE%D8%B7%D8%A7-stack-trace-%D9%88%D8%A7%D9%84%D8%AA%D8%B5%D8%AD%D9%8A%D8%AD-debugging/).

### تعديل ثابت Constant معرف بـ const في JavaScript

```text
/lab/errors/e10_const.js:2
budget = 3500;
       ^

TypeError: Assignment to constant variable.
    at Object.<anonymous> (/lab/errors/e10_const.js:2:8)
    ...
Node.js v26.13.0
[exit 1]
```

عرفنا `budget` بـ `const` ثم حاولنا تغييره، فرفضت JavaScript. وهذا خطأ مفيد وليس عائقاً، فهو يعني أن `const` تحميك من تغيير قيمة قررت أنها ثابتة، ولذلك القاعدة الشائعة أن تعرف كل متغير بـ `const`، ولا تغيرها إلى `let` إلا عندما تحتاج فعلاً أن تغير القيمة، كما تشرح [صفحة const على MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const?ref=arabroot.io).

### الخطأ الصاخب والخطأ الصامت Silent Error

إذا جمعت كل ما سبق في جدول واحد ستجد نمطاً واضحاً:

![جدول نفس الغلطة في اللغتين: اسم مكتوب خطأ NameError مع اقتراح في Python وReferenceError في JavaScript، وعلامة = بدل == SyntaxError في Python وتنفيذ صامت يغير القيمة في JavaScript، وعنصر بعد آخر القائمة IndexError في Python وundefined في JavaScript، ودالة تطبع ولا تعيد TypeError في Python وNaN في JavaScript، ومقارنة نص برقم TypeError في Python وfalse في JavaScript، ونسيان قوس SyntaxError في اللغتين، وتعديل ثابت TypeError في JavaScript](https://arabroot.io/content/images/2026/10/programming-first-steps-07-loud-silent.webp)

في أغلب الحالات Python تتوقف وJavaScript تكمل

وليس هذا حكماً بأن إحدى اللغتين أفضل، فلكل لغة فلسفتها: Python تنص في المبادئ المعروفة باسم [The Zen of Python](https://peps.python.org/pep-0020/?ref=arabroot.io) على أن الأخطاء لا يجب أن تمر بصمت إلا إذا أسكتها المبرمج عن قصد، أما JavaScript فتحاول أن تكمل التنفيذ بأقرب قيمة تراها منطقية، وهذا مريح عندما تكون القيمة صحيحة ومكلف عندما لا تكون. والنتيجة العملية لك كمبتدئ: في Python اقرأ الرسالة، فهي غالباً تقول لك الحل. وفي JavaScript لا تثق بأن غياب الرسالة يعني أن الكود صحيح، واطبع القيم الوسيطة وتحقق منها، واستخدم `===` و`const`، وعندما تكبر مشاريعك سوف تجد أن أدوات مثل TypeScript وESLint موجودة أصلاً لتجعل أخطاء JavaScript صاخبة.

## مقارنة سريعة بين اللغتين Syntax

| الفكرة           | Python                  | JavaScript                  |
| ---------------- | ----------------------- | --------------------------- |
| تعريف متغير      | budget = 3000           | let budget = 3000; أو const |
| الطباعة          | print(x)                | console.log(x)              |
| معرفة النوع      | type(x)                 | typeof x                    |
| صح وخطأ          | True وFalse             | true وfalse                 |
| لا توجد قيمة     | None                    | undefined وnull             |
| المساواة         | \==                     | \=== (وتجنب \==)            |
| و، أو، ليس       | and or not              | && \|| !                    |
| كتلة الشرط       | نقطتان وإزاحة           | قوسان للشرط و{ } للكتلة     |
| وإلا إذا         | elif                    | else if                     |
| المرور على قائمة | for x in items:         | for (const x of items)      |
| طول القائمة      | len(items)              | items.length                |
| إضافة عنصر       | items.append(x)         | items.push(x)               |
| تعريف دالة       | def f(a, b=1):          | function f(a, b = 1) { }    |
| نص إلى رقم       | int("10") وfloat("1.5") | Number("10")                |
| إدراج قيمة في نص | f"المتبقي: {x}"         | \`المتبقي: ${x}\`           |
| التعليق          | \# تعليق                | // تعليق                    |

## قائمة التحقق قبل أن تشغل برنامجك Checklist

- هل كل متغير له اسم يشرح ما فيه، وبعرف لغتك (`snake_case` في Python و`camelCase` في JavaScript)؟
- هل حولت كل قيمة تأتي من المستخدم أو من ملف إلى النوع الصحيح قبل أن تحسب بها؟
- هل جربت كل شرط على حده بالضبط، وعلى قيمة أصغر منه وأكبر منه؟
- هل تعرف أين تتوقف كل حلقة `while`، وما المتغير الذي يتغير فيها؟
- هل جربت برنامجك على قائمة فارغة وعلى قائمة فيها عنصر واحد؟
- هل كل دالة تحسب شيئاً تعيده بـ `return`، والطباعة في مكان واحد؟
- في JavaScript: هل استخدمت `===` و`const`، وهل تحققت من عدم ظهور `NaN` أو `undefined` في المخرج؟
- عند الخطأ: هل قرأت آخر سطر في رسالة Python، أو أول سطر في رسالة Node، قبل أن تبحث في الإنترنت؟

## تدريب عملي

1. أضف إلى برنامج المصاريف دالة `biggest(items)` تعيد أكبر مصروف واسمه، بلغة واحدة أولاً ثم بالأخرى، وجربها على قائمة فارغة. ماذا قررت أن تعيد في هذه الحالة؟
2. اجعل البرنامج يقرأ الميزانية من المستخدم بـ `input()` في Python، ولاحظ أنها تصل نصاً، ثم جرب أن تكتب `3000 ريال` واقرأ الرسالة، ثم أصلحها برسالة واضحة للمستخدم.
3. غير شرط التنبيه في `status` ليكون قبل الحد بـ 10% بالضبط، واكتب ثلاث استدعاءات تختبر الحد وما قبله وما بعده.
4. خذ ملفاً من مجلد `errors` وأصلحه، ثم اكتب خطأ جديداً لم نذكره وشغله في اللغتين، وقارن الرسالتين.

## حذف المختبر Cleanup

الأمثلة عملت داخل Container بالخيار `--rm`، ويبقى بعدها الـ Image التي بنيناها والـ Images الرسمية التي نزلها Docker، واحذفها إذا لم تكن تحتاجها:

```bash
docker rmi first-steps node:26-slim python:3.13-slim
```

## الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

- المتغير اسم يشير إلى قيمة، وعلامة `=` إسناد وليست مساواة، وفي JavaScript تعرفه بـ `const` أو `let`.
- كل قيمة لها نوع، والأنواع الأساسية النص والعدد الصحيح والعشري والقيمة المنطقية، وPython تفرق بين الصحيح والعشري بينما JavaScript لها `number` واحد.
- Python ترفض خلط الأنواع برسالة خطأ، وJavaScript تحول بنفسها فيعطي `"5" + 2` النتيجة 52، واستخدم `===` دائماً في JavaScript.
- الشروط تنفذ طريقاً واحداً بالترتيب، وحد الشرط قرار يجب أن تتخذه وتختبره.
- القوائم تبدأ من الموقع 0، و`for` تمر على كل العناصر، و`while` تتوقف بشرط، والحلقة التي لا يتغير شرطها لا تنتهي.
- الدالة تأخذ معاملات وتعيد قيمة بـ `return`، والدالة التي تطبع فقط تعيد `None` أو `undefined`.
- اقرأ رسالة الخطأ قبل أن تبحث: آخر سطر في Python وأول سطر في Node، والمكان الذي يظهر فيه الخطأ ليس دائماً مكان سببه.
- في أغلب أخطاء المبتدئين Python تتوقف وJavaScript تكمل بقيمة خاطئة، والخطأ الصامت هو الأخطر.

وبعد هذا المقال تكمل المرحلة بقراءة الأخطاء وتصحيحها بالمصحح Debugger، ثم قراءة الملفات وكتابتها، ثم الاستدعاء الذاتي Recursion، وهذه مقالاتها مع مقالات مرتبطة بما رأيته هنا:

[![](https://arabroot.io/content/images/2026/10/debugging-basics-cover-cover.webp)دليلقراءة ال Stack Trace وتصحيح الأخطاء Debugging للمبتدئينكيف تقرأ رسالة الخطأ في Python وJavaScript سطراً بسطر، ومتى تستخدم الطباعة أو نقاط التوقف في VS Code، وكيف يجد git bisect التعديل الذي أدخل الخطأ.](https://arabroot.io/articles/%D9%82%D8%B1%D8%A7%D8%A1%D8%A9-%D8%B1%D8%B3%D8%A7%D9%84%D8%A9-%D8%A7%D9%84%D8%AE%D8%B7%D8%A7-stack-trace-%D9%88%D8%A7%D9%84%D8%AA%D8%B5%D8%AD%D9%8A%D8%AD-debugging/)[![](https://arabroot.io/content/images/2026/10/files-io-streams-cover-cover.webp)دليلالملفات Files والتدفقات Streams: كيف يقرأ برنامجك ملفاً؟لماذا يظهر النص العربي في ملف CSV من Excel مشوهاً، ولماذا يتوقف تقرير بسيط عندما يكبر ملف السجلات، وكيف تقرأ الملفات وتكتبها بأمان في Python وNode.js بقياسات حقيقية.](https://arabroot.io/articles/%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%AA%D8%AF%D9%81%D9%82%D8%A7%D8%AA-files-%D9%88-streams-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)[![](https://arabroot.io/content/images/2026/10/recursion-cover-cover.webp)دليلالاستدعاء الذاتي Recursion: كيف تفكر فيه ومتى تتجنبهكيف تكتب دالة تستدعي نفسها لشجرة تعليقات أو مجلدات، وما الذي يحدث في ال Stack، وإلى أي عمق تصل Python وNode.js بالقياس، ومتى تكون الحلقة هي الحل الأنسب.](https://arabroot.io/articles/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AF%D8%B9%D8%A7%D8%A1-%D8%A7%D9%84%D8%B0%D8%A7%D8%AA%D9%8A-recursion-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)[![](https://arabroot.io/content/images/2026/10/dev-environment-cover-cover.webp)دليلبيئة البرمجة للمبتدئين: المحرر VS Code والطرفية Terminalنجهز المحرر والطرفية وPython وNode على جهاز نظيف، ونفهم كل رسالة خطأ تظهر في الأيام الأولى مثل command not found وPATH وpython3 قبل أن نكتب أول برنامج.](https://arabroot.io/articles/%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-vs-code-%D9%88%D8%A7%D9%84%D8%B7%D8%B1%D9%81%D9%8A%D8%A9/)[![](https://arabroot.io/content/images/2026/10/problem-solving-thinking-cover-cover.webp)دليلكيف تفكر كمبرمج؟ التفكير الحاسوبي Computational Thinkingنحل مسألة واحدة من الفهم والتفكيك والكود الوهمي والتتبع اليدوي حتى ثلاث طرق مختبرة ومقاسة، لترى كيف يتحول سؤال غامض إلى خطوات يكتبها الحاسب دون تخمين.](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D9%81%D9%83%D8%B1-%D9%83%D9%85%D8%A8%D8%B1%D9%85%D8%AC-%D8%A7%D9%84%D8%AA%D9%81%D9%83%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8%D9%8A/)[![](https://arabroot.io/content/images/2026/10/floating-point-cover-cover.webp)دليلFloating Point من الداخل: كيف يخزن الحاسب الكسوركيف يحفظ معيار IEEE 754 الكسور في البتات، ولماذا تكبر الفجوة بين الأرقام وتضيع خانات المعرفات، ومتى تحتاج النوع العشري.](https://arabroot.io/articles/%D8%A7%D9%84%D8%A3%D8%B1%D9%82%D8%A7%D9%85-%D8%A7%D9%84%D8%B9%D8%B4%D8%B1%D9%8A%D8%A9-floating-point-%D9%85%D9%86-%D8%A7%D9%84%D8%AF%D8%A7%D8%AE%D9%84/)

## سجل التحديثات

- أكتوبر 2026: كتابة المقال، وتشغيل كل الأمثلة وملفات الأخطاء داخل Container من python:3.13-slim مع Python 3.13.17 وNode.js 26.13.0 المنسوخ من node:26-slim، على Docker Engine 29.8.0.