<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x645;&#x642;&#x627;&#x644;&#x627;&#x62A; &#x628;&#x631;&#x645;&#x62C;&#x629; &#x645;&#x62A;&#x642;&#x62F;&#x645;&#x629;</title><link>https://academy.hsoub.com/programming/advanced/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x645;&#x642;&#x627;&#x644;&#x627;&#x62A; &#x628;&#x631;&#x645;&#x62C;&#x629; &#x645;&#x62A;&#x642;&#x62F;&#x645;&#x629;</description><language>ar</language><item><title>&#x627;&#x643;&#x62A;&#x634;&#x641; &#x623;&#x641;&#x636;&#x644; &#x62A;&#x642;&#x646;&#x64A;&#x627;&#x62A; &#x62A;&#x633;&#x631;&#x64A;&#x639; &#x645;&#x648;&#x642;&#x639;&#x643; &#x627;&#x644;&#x625;&#x644;&#x643;&#x62A;&#x631;&#x648;&#x646;&#x64A;</title><link>https://academy.hsoub.com/programming/advanced/%D8%A7%D9%83%D8%AA%D8%B4%D9%81-%D8%A3%D9%81%D8%B6%D9%84-%D8%AA%D9%82%D9%86%D9%8A%D8%A7%D8%AA-%D8%AA%D8%B3%D8%B1%D9%8A%D8%B9-%D9%85%D9%88%D9%82%D8%B9%D9%83-%D8%A7%D9%84%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A-r2546/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_04/5.------.png.e0d2336850c94e0afee002474a3b76da.png" /></p>
<p>
	هل سبق لكم قضاء أسابيع من العمل المضني على بناء موقع إلكتروني وبعد نشره على الإنترنت بدا بطيئًا جدًا ولا يحقق أفضل أداء، لاشك أن بطء الموثع أمر محبط وله أثر سلبي على تجربة مستخدمي الموقع ويحول دون تحقيق الأهداف المرجوة منه ويعطي انطباعًا سيئًا عن احترافية العمل وجودة الخدمات، فضلًا عن كونه يؤثر سلبًا على ترتيب الموقع في نتائج البحث. 
</p>

<p>
	إن مشكلة بطء الموقع مشكلة شائعة يواجهها العديد من مطوري المواقع، لكن  لا داعي للقلق ففي هذا المقال سنستعرض لكم عدة تقنيات وحلول فعالة من شأنها تسريع وتحسين مواقعكم الإلكترونية، وتوفير تجربة مستخدم أفضل، وتسريع أوقات تحميل صفحات الموقع.
</p>

<h2 id="-1">
	آلية تحميل صفحة الويب
</h2>

<p>
	قبل استعرض التقنيات والحلول، نحتاج لفهم طريقة تحميل صفحات الموقع، فعندما يطلب المتصفح صفحة ويب ما، سيتلقى أولاً <a href="https://www.thedevspace.io/course/htmlcss-html-elements" rel="external nofollow">مستند HTML</a>، ثم يُحلّل هذا المستند، وعندما يعثر المحلل parser الموجود في المتصفح على ملف خارجي مرتبط بهذه الصفحة مثل ملف تنسيقات CSS أو كود جافا سكريبت أو رابط لصورة، سيرسل طلبًا جديدًا للحصول على هذا الملف.
</p>

<p>
	على سبيل المثال، لنفرض أن لدينا موقع ويب بالهيكلية التالية:
</p>

<pre class="ipsCode">.
├── index.html
├── package.json
└── statics
    ├── css
    │   ├── footer.css
    │   ├── header.css
    │   └── main.css
    ├── images
    │   ├── 1.jpg
    │   ├── 2.jpg
    │   ├── 3.png
    │   ├── 4.png
    │   └── 5.jpg
    └── js
        ├── foo.js
        ├── bar.js
        └── index.js
</pre>

<p>
	لعرض ملف index.html لهذا الموقع بشكل كامل، سيحتاج المتصفح لإرسال 12 طلب على النحو التالي:
</p>

<ul>
	<li>
		طلب واحد لتحميل ملف HTML
	</li>
	<li>
		ثلاثة طلبات لتحميل ملفات CSS
	</li>
	<li>
		ثلاثة طلبات لتحميل ملفات JavaScript
	</li>
	<li>
		خمسة طلبات لتحميل الصور 
	</li>
</ul>

<p>
	تستغرق هذه العملية وقتًا كبيرًا، وتستهلك الكثير من الموارد ما يتسبب بدوره في بطء سرعة تحميل الموقع وضعف أدائه. لحسن الحظ، هناك عدة طرق من شأنها تعزيز سرعة تحميل هذا الموقع، مثل تحسين الصور، دمج الملفات الثابتة كملفات CSS أو ملفات JavaScript المتعددة في ملف واحد لتقليل عدد الطلبات التي يرسلها المتصفح للخادم، واستخدام <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-%D9%84%D9%84%D9%88%D9%8A%D8%A8-web-caching-%D8%A7%D9%84%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r204/" rel="">التخزين المؤقت caching</a>، والعديد من التقنيات الأخرى التي سنشرحها لكم بالتفصيل  في الفقرات التالية، ولنبدأ بالصور.
</p>

<h2 id="-2">
	أولًا: تحسين الصور
</h2>

<p>
	تعد الصور من أهم العوامل التي تؤثر في سرعة تحميل مواقع الويب، وإليكم عدة نصائح وتقنيات لتحسينها.
</p>

<h3 id="-3">
	استخدام تنسيقات الصور الحديثة
</h3>

<p>
	من الأفضل استخدام التنسيقات الحديثة للصور مثل WebP و AVIF التي توفر تحسينات كبيرة في ضغط الصورة بدلًا من استخدام التنسيقات التقليدية JPG و PNG، فهذا يساعد في تقليل حجم الملفات بشكل كبير دون التأثير على الجودة. هناك العديد من الأدوات والمواقع السحابية المخصصة لتحويل الصور لهذه التنسيقات، ولكن المشكلة في هذه الأدوات أنها تتطلب رفع الملفات لمعالجتها، وبعض خدماتها غير مجانية. 
</p>

<p>
	سنستخدم في حالتنا أداة تسمى FFmpeg لتحسين الصور، حيث تتيح لنا هذه الأداة تحويل الصور محليًا باستخدام أمر بسيط. سنحتاج لتثبيت الأداة على حاسوبنا، في حال استخدام نظام التشغيل Mac سنثبتها من خلال مدير الحزم <a href="https://brew.sh/" rel="external nofollow">Homebrew</a> من خلال الأمر التالي:
</p>

<pre class="ipsCode">brew install ffmpeg
</pre>

<p>
	وفي حال استخدام نظام التشغيل ويندوز فيمكننا تثبيتها مباشرة من <a href="https://ffmpeg.org/download.html" rel="external nofollow">موقعها الرسمي</a> أو تثبيتها باستخدام <a href="https://winget.run/" rel="external nofollow">Winget</a> كالتالي:
</p>

<pre class="ipsCode">winget install --id=Gyan.FFmpeg  -e
</pre>

<p>
	بعد الانتهاء من عملية التثبيت، سنفتح <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B7%D8%B1%D9%81%D9%8A%D9%91%D8%A9-%D9%84%D9%8A%D9%86%D9%83%D8%B3-linux-terminal-r18/" rel="">الطرفية terminal</a> وننتقل إلى دليل صور موقعنا باستخدام الأمر التالي:
</p>

<pre class="ipsCode">cd /path/to/images
</pre>

<p>
	ثم نحول الصور للتنسيق webp باستخدام الأمر التالي:
</p>

<pre class="ipsCode">for file in *.{jpg,png}; do ffmpeg -i "$file" -c:v libwebp -q:v 80 "$(basename "$file" .${file##*.}).webp"; done
</pre>

<p>
	وفي حال استخدام <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر CMD</a> في ويندوز، نستخدم الأمر التالي:
</p>

<pre class="ipsCode">for %i in (*.jpg *.png) do ffmpeg -i "%i" -c:v libwebp -q:v 80 "%~ni.webp"
</pre>

<p>
	أما إذا كنا نستخدم PowerShell في ويندوز، فسنستخدم الأمر التالي:
</p>

<pre class="ipsCode">Get-ChildItem -Path . | Where-Object { $_.Extension -match '\.jpg$|\.png$' } | ForEach-Object { ffmpeg -i $_.FullName -c:v libwebp -q:v 80 ($_.BaseName + ".webp") }
</pre>

<p>
	قد نحتاج إلى تعديل الأمر ليناسب حالتنا الخاصة:
</p>

<ul>
	<li>
		<code>{jpg,png}</code> لتحويل صيغ الصور <code>jpg</code> و <code>png </code>في المجلد
	</li>
	<li>
		<code>c:v libwebp-</code> يحدد للأداة FFmpeg ما هو الترميز المستخدم لصيغة WebP. لا حاجة لتغيير هذا الخيار
	</li>
	<li>
		<code>q:v 80-</code> يحدد مستوى ضغط للصور، يمكن تعديل القيمة بين 0 لأدنى جودة وأعلى ضغط، و 100 لأعلى جودة ودون ضغط
	</li>
</ul>

<p>
	يمكن تجربة مستويات ضغط مختلفة حتى نصل لأفضل ضغط دون التأثير على جودة الصورة، لنلاحظ مثلًا الصورة التالية بتنسيقين مختلفين هما JPG وWebP.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="170038" href="https://academy.hsoub.com/uploads/monthly_2025_04/01-jpg-vs-webp.png.ec4d12d49406a3af5cc27e8b96cc6433.png" rel=""><img alt="01 jpg vs webp" class="ipsImage ipsImage_thumbnailed" data-fileid="170038" data-unique="fv2tjuh6i" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/01-jpg-vs-webp.thumb.png.3374f92f93141732f574c5ed79743711.png"> </a>
</p>

<p>
	حجم ملف JPG  هنا هو 2.7 ميجابايت، بينما حجم ملف WebP هو 176 كيلوبايت بالرغم أن كلا الصورتين لهما نفس الأبعاد 4000 × 6000 بكسل ونفس الوضوح لكن تنسيق WebP تمكن من ضغط الصورة بشكل أكبر، لذا يُعد خياراً مثالياً للاستخدام على الويب وسيساعدنا في زيادرة سرعة تحميل الصفحات.
</p>

<p>
	لا يقتصر استخدام FFmpeg على ضغط الصور بل يمكنها التعامل مع مهام عديدة متعلقة بمعالجة الصوت والفيديو والصور، لهذا تعتمد عليها العديد من المواقع والأدوات الشهيرة عالميًا مثل يوتيوب YouTube وتويتش Twitch ومشغل الوسائط VLC، لمزيد من التفاصيل حول التعامل مع هذه الأداة يمكن الرجوع إلى <a href="https://ffmpeg.org/documentation.html" rel="external nofollow">توثيقها الرسمي</a>.
</p>

<h3 id="-4">
	استخدام أحجام صور مختلفة لأحجام الشاشات المختلفة
</h3>

<p>
	عندما <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D9%88%D8%AC%D8%B2-%D8%A5%D9%84%D9%89-%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%85%D9%88%D9%82%D8%B9-%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A-r1883/" rel="">نصمم موقع ويب</a>، سيُعرَض هذا الموقع على أجهزة مختلفة كالهواتف والأجهزة اللوحية وأجهزة الحواسيب ولكل من هذه الأجهزة حجم شاشة مختلف، لذا من الضروري عرض صور متناسبة مع كل حجم شاشة لتحسين الأداء والتصميم، فإذا استخدمنا صورًا كبيرة جدًا سيكون تحميلها بطيئًا على أجهزة الهواتف، وإذا استخدمنا صورًا صغيرة جدًا فستبدو غير واضحة على الشاشات الكبيرة. يمكننا حل هذه المشكلة بإنشاء إصدارات مختلفة من نفس الصورة بحجم صغير small، ومتوسط medium، وكبير large واستخدام الحجم المناسب لكل شاشة.
</p>

<p>
	تساعدنا أداة FFmpeg التي استخدمناها سابقًا على تحقيق ذلك، بعد إنتاج إصدارات الصور المختلفة علينا تضمين الصور في صفحة الويب باستخدام العنصر <code>&lt;picture&gt;</code> بدلاً من <code>&lt;img&gt;</code>. حيث يسمح العنصر <code>&lt;picture&gt;</code> بتحديد مصادر متعددة للصورة نفسها، ويتيح للمتصفح اختيار الصورة الأنسب تلقائيًا حسب حجم الشاشة.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7727_13" style=""><span class="tag">&lt;picture&gt;</span><span class="pln">
  </span><span class="tag">&lt;source</span><span class="pln"> </span><span class="atn">media</span><span class="pun">=</span><span class="atv">"(max-width: 600px)"</span><span class="pln"> </span><span class="atn">srcset</span><span class="pun">=</span><span class="atv">"small.webp"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;source</span><span class="pln"> </span><span class="atn">media</span><span class="pun">=</span><span class="atv">"(max-width: 1200px)"</span><span class="pln"> </span><span class="atn">srcset</span><span class="pun">=</span><span class="atv">"medium.webp"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;img</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"large.webp"</span><span class="pln"> </span><span class="atn">alt</span><span class="pun">=</span><span class="atv">"example image"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;/picture&gt;</span></pre>

<p>
	إذا كان عرض الشاشة 600 بكسل أو أقل كحالة هاتف محمول ستُعرَض الصورة <code>small.webp</code>، وإذا كان العرض بين 601 بكسل و 1200 بكسل كحالة جهاز لوحي ستُعرَض الصورة <code>medium.webp</code>، وإذا لم يتحقق أي من الشروط السابقة أي كان عرض الشاشة أكبر من أكبر من 1200 بكسل فستُعرَض الصورة الصورة الافتراضية <code>large.webp.</code>
</p>

<h3 id="lazyload">
	التحميل الكسول للصور Lazy Load
</h3>

<p>
	إذا لم يكن ظهور الصورة ضروريًا عند تحميل الصفحة مباشرة، فمن الأفضل تحميل هذه الصور بشكل كسول Lazy Load، أي تحميلها فقط عندما تصبح مرئية على شاشة المستخدم، فهذا سيساعد أيضًا في تسريع تحميل الصفحة.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7727_16" style=""><span class="tag">&lt;picture&gt;</span><span class="pln">
  </span><span class="tag">&lt;source</span><span class="pln"> </span><span class="atn">media</span><span class="pun">=</span><span class="atv">"(max-width: 600px)"</span><span class="pln"> </span><span class="atn">srcset</span><span class="pun">=</span><span class="atv">"small.webp"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;source</span><span class="pln"> </span><span class="atn">media</span><span class="pun">=</span><span class="atv">"(max-width: 1200px)"</span><span class="pln"> </span><span class="atn">srcset</span><span class="pun">=</span><span class="atv">"medium.webp"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;img</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"large.webp"</span><span class="pln"> </span><span class="atn">alt</span><span class="pun">=</span><span class="atv">"example image"</span><span class="pln"> </span><span class="atn">loading</span><span class="pun">=</span><span class="atv">"lazy"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;/picture&gt;</span></pre>

<p>
	لاحظ أننا جعلنا الصورة الكبيرة <code>large.webp</code> تُحمّل بشكل كسول من خلال ضبط الخاصية <code>loading</code> بالقيمة <code>"lazy"</code> في الوسم <code>&lt;img&gt;</code> لضمان أن المتصفح سيحمّل هذه الصورة فقط عندما يمرر المستخدم لأسفل ويصل لموقع هذه الصورة. لكن إذا كانت الصورة جزءًا أساسيًا من تصميم الصفحة وكنا نحتاج لعرضها فورًا عند تحميل الصفحة فمن الأفضل ضبط الخاصية <code>loading</code> بالقيمة <code>"eager"</code>. لجعل المتصفح يحمّلها على الفور دون انتظار.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7727_18" style=""><span class="tag">&lt;picture&gt;</span><span class="pln">
  </span><span class="tag">&lt;source</span><span class="pln"> </span><span class="atn">media</span><span class="pun">=</span><span class="atv">"(max-width: 600px)"</span><span class="pln"> </span><span class="atn">srcset</span><span class="pun">=</span><span class="atv">"small.webp"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;source</span><span class="pln"> </span><span class="atn">media</span><span class="pun">=</span><span class="atv">"(max-width: 1200px)"</span><span class="pln"> </span><span class="atn">srcset</span><span class="pun">=</span><span class="atv">"medium.webp"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;img</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"large.webp"</span><span class="pln"> </span><span class="atn">alt</span><span class="pun">=</span><span class="atv">"example image"</span><span class="pln"> </span><span class="atn">loading</span><span class="pun">=</span><span class="atv">"eager"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;/picture&gt;</span></pre>

<p>
	بعد أن استعرضنا طرق نختلفة لتحسين سرعة الموقع من خلال تحسين الصور، لنشرح تقنيات تحسين الملفات الثابتة للموقع.
</p>

<h2 id="css">
	ثانيًا: تحسين ملفات CSS وجافا سكريبت
</h2>

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

<h3 id="css-1">
	دمج ملفات CSS وتقليل حجمها
</h3>

<p>
	تتوفر عدة أدوات لتحقيق هذا الأمر مثل PostCSS فهو معالج CSS يمكنه دمج ملفات التنسيقات، وتقليل حجم كودها. كما يمكن تعزيزه بإضافات plugins مساعدة لتحسين الكود وحل مشكلات <a href="https://academy.hsoub.com/programming/html/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%84%D9%84%D8%AA%D9%88%D8%A7%D9%81%D9%82-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D9%81%D9%8A-html-%D9%88-css-r1955/" rel="">التوافق مع المتصفحات</a>.
</p>

<p>
	غالبًا ما يكون PostCSS جزءًا من أدوات تسمى مُجمِّعات الويب web bundlers تستخدم لتجميع ملفات الموقع المختلفة وسنتحدث عنها بالتفصيل لاحقًا، ومع ذلك، يمكننا استخدام PostCSS بشكل مستقل وتثبيته في مشروعنا باستخدام مدير الحزم <a href="https://www.npmjs.com/" rel="external nofollow">npm</a> كالتالي:
</p>

<pre class="ipsCode">npm install postcss postcss-cli postcss-import postcss-preset-env cssnano --save-dev
</pre>

<p>
	بعدها علينا إنشاء ملف إعدادات PostCSS باسم <code>postcss.config.js</code> في الدليل الجذر لمشروعنا، ونحدد فيه ما هي الإضافات plugins التي نريد استخدامها لتحسين الكود.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7727_23" style=""><span class="pln">module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  plugins</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    require</span><span class="pun">(</span><span class="str">"postcss-import"</span><span class="pun">),</span><span class="pln">
    require</span><span class="pun">(</span><span class="str">"postcss-preset-env"</span><span class="pun">),</span><span class="pln">
    require</span><span class="pun">(</span><span class="str">"cssnano"</span><span class="pun">),</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	استخدمنا هنا الإضافات التالية:
</p>

<ul>
	<li>
		<code>postcss-import</code> لدمج ملفات CSS متعددة داخل ملف واحد
	</li>
	<li>
		<code>postcss-preset-env</code> لاستخدام خصائص CSS الحديثة والتأكد من توافقها مع المتصفحات
	</li>
	<li>
		<code>cssnano</code> لتقليص حجم ملفات CSS
	</li>
</ul>

<p>
	بعدها ننشئ ملف CSS رئيسي باسم <code>styles.css</code> كي يستورد جميع التنسيقات الأخرى الضرورية:
</p>

<pre class="ipsCode">├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
└── statics
    ├── css
    │   ├── footer.css
    │   ├── header.css
    │   ├── main.css
    │   └── styles.css
    ├── images
    └── js
</pre>

<p>
	يستورد الملف <code>styles.css</code> كافة ملفات التنسيقات كالتالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_7727_27" style=""><span class="kwd">@import</span><span class="pln"> </span><span class="str">"./header.css"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">@import</span><span class="pln"> </span><span class="str">"./main.css"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">@import</span><span class="pln"> </span><span class="str">"./footer.css"</span><span class="pun">;</span></pre>

<p>
	بعدها ندمج الملفات معًا في هذا الملف، ونضعه في المجلد <code>dist/</code> ونصغّر حجمه باستخدام الأمر التالي:
</p>

<pre class="ipsCode">npx postcss statics/css/styles.css -o dist/styles.css
</pre>

<p>
	وبهذا سيكون الملف جاهزًا للاستخدام في الموقع، كل ما علينا هو استيراد الملف <code>dist/styles.css</code> الناتج في مستند HTML كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7727_29" style=""><span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"viewport"</span><span class="pln"> </span><span class="atn">content</span><span class="pun">=</span><span class="atv">"width=device-width, initial-scale=1.0"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">Document</span><span class="tag">&lt;/title&gt;</span><span class="pln">
    </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"dist/styles.css"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    . . .
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	كما يمكننا بدلاً من تجميع كافة ملفات CSS في ملف واحد، تقسيمها إلى نوعين من الملفات: الأول يتضمن تنسيقات CSS الأساسية، والثاني يتضمن CSS غير الأساسية. ثم نُحمّل تنسيقات CSS الأساسية أولًا في القسم head لكونها ضرورية لتنسيق الصفحة بشكل صحيح فورًا. وتأجيل تحميل تنسيقات CSS الثانوية للقسم body بعد تحميل كامل محتوى الصفحة، وبهذا نساعد على تحسين سرعة الصفحة.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7727_31" style=""><span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"viewport"</span><span class="pln"> </span><span class="atn">content</span><span class="pun">=</span><span class="atv">"width=device-width, initial-scale=1.0"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">Document</span><span class="tag">&lt;/title&gt;</span><span class="pln">
    </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"dist/critical.css"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    . . .
    </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"dist/non-critical.css"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<h3 id="webbundlers">
	مُجمِّعات الويب web bundlers
</h3>

<p>
	أصبحت تطبيقات الويب اليوم أكثر تعقيدًا. لذا لا يمكننا الاعتماد فقط على معالج CSS بسيط لتحسين كل شيء. ويفضل الاعتماد على مجمعات حزم الويب المتكاملة، حيث توفر لنا عدة أدوات مساعدة لتحسين جميع أجزاء الموقع من ملفات CSS وملفات جافا سكريبت وصور، كما أنها تساعدنا على تنظيم وإدارة كل الملفات التي يحتاجها الموقع من مكان واحد. لنتعرف على أهم هذه المجمعات.
</p>

<h4 id="webpack">
	الأداة Webpack
</h4>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="170039" href="https://academy.hsoub.com/uploads/monthly_2025_04/02-webpack.png.837e647129b6587ca98d958cdfe3721b.png" rel=""><img alt="02 webpack" class="ipsImage ipsImage_thumbnailed" data-fileid="170039" data-unique="njv7bt55w" src="https://academy.hsoub.com/uploads/monthly_2025_04/02-webpack.png.837e647129b6587ca98d958cdfe3721b.png"> </a>
</p>

<p>
	يعد Webpack أداة مشهورة حاصلة على أكثر من 63 ألف نجمة على GitHub، فهو يساعدنا على تجميع ملفات مختلفة من أنواع متعددة مثل JavaScript و CSS في ملفات مُجمعّة يمكن للمتصفح تنفيذها بسرعة، كما يوفر لنا أدوات لمعالجة الصور. يمكن تثبيت Webpack في مشروعنا كما يلي:
</p>

<pre class="ipsCode">npm install webpack webpack-cli --save-dev
</pre>

<p>
	لنفترض أن لدينا هيكل المشروع التالي، ولنوضح بشكل مبسط كيفية عمل Webpack في تجميع ملفات جافا سكريبت لهذا المشروع:
</p>

<pre class="ipsCode">.

├── dist
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js   # إعدادات PostCSS
├── statics
│   ├── css
│   ├── images
│   └── js
│       ├── bar.js
│       ├── foo.js
│       └── index.js
└── webpack.config.js #  إعدادات Webpack
</pre>

<p>
	لدينا ثلاث ملفات جافا سكريبت في مشروعنا وهي: الملف <code>foo.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7727_33" style=""><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> foo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"foo"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	والملف <code>bar.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7727_35" style=""><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> bar</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"bar"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	والملف <code>index.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7727_37" style=""><span class="kwd">import</span><span class="pln"> foo from </span><span class="str">"./foo.js"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> bar from </span><span class="str">"./bar.js"</span><span class="pun">;</span><span class="pln">

foo</span><span class="pun">();</span><span class="pln">
bar</span><span class="pun">();</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Hello, World!"</span><span class="pun">);</span></pre>

<p>
	في هذه الحالة، سيكون الملف <code>index.js</code> هو نقطة الدخول لمشروعنا، أي سنحمّل هذا الملف أولاً ومن ثم نتبعه بتحميل الملفات المستوردة داخله. ولإنشاء ملف إعدادات Webpack، علينا إنشاء ملف <code>webpack.config.js</code> في الدليل الجذر لموقعنا، ونتأكد من أن خيار <code>entry</code> يشير إلى ملف <code>index.js</code>. سيكون ملف الإعدادات <code>webpack.config.js</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7727_39" style=""><span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"path"</span><span class="pun">);</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  entry</span><span class="pun">:</span><span class="pln"> </span><span class="str">"./statics/js/index.js"</span><span class="pun">,</span><span class="pln">
  output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    filename</span><span class="pun">:</span><span class="pln"> </span><span class="str">"bundle.js"</span><span class="pun">,</span><span class="pln">
    path</span><span class="pun">:</span><span class="pln"> path</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">"dist"</span><span class="pun">),</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	الآن عند تنفيذ Webpack، سيبدأ بالبحث عن الملف <code>index.js</code>، ثم عن الملفين <code>foo.js</code> و <code>bar.js</code> ويجمع كل الأكواد في ملف واحد يسمى <code>bundle.js</code>. بعدها نستخدم الأمر التالي لتشغيل Webpack :
</p>

<pre class="ipsCode">npx webpack --config webpack.config.js
</pre>

<p>
	سيؤدي تنفيذ هذا الأمر لإنشاء ملف <code>bundle.js</code> يحتوي النسخة المُصغّرة من جميع أكواد جافا سكريبت.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7727_41" style=""><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"use strict"</span><span class="pun">;</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"foo"</span><span class="pun">),</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"bar"</span><span class="pun">),</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Hello, World!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">})();</span></pre>

<p>
	يعالج Webpack ملفات جافا سكريبت فقط، ولكن يمكننا توسيع إمكانياته بتثبيت أدوات تحميل loaders مختلفة. على سبيل المثال، عند تحميل css-loader في Webpack سنتمكن من معالجة ملفات CSS، وعند تحميل postcss-loader ستوافق Webpack مع معالج PostCSS الذي استخدمناه سابقًا.
</p>

<p>
	إضافة إلى Webpack، هناك العديد من أدوات تجميع الويب الشهيرة الأخرى نذكر منها Parcel و Esbuild و Rollup وكل منها يمتلك ميزات وخصائص فريدة. يعود اختيار الأداة المناسبة لاحتياجاتنا ومتطلبات مشروعنا.
</p>

<h4 id="vite">
	الأداة Vite
</h4>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="170040" href="https://academy.hsoub.com/uploads/monthly_2025_04/03-vite.png.2b7429797afd1b7583ecefd5880670e3.png" rel=""><img alt="03 vite" class="ipsImage ipsImage_thumbnailed" data-fileid="170040" data-unique="7bywm4xiv" style="width: 400px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/03-vite.thumb.png.a16cd8cce130dfffabb5616fcd0dc1b2.png"> </a>
</p>

<p>
	بالحديث عن <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A3%D9%85%D8%A7%D9%85%D9%8A%D8%A9-frontend-web-development/" rel="">أدوات تطوير الواجهة الأمامية</a>، لا يمكننا تجاهل الحديث عن Vite فهي أداة بناء واجهات أمامية سريع مصممة لتحسين وتسريع تطوير التطبيقات، وهي سريعة وموثوقة وتستحق التجربة.
</p>

<p>
	تعمل Vite بشكل مختلف عن الأدوات التقليدية مثل Webpack وتمكننا من بعض التحديات التي قد تواجهنا عند بناء تطبيقات كبيرة ومعقدة. فمع تزايد تعقيد تطبيقات الويب، قد نجد أن التطبيق الواحد يحتوي على مئات أو حتى آلاف الوحدات وسيستغرق Webpack وقتًا طويلاً لمعالجتها قبل بدء عمل الخادم. 
</p>

<p>
	تحل Vite هذه المشكلة من خلال دعم تقنية استبدال الوحدة أثناء التشغيل Hot Module Replacement أو HMR اختصارًا، التي تسمح للمطورين بتطبيق التحديثات فورًا في المتصفح دون الحاجة إلى تحديث الصفحة بالكامل. كما تتبع Vite نهجًا فريدًا في تجميع الملفات فبدلاً من تجميع كل شيء معًا، فإنها تنشئ حزمًا أصغر لكل وحدة فردية، ثم تقدمها للمتصفح حسب الحاجة. تسمح هذه الطريقة بتحميل الصفحات بسرعة أكبر.
</p>

<h3 id="asyncdefer">
	الخاصيتان async و defer
</h3>

<p>
	أصبحت ملفات جافا سكريبت اليوم أكثر تعقيدًا وأكبر حجمًا، وغالبًا ما تكون أكبر من ملف HTML نفسه، وهي تؤخر عرض المحتوى لكونها تستغرق وقتًا أطول لتحميلها ومعالجتها، حتى عندما نعمل على دمجها وتصغير حجمها. فعندما يفتح المتصفح صفحة ويب، فإنه يبدأ افتراضيًا بقراءة ملف HTML سطرًا سطرًا. وعندما يصل إلى كود أو ملف جافا سكريبت سيتوقف عن قراءة الصفحة مؤقتًا ليحمّل هذا الكود وينفذه، ثم يعود لاستكمال قراءة باقي الصفحة بعد الانتهاء منه أي أن تحميل أكواد جافا سكريبت يجري على التسلسل كما توضح الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="170041" href="https://academy.hsoub.com/uploads/monthly_2025_04/04-sync.excalidraw.png.2bffa2f64522ae6a5884bb8ae17d60db.png" rel=""><img alt="04 مخطط تحميل ملفات الصفحة" class="ipsImage ipsImage_thumbnailed" data-fileid="170041" data-ratio="18.71" data-unique="j1d1r7n0b" style="width: 700px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_04/04-sync.excalidraw.thumb.png.b182c65c156a9afb6e315cb08742b688.png"></a>
</p>

<p>
	لحل هذه المشكلة، يمكننا استخدام الخاصيتين <code>defer</code> أو <code>async</code> لتحميل ملفات جافا سكريبت على التوازي، أي دون توقف المتصفح عن تحليل باقي محتويات الصفحة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7727_49" style=""><span class="pun">&lt;</span><span class="pln">script src</span><span class="pun">=</span><span class="str">"path/to/script.js"</span><span class="pln"> </span><span class="kwd">async</span><span class="pun">&gt;&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">script src</span><span class="pun">=</span><span class="str">"path/to/script.js"</span><span class="pln"> defer</span><span class="pun">&gt;&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	كلا الخيارين يوجهان المتصفح لتحميل كود جافا سكريبت في الخلفية. ويكمن الاختلاف بينهما هو أن الخاصية <code>async</code> تخبر المتصفح بتنفيذ هذا الكود فور تحميله، بينما تخبر الخاصية <code>defer</code> المتصفح بأن ينتظر حتى يكتمل تحليل الصفحة قبل تنفيذه.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="170042" href="https://academy.hsoub.com/uploads/monthly_2025_04/05-async-vs-defer.excalidraw.png.7af8b865c80a85015e9b8978a7beae7a.png" rel=""><img alt="05 مخطط يوضح الفرق بين async و defer " class="ipsImage ipsImage_thumbnailed" data-fileid="170042" data-ratio="43.71" data-unique="qvwxip7i3" style="width: 700px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_04/05-async-vs-defer.excalidraw.thumb.png.89c0c4beac89df0d58a5bfb2932ed6be.png"></a>
</p>

<p>
	في حال وجود عدة أكواد جافا سكريبت في صفحة الويب مع استخدام الخاصية <code>defer</code> فإنها ستحمّل جميع هذه الأكواد في الخلفية، ثم ستنفّذ بالترتيب الذي ظهرت به في الكود، أما إذا استخدمنا الخاصية <code>async</code> فسينفّذ السكربت الذي انتهى تحميله أولاً بغض النظر عن ترتيبه.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="170043" href="https://academy.hsoub.com/uploads/monthly_2025_04/06-async-vs-defer-multi.excalidraw.png.f9a542e23cc3d3ba289137c0f01eb3b4.png" rel=""><img alt="الفرق بين async و defer عند وجود عدة ملفات جافا سكريبت" class="ipsImage ipsImage_thumbnailed" data-fileid="170043" data-ratio="42.93" data-unique="u9jrchmsm" style="width: 750px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_04/06-async-vs-defer-multi.excalidraw.thumb.png.373396df4fe605a4f9149deb2fa1c78b.png"></a>
</p>

<p>
	بشكل عام، إذا كان السكريبت يعتمد على تحميل <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B4%D8%AC%D8%B1%D8%A9-dom-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2360/" rel="">شجرة DOM</a> بالكامل، فيجب استخدام الخاصية <code>defer</code>، التي تنفذ الملف بعد اكتمال التحليل، وكذلك إذا كان السكربت يعتمد على سكربتات أخرى يجب تنفيذها بالترتيب فسنستخدم الخاصية<code>defer</code> أيضًا لأنها تنفذ السكربتات بنفس ترتيب ظهورها في الصفحة. أما إذا كان السكريبت يحتاج إلى التنفيذ في وقت أسرع حتى لو لم تكتمل الصفحة بالكامل، فيجب استخدام الخاصية <code>async</code>.
</p>

<p>
	بهذا نكون قد ناقشنا عدة طرق لتسريع موقع الويب من خلال تحسين الملفات الثابتة، لنشرح تقنيات أخرى لتحسين سرعة الموقع من خلال تلميحات الموارد.
</p>

<h2 id="-5">
	ثالثًا: تعزيز أداء صفحة الويب باستخدام تلميحات الموارد
</h2>

<p>
	تسهم تلميحات الموارد Resource hints في تسريع صفحة الويب من خلال إخبار المتصفح بكيفية التعامل مع ملفات الموارد التي نحتاجها فورًا في الصفحة الحالية والموارد التي قد نحتاجها لاحقًا. ففي بعض الأحيان، نحتاج لتنزيل مورد معين في أسرع وقت ممكن، وقد نحتاج لتنزيل مورد آخر في وقت متأخر، لذا يمكننا تنزيله في الخلفية.
</p>

<p>
	تسمح لنا تلميحات الموارد بإخبار المتصفح بكيفية معالجة هذه الموارد وذلك باستخدام وسم <code>&lt;link&gt;</code> الذي نضعه في قسم الترويسة <code>head</code> من ملف HTML، هناك العديد من تلميحات الموراد المفيدة والتي سنشرحها تاليًا.
</p>

<h3 id="dnsprefetch">
	التلميح dns-prefetch
</h3>

<p>
	يساهم التلميح <code>dns-prefetch</code> في تحسين أداء الصفحة عن طريق إجراء عملية بحث مسبق لنظام أسماء النطاقات DNS لمورد خارجي قد يحتاجه المتصفح في المستقبل. فعندما يستخدم المتصفح هذا التلميح سيبدأ في معالجة اسم النطاق ويحوله لعنوان IP مناسب في الخلفية، مما يسرع الاتصال بالخادم عند الحاجة إليه لاحقًا، لاحظ الكود التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7727_57" style=""><span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"viewport"</span><span class="pln"> </span><span class="atn">content</span><span class="pun">=</span><span class="atv">"width=device-width, initial-scale=1.0"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">Document</span><span class="tag">&lt;/title&gt;</span><span class="pln">

    </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"dns-prefetch"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"https://style.example.com"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    . . .
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	يوضح الكود أعلاه مثالًا على تلميح مورد من نوع <code>dns-prefetch</code>. فعندما يزور المتصفح موقعًا إلكترونيًا، فإنه يذهب أولاً إلى خادم DNS الذي يشبع دليل الهاتف على الإنترنت فهو يربط النطاقات بعناوين IP. يذهب المتصفح إلى خادم DNS مع النطاق، ثم يعيد خادم DNS عنوان IP المقابل، وعندها يمكن للمتصفح الاتصال بذلك العنوان ويطلق على هذه العملية اسم بحث DNS.
</p>

<p>
	من خلال تلميح المورد هنا سيعرف المتصفح بأن المستخدم قد يقوم ببعض الإجراءات التي ستتطلب منه البحث عن عنوان الـ IP الخاص بـالنطاق <code><a href="https://style.example.com" ipsnoembed="true" rel="external nofollow">https://style.example.com</a></code>. وبالنتيجة، يبدأ المتصفح في حل اسم النطاق في أقرب وقت ممكن ويخزن النتيجة محليًا. يسمح هذا التخزين المؤقت للمتصفح بسحب عنوان IP مباشرة من الذاكرة المؤقتة عندما يطلبه المستخدم.
</p>

<h3 id="preconnect">
	التلميح preconnect
</h3>

<p>
	يستخدم التلميح <code>preconnect</code> لتحسين أداء تحميل الصفحة عن طريق إنشاء اتصال مسبق مع الخادم قبل أن يحتاج المتصفح إلى تحميل أي موارد من هذا الخادم.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4071_9" style=""><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"preconnect"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"https://style.example.com"</span><span class="pln"> </span><span class="tag">/&gt;</span></pre>

<p>
	سيطلب التلميح <code>preconnect</code> من المتصفح الاتصال بالخادم المضيف ويسمى هذا الأمر مصافحة TCP وهي تجري بعد اكتمال بحث DNS. فمصافحة TCP هي عملية إنشاء اتصال بين المتصفح والخادم قبل إرسال أي بيانات وهي تطلب تبادل بعض المعلومات بين المتصفح والخادم، وتحدث مرة واحدة لكل خادم ولكنها قد تستغرق وقتًا طويلاً إذا كانت الشبكة بطيئة، وهنا يأتي دور <code>preconnect</code> الذي يتولى إتمام هذه المصافحة مبكرًا، مما يقلل الوقت الذي يستغرقة المتصفح لإنشاء الاتصال الفعلي عند الحاجة إلى الموارد لاحقًا.
</p>

<h3 id="preload">
	التلميح preload
</h3>

<p>
	يخبر التلميح <code>preload</code> المتصفح بكيفية تحميل الموارد لصفحة الويب الحالية.
</p>

<pre class="ipsCode">&lt;link rel="preload" href="path/to/script.js" as="script" /&gt;
</pre>

<p>
	تٌعطى الموارد المعرفة بـ <code>href</code> أولوية عالية وتُحمّل في أسرع وقت ممكن. كما يسمح تلميح<code> preload</code> بتحديد خاصية إضافية تسمى <code>as</code> كي نحدد نوع المورد ونوضح بأنه يتطلب ترويسات طلب request headers مختلفة أو سياسات أمان مختلفة. 
</p>

<p>
	على سبيل المثال قد تحتاج الموارد من نوع <code>script</code> مثل أكواد جافا سكريبت لطريقة تعامل مختلف عن الموارد من نوع <code>style</code> مثل أكواد CSS أو الموارد من نوع <code>image</code> لضمان الأمان ومنع التعارضات. ولتحقيق أفضل تجربة مستخدم، يمكن تحديد الموارد الأساسية مثل تنسيقات CSS و أكواد جافا سكريبت الأساسية والخطوط والصور المهمة باستخدام التلميح <code>preload</code> لتحميلها بأولوية عالية عند التحميل الأولي للصفحة.
</p>

<h3 id="prefetch">
	التلميح prefetch
</h3>

<pre class="ipsCode">&lt;link rel="prefetch" href="path/to/style.css" as="style" /&gt;
</pre>

<p>
	يشبه التلميح <code>prefetch</code> التلميح <code>preload</code>، إلا أنه يحدد الموارد التي قد يحتاجها المستخدم لاحقًا، وبالتالي يعيّن المتصفح أولوية منخفضة لهذه الموارد، ويحمّلها بعد تحميل الصفحة، ستُخزّن الملفات المحملة في ذاكرة <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-%D9%84%D9%84%D9%88%D9%8A%D8%A8-web-caching-%D8%A7%D9%84%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-r204/" rel="">التخزين المؤقت cache</a> وتسترجع عندما يطلبها المستخدم.
</p>

<h3 id="prerender">
	التلميح prerender
</h3>

<p>
	يخبر التلميح <code>prerender</code> المتصفح بتحميل الصفحة التالية وجميع الموارد المرتبطة بها، بالإضافة إلى عرضها في الخلفية، وعندما يطلب المستخدم تلك الصفحة يسحبها من الخلفية لعرضها فورًا.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4071_13" style=""><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"prerender"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"next_page.html"</span><span class="pln"> </span><span class="tag">/&gt;</span></pre>

<h2 id="cdn">
	رابعًا: استخدم شبكة توصيل المحتوى CDN
</h2>

<p>
	<a href="https://academy.hsoub.com/devops/networking/%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%AA%D9%88%D9%89-content-distribution-networks-r569/" rel="">شبكة توصيل المحتوى CDN</a> هي مجموعة من الخوادم المنتشرة في جميع أنحاء العالم، تُخزّن نسخًا مؤقتة للموقع الإلكتروني، وعندما يطلب المستخدم صفحة ويب، عندها بدلاً من الاتصال بخادم الاستضافة الخاص بالموقع، ستنقل البيانات من أقرب خادم CDN إلى المستخدم. 
</p>

<p style="text-align: center;">
	<img alt="07 شبكة توصيل المحتوى CDN" class="ipsImage ipsImage_thumbnailed" data-fileid="170044" data-ratio="86.75" data-unique="b2gx81hit" style="width: 400px; height: auto;" width="554" src="https://academy.hsoub.com/uploads/monthly_2025_04/07-cdn.excalidraw.png.4dcb412815954abc3e748b85c09ee7f8.png">
</p>

<p>
	تساهم شبكة توصيل المحتوى CDN في تحسين سرعة تحميل الموقع الإلكتروني وتحسين تجربة المستخدم وتقليل تكاليف النطاق الترددي Bandwidth، حيث تُنقل البيانات من خادم CDN بدلاً من خادم الاستضافة، منا يساهم استخدام CDN في <a href="https://academy.hsoub.com/devops/security/%D9%83%D9%8A%D9%81-%D9%86%D8%AE%D9%81%D9%81-%D9%85%D9%86-%D9%87%D8%AC%D9%85%D8%A7%D8%AA-ddos-%D8%B6%D8%AF-%D9%85%D9%88%D9%82%D8%B9%D9%86%D8%A7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-cloudflare-r136/" rel="">حماية خادم الموقع من الهجمات الأمنية </a>DDoS ويعزز أمانه.
</p>

<p>
	هناك العديد من الشركات التي توفر خدمات CDN مثل CloudFlare و Amazon CloudFront و Google Cloud CDN، وتختلف عملية إعداد CDN حسب الشركة التي نختارها ويمكن الرجوع إلى الوثائق الرسمية لموفر الخدمة وإعدداها وفق ما يناسبنا.
</p>

<h2 id="-6">
	خامسًا: إعداد التخزين المؤقت Caching
</h2>

<p>
	يمكننا إضافة لتخزين صفحات الويب الخاصة بنا باستخدام شبكة توصيل المحتوى CDN، مع الانتباه لجزئية تقريب المحتوى أكثر من المستخدمين من خلال التخزين المؤقت للموارد محليًا مثل ملفات الصور وملفات جافاسكريبت وملفاتCSS بحيث نصل إليها بسرعة أكبر ونسرع تحميل الموقع في الزيارات التالية له.
</p>

<p>
	كما يحتوي مستند HTML على قسم <code>&lt;head&gt;</code>، الذي يستخدم لتخزين البيانات الوصفية metadata للصفحة، يحتوي كل طلب HTTP واستجابة HTTP أيضًا على ترويسة header وهي جزء من البيانات التي ترسل مع كل طلب أو استجابة بين المتصفح والخادم. ويمكن استخدام الترويسة <code>Cache-Control</code> في HTTP لإخبار المتصفح بكيفية تخزين الموارد مؤقتًا.
</p>

<p>
	قد توجد طرق مختلفة لإضافة رؤوس HTTP مخصصة اعتمادًا على كيفية استضافة موقعنا الإلكتروني، على سبيل المثال إذا كنا نستخدم منصة سحابية لاستضافة موقعنا مثل  AWS Amplify، فيمكن إضافة هذه الرؤوس المخصصة في ملف إعدادات التطبيق <code>customHttp.yml</code> لزيادة سرعة تحميل الصفحات. كل ما علينا هو الانتقال إلى إعدادات التطبيق App settings ثم اختيار الرؤوس المخصصة custom headers وتحرير الملف <code>customHttp.yml</code> ليشمل إعدادات التخزين المؤقت المناسبة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-xml prettyprinted" id="ips_uid_4071_22" style=""><span class="pln">customHeaders:
  - pattern: '**/*.js'
    headers:
      - key: Cache-Control
        value: 'public,max-age=31536000,s-maxage=86400,immutable'
  - pattern: '**/*.css'
    headers:
      - key: Cache-Control
        value: 'public,max-age=31536000,s-maxage=86400,immutable'
  . . .</span></pre>

<p>
	في هذا المثال سنخزن ملفات جافا سكريبت و CSS في الذاكرة المؤقتة لزيادة سرعة تحميل الموقع وفق التالي:
</p>

<ul>
	<li>
		تعني <code>public</code> أن الملف يمكن تخزينه في ذاكرة التخزين المؤقت العامة مثل شبكة توصيل المحتوى CDN
	</li>
	<li>
		يحدد <code>max-age=31536000 </code>المدة التي يمكن فيها تخزين الملف في ذاكرة التخزين المؤقت للمتصفح مقدرة بالثواني
	</li>
	<li>
		يحدد <code>s-maxage=86400 </code>المدة التي يمكن فيها تخزين الملف في ذاكرة التخزين المؤقت العامة مثل CDN مقدرة بالثواني 
	</li>
	<li>
		تعني <code>immutable </code>أن محتوى الملف لن يتغير مع مرور الوقت فإذا كان لدينا ملف .htaccess في موقعنا، يمكن استخدام الأداة <a href="https://www.htaccessredirect.net/cache-files" rel="external nofollow">.htaccess Generator</a> لإنشاء الكود المناسب بدلاً من كتابته يدويًا
	</li>
</ul>

<h2 id="googleanalytics">
	سادسًا: استخدام بدائل Google Analytics
</h2>

<p>
	يمكن اللجوء إلى بدائل الجيدة مفتوحة المصدر لتحليلات جوجل Google Analytics والتي توفر أكواد تتبع أصغر بكثير مثل Plausible و Matomo و Fathom و Simple Analytics وغيرها. فعظم هذه الخيارات مفتوحة المصدر ويمكن استضافتها ذاتيًا. على سبيل المثال، يبلغ حجم كود Plausible كيلو بايت واحد فقط، بينما يبلغ حجم كود Google Analytics أكثر من 300 كيلو بايت. لكن وبالرغم من كون هذه الخيارات جيدة جدًا ومفتوحة المصدر، إلا أن Google Analytics قد يوفر بعض الميزات الفريدة التي لا تقدمها البدائل. لذا ننصحكم بتجربتها أولاً ومعرفة ما إذا كانت تلبي متطلباتكم بالفعل قبل الاعتماد عليها لتسريع الموقع.
</p>

<p>
	كما قد يختار الكثير من المطورين استخدام نظام إدارة محتوى احترافي مثل <a href="https://academy.hsoub.com/apps/web/wordpress/" rel="">ووردبريس</a> أو Wix عند بناء موقعهم الإلكتروني الأول وتتضمن هذه الأنظمة الكثير من الميزات غير الضرورية للموقع، فإذا كان كل ما نحتاجه هو بناء <a href="https://academy.hsoub.com/programming/general/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%B4%D8%A7%D9%85%D9%84%D8%A9-%D8%A8%D9%8A%D9%86-%D9%85%D8%AF%D9%88%D9%86%D8%A7%D8%AA-blogger-%D9%88%D9%88%D8%B1%D8%AF%D8%A8%D8%B1%D9%8A%D8%B3-r65/" rel="">مدونة</a> شخصية بسيطة أو <a href="https://academy.hsoub.com/freelance/personal-branding/%D9%86%D8%B5%D8%A7%D8%A6%D8%AD-%D9%84%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%85%D8%B9%D8%B1%D8%B6-%D8%A3%D8%B9%D9%85%D8%A7%D9%84-%D8%AC%D8%B0%D9%91%D8%A7%D8%A8-r10/" rel="">معرض أعمال</a>، فمن الأفضل استخدام مولد مواقع ثابتة مثل <a href="https://academy.hsoub.com/devops/servers/web/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D8%B5%D9%8A%D8%A8-%D9%88%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-hugo%D8%8C-%D9%85%D9%88%D9%91%D9%84%D8%AF-%D8%A7%D9%84%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D8%A5%D8%B3%D8%AA%D8%A7%D8%AA%D9%8A%D9%83%D9%8A%D8%A9-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-r292/" rel="">Hugo</a> أو <a href="https://academy.hsoub.com/programming/ruby/%D9%85%D8%AF%D9%88%D9%86%D8%AA%D9%83-%D8%A7%D9%84%D8%AE%D8%A7%D8%B5%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-jekyll-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r39/" rel="">Jekyll</a> بدلًا من الخيارات السابقة.
</p>

<p>
	ينشئ ووردبريس على سبيل المثال موقعًا ديناميكيًا متكاملًا يتضمن الواجهة الأمامية والخلفية وقاعدة البيانات وعندما يطلب المستخدم صفحة من هذا الموقع ستجلب الواجهة الخلفية البيانات اللازمة من قاعدة البيانات، وتُجمّعها في صفحة ويب، ثم ستعرض في الواجهة الأمامية للمستخدم.
</p>

<p style="text-align: center;">
	<img alt="08 مخطط CMS" class="ipsImage ipsImage_thumbnailed" data-fileid="170045" data-ratio="92.00" data-unique="6g7b56q5j" style="width: 400px; height: auto;" width="575" src="https://academy.hsoub.com/uploads/monthly_2025_04/08-cms.excalidraw.png.07db6f6ae1de3ec5cba4b5b0ab5d5027.png">
</p>

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

<p style="text-align: center;">
	<img alt="09 مخطط site-generator" class="ipsImage ipsImage_thumbnailed" data-fileid="170046" data-ratio="32.80" data-unique="m1q1lpzhd" style="width: 500px; height: auto;" width="600" src="https://academy.hsoub.com/uploads/monthly_2025_04/09-site-generator.excalidraw.png.90d6ee73674c934c815ea6266368f65f.png">
</p>

<p>
	لكن العيب في مولدات المواقع الثابتة هو أنها صعبة التعلم في البداية. فهي لا تحتوي على ميزات السحب والإفلات السهلة كتلك الموجودة في ووردبريس و Wix وتحتاج إلى امتلاك بعض المهارات البرمجية لتخصيص تصميم الموقع.
</p>

<h2 id="-7">
	الخاتمة
</h2>

<p>
	بهذا نكون وصلنا لنهاية مقالنا الشامل الذي شرحنا فيه العديد من التقنيات والأدوات والطرق المساعدة في تحسين سرعة المواقع وتحسين أدائها. ننصح بتجربة هذه التقنيات والاستفادة منها في تطوير تطبيقات سريعة التحميل وتحقق تجربة مستخدم أفضل. وإذا كان لديكم نصائح أخرى مفيدة لتحسين أداء المواقع شاركونا إياها في قسم التعليقات أسفل المقال.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.thedevspace.io/course/miscellaneous-web-app-optimization" rel="external nofollow">Top Website Optimization Tools in 2024</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D8%B2%D9%8A%D8%A7%D8%AF%D8%A9-%D8%B3%D8%B1%D8%B9%D8%A9-%D8%A3%D8%AF%D8%A7%D8%A1-%D8%A7%D9%84%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D9%82%D9%86%D9%8A%D8%A9-pre-fetching-r790/" rel="">زيادة سرعة أداء المواقع باستخدام تقنية pre-fetching</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/laravel/%D9%86%D8%B5%D8%A7%D8%A6%D8%AD-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A3%D8%AF%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D9%84%D8%A7%D8%B1%D8%A7%D9%81%D9%8A%D9%84-r2544/" rel="">نصائح لتحسين أداء تطبيقات لارافيل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/apps/web/wordpress/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%AA%D9%88%D9%89-cdn-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A3%D8%AF%D8%A7%D8%A1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D9%88%D9%88%D8%B1%D8%AF%D8%A8%D8%B1%D9%8A%D8%B3-r299/" rel="">كيفية استخدام شبكات توزيع المحتوى CDN لتحسين أداء مواقع ووردبريس</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/web/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D9%8F%D8%B3%D8%B1%D9%91%D8%B9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-varnish-cache-r377/" rel="">مدخل إلى مُسرّع تطبيقات الويب Varnish Cache</a>
	</li>
</ul>

<p>
	 
</p>
]]></description><guid isPermaLink="false">2546</guid><pubDate>Tue, 01 Apr 2025 15:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x641;&#x631;&#x642; &#x628;&#x64A;&#x646; &#x648;&#x62D;&#x62F;&#x629; &#x627;&#x644;&#x645;&#x639;&#x627;&#x644;&#x62C;&#x629; &#x627;&#x644;&#x645;&#x631;&#x643;&#x632;&#x64A;&#x629; CPU &#x648;&#x648;&#x62D;&#x62F;&#x629; &#x627;&#x644;&#x645;&#x639;&#x627;&#x644;&#x62C;&#x629; &#x627;&#x644;&#x631;&#x633;&#x648;&#x645;&#x64A;&#x629; GPU</title><link>https://academy.hsoub.com/programming/advanced/%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B1%D9%83%D8%B2%D9%8A%D8%A9-cpu-%D9%88%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-gpu-r2527/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_03/gpucpu.jpg.a9dbc4e31927321af14515f7ff6b85ae.jpg" /></p>
<p>
	تستعمل العديد من الشركات اليوم كلًا من وحدة المعالجة المركزية CPU ووحدة المعالجة الرسومية GPU لتحسين أداء طيف واسع من التطبيقات بداية من مراكز البيانات Data centers وصولًا إلى الأجهزة المحمولة وغيرها من الاستخدامات المفيدة، وسنشرح في هذا المقال كل وحدة معالجة وآلية عملها ونوضح أهم أوجه التشابه والاختلاف بينهما، وحالات الاستخدام المناسبة لكل منهما، لنساعدكم على تقرير الوحدة الأفضل لمشروعكم أو نشاطكم التجاري.
</p>

<h2>
	تطور المعالجات الحاسوبية
</h2>

<p>
	أصبحت وحدة المعالجة المركزية Central Processing Unit -أو CPU اختصارًا- مكونًا أساسيًا في أنظمة الحواسيب منذ بداية سبعينيات القرن الماضي، فتطورت من مجرد آلات بسيطة لإجراء الحسابات والمعالجات الرياضية إلى معالجات متعددة النواة وبالغة التعقيد. فكان إصدار إنتل لمعالجها 4004 في عام 1971علامة فارقة لبداية حقبة وحدات المعالجات المركزية الحديثة ومهد الطريق للتطورات المتسارعة في قدرات المعالجة، ومع مرور الزمن أصبحت وحدات المعالجة المركزية CPUs أكثر تخصصًا وتعقيدًا حيث ظهرت ابتكارات مثل <a href="https://academy.hsoub.com/programming/os-embedded-systems/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B1%D9%83%D8%B2%D9%8A%D8%A9-%D9%88%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r1716/#pipeline:~:text=%D9%85%D8%AE%D8%AA%D9%84%D9%81%D8%A9%D9%8B%20%D9%82%D9%84%D9%8A%D9%84%D9%8B%D8%A7%20%D9%84%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA%D9%87%D8%A7.-,%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%20%D8%AE%D8%B7%20%D8%A7%D9%84%D8%A3%D9%86%D8%A7%D8%A8%D9%8A%D8%A8,-%D8%AA%D9%8F%D8%B9%D9%8E%D8%AF%D9%91%20%D8%B9%D9%85%D9%84%D9%8A%D8%A9%20%D9%88%D8%AD%D8%AF%D8%A9%20ALU" rel="">خطوط أنابيب تجزئة تنفيذ التعليمات Instruction pipelining</a> ومتنبئ التعليمات التفرعية Branch Predictor و <a href="https://academy.hsoub.com/programming/os-embedded-systems/%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A7%D8%AA-%D9%81%D9%8A-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r1722/#hyperthreading:~:text=%D8%AA%D8%B1%D8%A7%D8%A8%D8%B7%20%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9%20%D8%A7%D9%84%D9%85%D8%AE%D8%A8%D8%A6%D9%8A%D8%A9.-,%D8%AA%D9%82%D9%86%D9%8A%D8%A9%20%D8%AE%D9%8A%D9%88%D8%B7%20%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9%20%D8%A7%D9%84%D9%81%D8%A7%D8%A6%D9%82%D8%A9%20Hyperthreading,-%D9%8A%D9%85%D9%83%D9%86%20%D8%A3%D9%86%20%D9%8A%D9%82%D8%B6%D9%8A" rel="">تقنية خيوط المعالجة الفائقة Hyper-threading</a> وقد دفعت هذه التقنيات بحدود المعالجة الحاسوبية العامة إلى الأمام.
</p>

<p>
	ثم ظهرت وحدة المعالجة الرسومية Graphics Processing Unit -أو GPU اختصارًا- في بداية التسعينيات لتلبي الطلب المتزايد على المعالجة الرسومية في <a href="https://academy.hsoub.com/programming/game-development/%D8%A3%D9%84%D8%B9%D8%A7%D8%A8-%D8%A7%D9%84%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%AA%D8%B7%D9%88%D8%B1%D9%87%D8%A7-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%AA%D9%87%D8%A7-%D9%88%D8%AE%D8%B7%D9%88%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D8%AA%D9%87%D8%A7-r2290/" rel="">ألعاب الفيديو</a> ورسوميات الحواسيب. وعلى النقيض من وحدات المعالجات المركزية CPUs التي كانت مصممة لتنفيذ التعليمات على التسلسل sequential فقد صممت المعالجات الرسومية GPUs للتعامل مع الحسابات والعمليات على التوازي parallel من أجل تنفيذ المهام الرسومية المعقدة، وكان تقديم نيفيديا لمعالجها <a href="https://ar.wikipedia.org/wiki/%D8%A5%D9%86%D9%81%D9%8A%D8%AF%D9%8A%D8%A7#:~:text=%D8%A7%D9%84%D8%A5%D8%B5%D8%AF%D8%A7%D8%B1%D8%A7%D8%AA%20%D8%A7%D9%84%D8%B1%D8%A6%D9%8A%D8%B3%D9%8A%D8%A9%20%D9%88%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA%20%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AD%D9%88%D8%A7%D8%B0" rel="external nofollow">جيفورس GeForce 256 في عام 1999</a> بداية حقبة جديدة للعتاد المخصص للرسوميات القادر على تخفيف الحمل الحاسوبي عن وحدات المعالجات المركزية CPUs في مهام المعالجة الرسومية.
</p>

<p>
	صُممت وحدات المعالجة المركزية والرسومية في البداية لأغراض مختلفة، لكن في السنوات الأخيرة بدأت قدراتهما وأدوارهما تتداخل مع بعضها البعض، فأصبحت وحدات المعالجة المركزية CPUs مدمجة مع قدرات المعالجة على التوازي، وصارت وحدات المعالجة الرسومية GPUs أكثر شمولًا وتنوعًا وظهرت لها تطبيقات أخرى غير الرسوميات مثل حل المسائل العلمية المعقدة وتطبيقات <a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A2%D9%84%D8%A9/" rel="">تعلم الآلة</a>، كما حظت المعالجات الرسومية بشعبية كبيرة في السنوات الأخيرة نتيجة أدائها المبهر في أداء مهام الذكاء الاصطناعي وبالتحديد قدرتها على المعالجة المتوازية parallel computing فكانت الأداة المثالية لتدريب شبكات <a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-%D9%84%D9%81%D9%87%D9%85-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%82-r1422/" rel="">التعلم العميق</a> وإجراء العمليات الحسابية على المصفوفات الضخمة التي تلعب دورًا أساسيًا في العديد من <a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/" rel="">خوارزميات الذكاء الاصطناعي</a>، مما دفع إلى اكتشافات علمية متقدمة في مجالات بحثية وتطبيقية مختلفة مثل الرؤية الحاسوبية Computer Vision ومعالجة اللغات الطبيعية Natural Language Processing و<a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%A7%D8%B3%D8%AA%D9%83%D8%B4%D9%81-%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A-%D8%A7%D9%84%D8%AA%D9%88%D9%84%D9%8A%D8%AF%D9%8A-r2399/#:~:text=%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%82%D8%A9%20%D9%81%D9%8A%20%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA.-,%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1%20%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A%20%D8%A7%D9%84%D8%AA%D9%88%D9%84%D9%8A%D8%AF%D9%8A%20Generative%20AI,-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1%20%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A%20%D8%A7%D9%84%D8%AA%D9%88%D9%84%D9%8A%D8%AF%D9%8A" rel="">الذكاء الاصطناعي التوليدي Generative AI</a>.
</p>

<h2 id="cpu">
	ما هي وحدة المعالجة المركزية CPU
</h2>

<p>
	وحدة المعالجة المركزية CPU هي المكون الرئيسي في أجهزة الحواسيب، فهي تقوم بأغلب العمليات والمعالجة، ويشار إليها في سياقات كثيرة بعقل الحاسوب، وتنفذ وحدة المعالجة المركزية CPU التعليمات والحسابات وتنظم الأنشطة المختلفة للعتاد الحاسوبي. وتسود اليوم معالجات متعددة الأنوية multi-core processors قادرة على تنفيذ العديد من المهام في نفس الوقت مستغلة قدرات المعالجة المتوازية والمعماريات المتقدمة.
</p>

<p>
	وقد تحتاج بعض الشركات لاستخدام أنظمة شديدة الاعتماد على استهلاك وحدة المعالجة المركزية CPU-intensive لإجراء <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">تحليل متقدم للبيانات</a>، مثل معالجة مجموعات من البيانات الكبيرة الخاصة بالعملاء لاكتشاف أنماط شرائهم، وتوقع اتجاهات السوق المستقبلية.
</p>

<h2 id="cpu-1">
	آلية عمل وحدة المعالجة المركزية CPU
</h2>

<p>
	تعمل وحدة المعالجة المركزية CPU من خلال حلقة من العمليات معروفة باسم دورة تنفيذ التعليمات وتتكون من ثلاث خطوات وهي الجلب Fetch و فك التشفير Decode ثم التنفيذ Execute، وتكون هذه الخطوات معًا أساس جميع عمليات وحدة المعالجة المركزية CPU. والآن لنستكشف المكونات الرئيسية والخطوات المرتبطة بمعالجة التعليمات وتنفيذها لنفهم كيفية عمل وحدة المعالجة المركزية CPU:
</p>

<h3>
	وحدة التحكم Control unit 
</h3>

<p>
	تعد وحدة التحكم بمثابة مركز القيادة الخاص بوحدة المعالجة المركزية CPU، فهي تدير وتنسق كل العمليات الخاصة بوحدة المعالجة المركزية CPU.
</p>

<h3>
	وحدة الحساب والمنطق ALU‎
</h3>

<p>
	تجري في وحدة الحساب والمنطق Arithmetic Logic Unit -أو ALU اختصارًا- كل العمليات الحسابية والمنطقية مثل الجمع والطرح والمقارنة، فهي تشكل أساس القدرات الحسابية لوحدة المعالجة المركزية CPU.
</p>

<h3>
	السجلات Registers 
</h3>

<p>
	هي وحدات تخزين فائقة السرعة توجد بداخل شريحة وحدة المعالجة المركزية CPU، حيث تحتفظ هذه السجلات Registers بالبيانات التي تعمل عليها وحدة المعالجة المركزية CPU حاليًا، مما يسمح بسرعة الوصول لها والتعديل عليها.
</p>

<h3>
	الذاكرة المخبئية Cache
</h3>

<p>
	<strong><a href="https://academy.hsoub.com/programming/os-embedded-systems/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B9%D9%85%D9%8A%D9%82%D8%A9-%D8%B9%D9%84%D9%89-%D8%AA%D8%B3%D9%84%D8%B3%D9%84-%D8%A7%D9%84%D8%B0%D9%88%D8%A7%D9%83%D8%B1-%D8%A7%D9%84%D9%87%D8%B1%D9%85%D9%8A-%D9%88%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D8%AE%D8%A8%D8%A6%D9%8A%D8%A9-%D9%81%D9%8A-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r1720/" rel="">الذاكرة المخبئية Cache</a></strong> هي ذاكرة مؤقتة صغيرة الحجم مبنية بداخل وحدة المعالجة المركزية وهي تتميز بسرعتها وتستخدم لتخزين البيانات والتعليمات التي يُحتمل استخدامها بشكل متكرر لتسريع المعالجة، ولكنها أقل سرعة من السجلات Registers وأكبر منها في الحجم.
</p>

<h3>
	الجلب Fetch
</h3>

<p>
	تجلب وحدة المعالجة المركزية CPU في هذه الخطوة التعليمات من الذاكرة، ويحدد عداد البرنامج program counter  موقع التعليمة الذي ستفذ حاليًا، فعداد البرنامج هو عبارة عن سجل مخصص لمتابعة وتعقب التعليمات المطلوب تنفيذها، ويحدّث نفسه تلقائيًا ليشير للتعليمة التالية.
</p>

<h3>
	فك التشفير Decode
</h3>

<p>
	تأتي هذه الخطوة بعد جلب fetching التعليمة حيث يجري تفسيرها بواسطة وحدة المعالجة المركزية CPU، وتعمل وحدة التحكم Control Unit على فك تشفير العمليات وتحديد ما هي العمليات التي ينبغي تنفيذها، وما هي البيانات التي تحتاجها هذه العملية.
</p>

<h3>
	التنفيذ Execute  
</h3>

<p>
	تنفذ وحدة المعالجة المركزية CPU التعليمة، وقد يتطلب التنفيذ عمليات حسابية أو منطقية باستخدام وحدة الحساب والمنطق ALU، أو نقل بيانات خلال السجلات المختلفة، أو التفاعل مع أجزاء مختلفة من الحاسوب.
</p>

<h3>
	التخزين Store
</h3>

<p>
	إن كانت العملية المُنفذَّة تعيد نتيجة فسوف تحفظ هذه النتيجة في سجل register أو في الذاكرة، ويُحَدَّث عداد البرنامج program counter ليشير للتعليمة القادمة، لتبدأ نفس الحلقة في التكرار مجددًا.
</p>

<h2 id="gpu">
	ما هي وحدة المعالجة الرسومية GPU
</h2>

<p>
	تعد وحدة المعالجة الرسومية GPU دارة إلكترونية مخصصة ومصممة للتعديل على الذاكرة بسرعة، وتسريع إنشاء الصور في ذاكرة مخصصة لعرض ولإخراج المعلومات على أجهزة العرض تسمى frame buffer، وقد طُوّرت في البداية لأغراض عرض الرسوميات ثلاثية الأبعاد في <a href="https://academy.hsoub.com/programming/game-development/%D8%A3%D9%84%D8%B9%D8%A7%D8%A8-%D8%A7%D9%84%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%AA%D8%B7%D9%88%D8%B1%D9%87%D8%A7-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%AA%D9%87%D8%A7-%D9%88%D8%AE%D8%B7%D9%88%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D8%AA%D9%87%D8%A7-r2290/" rel="">ألعاب الفيديو</a>، ثم تطورت لتصبح معالجات متوازية قووية قادرة على التعامل مع العمليات الحسابية المعقدة على التزامن. وتحتوي وحدات المعالجة الرسومية GPUs على آلاف من الأنوية cores الصغيرة وعالية الكفاءة المصممة للتعامل مع مهام متعددة على التوازي، مما يجعلها الخيار المثالي لمهام <a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%B2%D9%8A-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1485/" rel="">المعالجة المتوازية</a> التي تتجاوز التطبيقات الرسومية.
</p>

<p>
	تستخدم الشركات اليوم قدرات وحدات المعالجة الرسومية GPUs لتطوير وتشغيل<a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%AA%D9%82%D9%8A%D9%8A%D9%85-%D9%88%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%86%D9%85%D8%A7%D8%B0%D8%AC-%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A2%D9%84%D8%A9-r1635/" rel=""> نماذج تعلم الآلة</a> الضخمة على نطاق واسع، وهذا يتيح لها تحقيق تقدم متسارع في تخصيص وتشخيص تجربة أنظمة التوصية الخاصة بها، وتستخدم أيضًا في تطوير تقنيات المركبات ذاتية القيادة وفي خدمات <a href="https://academy.hsoub.com/apps/cat/" rel="">الترجمة الفورية</a> في الوقت الحقيقي وغيرها من <a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/" rel="">التطبيقات الذكية</a>.
</p>

<h2 id="gpu-1">
	آلية عمل وحدة المعالجة الرسومية GPU
</h2>

<p>
	تعمل وحدات المعالجة الرسومية GPUs بمبادئ مشابهة لتلك التي تتبعها وحدات المعالجة المركزية CPUs ولكنها مصممة ومحسنة لمهام المعالجة المتوازية، وهذا يجعلها الخيار الأنسب لعرض الرسوميات ولأنواع معينة من المهام. وسنشرح تاليًا آلية عمل وحدة المعالجة الرسومية GPU:
</p>

<h3>
	المعالجات متعددة التدفق SMs‎
</h3>

<p>
	تكافئ المعالجات متعددة التدفق Streaming Multiprocessors -أو SMs اختصارًا- الأنوية cores المستخدمة في وحدات المعالجة المركزية ولكن يحتوي كل معالج متعدد التدفق SM على وحدات معالجة أصغر ومتعددة ما يكسبه القدرة على التعامل مع تدفق كبير من البيانات حيث تُمكّن هذه المعمارية وحدات المعالجة الرسومية GPUs من التعامل مع العديد من المهام بنفس الوقت.
</p>

<h3>
	أنوية كودا CUDA Cores 
</h3>

<p>
	تقع هذه الأنوية بداخل وحدات معالجة التدفق SM، وهي المسؤولة عن المعالجة بداخلها، وهي أبسط من أنوية وحدة المعالجة المركزية CPU ولكنها توجد بأعداد أكبر بكثير، حيث قد يوجد في وحدة المعالجة الرسومية GPU آلاف منها.
</p>

<h3>
	وحدات رسم الخامة TMUs‎ 
</h3>

<p>
	تعمل وحدة رسم الخامة Texture Mapping Unit أو TMU اختصارًا على رسم الخامة أو السطح الخارجي للنماذج ثلاثية الأبعاد بسرعة كبيرة، وهي وظيفة فريدة توجد فقط في وحدات المعالجة الرسومية GPUs وليست موجودة في وحدات المعالجة المركزية CPUs.
</p>

<h3>
	ذاكرة الفيديو VRAM‎
</h3>

<p>
	تتشابه ذاكرة الفيديو Video Memory في الوظيفة مع ذاكرة الوصول العشوائي الخاصة بوحدة المعالجة المركزية CPU، ولكنها محسنَّة لمهام المعالجة الرسومية، وهي في الغالب أسرع وتمتلك معدل نقل بيانات أكبر من الذي تملكه <a href="https://academy.hsoub.com/apps/operating-systems/%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87%D8%A7-r880/" rel="">ذاكرة</a> RAM.
</p>

<h3>
	الجلب Fetch
</h3>

<p>
	تمتلك وظيفة مماثلة لتلك الخطوة الخاصة بجلب التعليمات في وحدة المعالجة المركزية CPU، ولكنها مصممة بشكل أفضل لتنفيذ العديد من التعليمات المتشابهة على التوازي بذات الوقت.
</p>

<h3>
	فك التشفير Decode
</h3>

<p>
	تشابه هذه الوظيفة نظيرتها في وحدة المعالجة المركزية CPU والاختلاف بينهما هو تحسين هذه الوظيفة في GPU لتناسب مع المهام المتعلقة بمعالجة الرسوميات.
</p>

<h3>
	التنفيذ Execute
</h3>

<p>
	تختلف هذه الخطوة بشكلٍ جذري عن نظيرتها في وحدة المعالجة المركزية CPU، حيث تُنفِّذ وحدات المعالجة الرسومية GPUs العديد من التعليمات المتشابهة بشكل متزامن من خلال عدد كبير من أنوية كودا CUDA cores في عملية تسمى Single instruction Multiple Data -أو SIMD اختصارًا- والتي تعني إمكانية تنفيذ نفس التعليمة على عدة بيانات في نفس الوقت.
</p>

<h3>
	التخزين Store
</h3>

<p>
	تُحفظ النتائج عادة في ذاكرة الفيديو Video Random Access Memory -أو VRAM اختصارًا- ويسمح المستوى العالي من التوازي الذي توفره وحدات المعالجة الرسومية GPUs بتحقيق معدل نقل بيانات كبير جدًا من أجل تنفيذ المهام المتعلقة بالرسوميات.
</p>

<h2 id="cpugpu-1">
	مقارنة بين وحدة المعالجة المركزية CPU ووحدة المعالجة الرسومية GPU
</h2>

<p>
	تعد كلتا وحدتي المعالجة المركزية والرسومية مكونات ضرورية في أنظمة الحوسبة الحديثة، وقد صُمّمت كل وحدة معالجة منهما بأولويات ومسؤوليات مختلفة وهدف تصميمي مختلف، لذا من الضروري فهم الفروقات بينهما لتحديد المناسب لنا من بينهما.
</p>

<h3 id="">
	الوظيفة الأساسية
</h3>

<p>
	صُمّمت وحدات المعالجة المركزية CPUs لتكون شاملة ومتعددة الأغراض وقادرة على التعامل مع طيف واسع من المهام بكفاءة عالية، فهي تتميز في المعالجة المتسلسلة sequential processing، كما أنها مُحسّنة لاتخاذ القرارات المعقدة، وتتحكم وحدة المعالجة المركزية CPU في العتاد الحاسوبي والتنسيق بين البرامج المختلفة لتشغيلها.
</p>

<p>
	بنما كانت وحدات المعالجة الرسومية GPUs متخصصة في البداية في عرض الرسوميات graphics rendering، ولكنها تطورت مع الوقت لتصبح معالجات فائقة لها القدرة على المعالجة المتوازية <a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%B2%D9%8A-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1485/" rel="">parallel processors</a>. وهي اليوم تشغّل التعليمات تمامًا كوحدات المعالجة المركزية CPUs ولكنها محسنة للتعامل مع الكثير من العمليات الحسابية المتزامنة. فوحدات المعالجة المركزية CPU متنوعة القدرات وشاملة المهام، في حين أن وحدات المعالجة الرسومية تتميز بالتخصص والتفوق في تنفيذ المهام القابلة للتقسيم إلى مهام كثيرة جدًا والتي تتميز بكونها مهام متشابهة ويمكن حسابها بشكل مستقل عن بعضها البعض.
</p>

<h3 id="-1">
	المعالجة
</h3>

<p>
	تُنفِّذ وحدات المعالجة المركزية CPUs العمليات بشكل متتابع أو متسلسل، حيث تُنفِّذ التعليمات المعقدة واحدة تلو الأخرى وتستخدم تقنيات مخصصة مثل التنبؤ بالتعليمات المتفرعة أو الشرطية branch prediction أو التنفيذ غير المراعي للترتيب Out-of-Order Execution الذي يسمح بتنفيذ التعليمات بترتيب مختلف عن ترتيبها الأصلي في البرنامج لتحسين عملية المعالجة المتسلسلة، حيث تشبه وحدات المعالجة المركزية CPUs المدير المنظم فهي تنسق وتتعامل مع مهام متنوعة وتتخذ القرارات المعقدة بسرعة.
</p>

<p>
	من ناحية أخرى، تعالج وحدات المعالجة الرسومية GPUs المهام بالتوازي، فتنفّذ العديد من التعليمات البسيطة بشكل متزامن عبر عدد ضخم من الأنوية، وتحتوي هذه الأنوية على عدد ضخم من الخيوط threads البسيطة التي يمكنها تنفيذ عمليات بسيطة للغاية ولكن عددها الضخم يمكِّنها من معالجة كمية بيانات أكبر بنفس الوقت، فبينما تشبه وحدة المعالجة المركزية CPU المدير، تشبه وحدة المعالجة الرسومية GPU خط التجميع في المصانع حيث يوكل لكل عامل أو آلة جزء معين من العمل، وبهذا يمكن لهذه الوحدة معالجة كميات ضخمة من البيانات المتشابهة في وقت واحد.
</p>

<h3 id="-2">
	تصميم الهيكلية
</h3>

<p>
	تمتلك وحدات المعالجة المركزية CPUs في الغالب من 2 إلى 64 نواة معقدة التركيب ومزودة بذاكرة مخبئية Caches ووحدات تحكم Control units مخصصة ومعقدة، حيث صممت هذه المكونات لتحقق أقل معدل تأخير في الاستجابة low latency، فتستطيع كل نواة التعامل مع مهام متنوعة بشكل مستقل، فتولي هذه الهيكلية أهمية للتنوع في القدرات وسرعة الاستجابة لعدد واسع ومتنوع من المهام.
</p>

<p>
	بينما تجمع وحدات المعالجة الرسومية GPUs مئات أو آلاف الأنوية البسيطة فيما يعرف بمعالجات متعددة التدفق streaming multiprocessors أي يمكنها معالجة عدة تدفقات من البيانات في وقت واحد، وعلى عكس وحدات المعالجة المركزية CPUs فإن أنوية وحدات المعالجة الرسومية GPUs تتشارك في وحدات التحكم  والذاكرات المخبئية بداخل كل معالج متعدد، وتختلف هذه المعمارية عن وحدات المعالجة المركزية CPUs بحيث تسمع لوحدات المعالجة الرسومية GPUs بمعالجة كميات ضخمة من البيانات من خلال عمليات متشابهة عليها، فهي لا تعتمد أنوية معقدة كما في وحدات المعالجة المركزية بل تستخدم أنوية بسيطة وصغيرة تكمن قوتها وتفوقها بقدرتها في العمل على التوازي على عدد كبير من البيانات.
</p>

<h3 id="-3">
	حالات الاستخدام
</h3>

<p>
	تبرز أهمية وحدات المعالجة المركزية CPUs في المهام التي تطلب اتخاذ قرارات معقدة، وتنفيذ عمليات متنوعة، أو تغيرات متكررة في تدفق تنفيذ العمليات. فتشغل هذه الوحدات<a href="https://academy.hsoub.com/programming/os-embedded-systems/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84/" rel=""> أنظمة التشغيل</a> والمتصفحات. وتعد الخيار الأول لمهام مثل إدارة قواعد البيانات وتشغيل <a href="https://academy.hsoub.com/apps/productivity/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D9%87%D8%A7%D9%85-%D9%88%D8%A2%D9%84%D9%8A%D8%A9-%D8%AA%D9%86%D8%B8%D9%8A%D9%85%D9%87%D8%A7-%D8%B9%D8%A8%D8%B1-%D9%85%D9%86%D8%B5%D8%A9-%D8%A3%D9%86%D8%A7-r468/" rel="">برامج الإنتاجية</a> والتنسيق بين عمليات النظام المختلفة.
</p>

<p>
	في حين تبرز أهمية وحدات المعالجة الرسومية GPUs في الحالات التي تتضمن إجراء العديد من العمليات الحسابية في نفس الوقت عبر عدد كبير من البيانات المتجانسة أي البيانات المتشابهة أو المتطابقة من حيث الهيكل أو النوع، فهي تتشارك مع وحدات المعالجة المركزية CPUs في القدرة على القيام بالعمليات الحسابية لكنها تتفوق بشكل جليّ في المهام التي تطلب عرض <a href="https://academy.hsoub.com/design/3d/" rel="">رسومات ثلاثية الأبعاد</a> وكثيرة التفاصيل، أو في تدريب نماذج <a href="https://academy.hsoub.com/programming/artificial-intelligence/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B3%D8%B1%D9%8A%D8%B9%D8%A9-%D8%B9%D9%84%D9%89-%D9%85%D8%AC%D8%A7%D9%84-%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A2%D9%84%D8%A9-r1933/" rel="">تعلم الآلة</a> أو تنفيذ عمليات محاكاة لظواهر علمية تتطلب حسابات ضخمة، ففي كل هذه التطبيقات تتفوق وحدات المعالجة الرسومية GPUs على وحدات المعالجة المركزية CPUs عشرات المرات وربما مئات المرات من حيث الأداء، مما يعطيها دورًا لا غنى عنه في مجالات مثل رسوميات الحاسوب، والذكاء الاصطناعي والحوسبة عالية الأداء.
</p>

<h2 id="cpugpu-2">
	دمج وحدة المعالجة المركزية CPU مع وحدة المعالجة الرسومية GPU
</h2>

<p>
	تستخدم الأنظمة الحاسوبية الحديثة كلًا من وحدة المعالجة المركزية CPU ووحدة المعالجة الرسومية GPU لتحسين التطبيقات والمهام. ويعرف هذا النهج بالحوسبة الهجينة، حيث تسمح هذه الطريقة لكل معالج بالتركيز على الأمور التي يتفوق فيهل، فتتعامل وحدات المعالجة المركزية CPUs مع المهام المعقدة المتسلسلة، وتتولى وحدات المعالجة الرسومية GPUs الحسابات المتوازية، ويعزز هذه الدمج في التقنيات أداء الأنظمة ويساعد في العديد من المجالات بداية من المحاكاة العلمية والذكاء الاصطناعي وصولًا إلى إنشاء المحتوى، ولنستعرض بعض الأمثلة على حالات يمكننا فيها دمج CPU مع GPU.
</p>

<p>
	لنفترض أن شركة ناشئة تطور نظامًا لاكتشاف الكائنات في الوقت الحقيقي من أجل المركبات ذاتية القيادة، فيمكن للشركة في هذه الحالة أن تستخدم وحدة المعالجة المركزية CPU في معالجة البيانات المتدفقة من المستشعرات واتخاذ قرارات سريعة، بينما تستخدم وحدة المعالجة الرسومية GPU في تشغيل نظام من <a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%A7%D8%B3%D8%AA%D9%83%D8%B4%D9%81-%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A-%D8%A7%D9%84%D8%AA%D9%88%D9%84%D9%8A%D8%AF%D9%8A-r2399/#artificialneuralnetworkann:~:text=%D8%A7%D9%84%D8%AD%D8%A7%D8%AC%D8%A9%20%D9%84%D9%84%D8%AA%D8%AF%D8%AE%D9%84%20%D8%A7%D9%84%D8%A8%D8%B4%D8%B1%D9%8A.-,%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A7%D8%AA%20%D8%A7%D9%84%D8%B9%D8%B5%D8%A8%D9%8A%D8%A9%20%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A%D8%A9%20(Artificial%20Neural%20Network%20(ANN,-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A7%D8%AA%20%D8%A7%D9%84%D8%B9%D8%B5%D8%A8%D9%8A%D8%A9%20%D9%87%D9%8A" rel="">الشبكات العصبية الاصطناعية Artificial Neural Networks</a> المعقدة للتعرف على الكائنات في البيئة المحيطة بالمركبة.
</p>

<p>
	مثال آخر، لنفترض أن شركة تقنيات مالية تريد تطوير منصة تداول وتتوقع ورود طلبات تداول كثيرة في نفس الوقت، فيمكنها أن تستخدم وحدات المعالجة المركزية CPUs لإدارة عملية التداول وتشغيل الخوارزميات المعقدة، بينما تستفيد من وحدات المعالجة الرسومية GPUs في تحليل بيانات السوق بسرعة وتقييم المخاطر التسويقية من خلال عمليات حسابية تنفذ على التوازي.
</p>

<p>
	أخيرًا، لنفترض أن لدينا استوديو تطوير ألعاب ونريد <a href="https://academy.hsoub.com/programming/game-development/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D9%87%D9%85%D9%8A%D8%A9-%D8%B5%D9%86%D8%A7%D8%B9%D8%A9-%D8%A7%D9%84%D8%A3%D9%84%D8%B9%D8%A7%D8%A8-%D8%A7%D9%84%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A%D8%A9-r2228/" rel="">صناعة لعبة</a> عالم مفتوح متعددة الشخصيات واللاعبين، فيمكننا في هذه الحالة استخدام وحدات المعالجة المركزية CPUs في تنفيذ منطق اللعب وإضافة سلوك الذكاء الاصطناعي للعبة، بينما نعتمد على وحدات المعالجة الرسومية GPUs في عرض الرسومات المعقدة ثلاثية الأبعاد ومحاكاة الظواهر الطبيعية ومحاكاة الفيزياء لإنشاء تجربة لعب مبهرة بصريًا.
</p>

<p>
	ترجمة وبتصرف لمقالة <a href="https://www.digitalocean.com/resources/articles/cpu-vs-gpu" rel="external nofollow">?What is the Difference Between CPU and GPU</a>.
</p>

<h1 id="-4">
	اقرأ أيضًا
</h1>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%AB%D8%A7%D9%85%D9%86-%D8%AA%D8%B9%D8%AF%D8%AF-%D8%A7%D9%84%D9%85%D9%87%D8%A7%D9%85-multitasking-%D9%81%D9%8A-%D8%A7%D9%84%D8%AD%D9%88%D8%A7%D8%B3%D9%8A%D8%A8-r1011/" rel="">تعدد المهام Multitasking في الحواسيب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/apps/operating-systems/%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B1%D9%83%D8%B2%D9%8A%D8%A9-r879/" rel="">وحدة المعالجة المركزية </a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/os-embedded-systems/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B1%D9%83%D8%B2%D9%8A%D8%A9-%D9%88%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r1716/" rel="">تعرف على وحدة المعالجة المركزية وعملياتها في معمارية الحاسوب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%A7%D8%B3%D8%AA%D9%83%D8%B4%D9%81-%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A-%D8%A7%D9%84%D8%AA%D9%88%D9%84%D9%8A%D8%AF%D9%8A-r2399/" rel="">استكشف مصطلحات الذكاء الاصطناعي التوليدي</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/artificial-intelligence/%D8%AA%D9%88%D9%84%D9%8A%D8%AF-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%86%D9%85%D8%A7%D8%B0%D8%AC-%D8%A7%D9%84%D9%84%D8%BA%D9%88%D9%8A%D8%A9-%D8%A7%D9%84%D9%83%D8%A8%D9%8A%D8%B1%D8%A9-llms-r2380/" rel="">توليد النصوص باستخدام النماذج اللغوية الكبيرة LLMs</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2527</guid><pubDate>Fri, 07 Mar 2025 12:06:02 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x631;&#x628;&#x637; &#x628;&#x648;&#x62A; &#x62A;&#x644;&#x63A;&#x631;&#x627;&#x645; &#x645;&#x639; &#x62C;&#x62F;&#x627;&#x648;&#x644; &#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; Google Sheets</title><link>https://academy.hsoub.com/programming/advanced/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%B1%D8%A8%D8%B7-%D8%A8%D9%88%D8%AA-%D8%AA%D9%84%D8%BA%D8%B1%D8%A7%D9%85-%D9%85%D8%B9-%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-google-sheets-r2526/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_03/1479652342_.png.f37ddedaf1a44e73030e155ba34d4295.png" /></p>
<p>
	نحتاج كثيرًا إلى استعمال جداول البيانات لتخزين بيانات دورية خلال يومنا لتجنب نسيانها فقد لا نكون دومًا خلف طاولة العمل حيث الدفاتر والحاسوب بل قد نكون في الشارع أو الحافلة ولا نملك سوى الهاتف في يدنا ولا نريد استعمال تطبيق الملاحظات الممتلئ بملاحظات مبعثرة هنا وهناك. على سبيل المثال إذا احتجنا لاستخدام جداول بيانات جوجل Google Sheets لتسهيل تسجيل بعض البيانات ومتابعتها دوريًا من مكان واحد سنواجه صعوبة في التعامل مع Google Sheets على الجوال لأن الشاشة صغيرة وغير مريحة بالعمل، لذا قد نضطر إلى تدوين الملاحظات سريعًا لإدخالها في جداول البيانات وتنظيمها لاحقًا عند توفر الحاسوب.<br>
	سنوضح في مقال اليوم طريقة سهلة وعملية لإدارة إدخال البيانات في جداول بيانات جوجل من خلال إنشاء بوت تلغرام Telegram وظيفته إدخال البيانات وتنظيمها مباشرةً في جداول البيانات، فلا يكاد يخلو أي هاتف جوال من تطبيق تلغرام الشهير وبهذا ندخل البيانات بسهولة من خلاله دون الحاجة  لفتح تطبيق جداول البيانات، كل ما نحتاجه هو إرسال رسالة بصيغة معينة للبوت ليأخذها ويرسلها لجداول بيانات جوجل ويخزنها هناك.أي تتألف العملية من ثلاثة خطوات، ارسال الرسالة التي تحوي البيانات إلى البوت، ثم نقل البيانات، ثم أخيرًا تخزينها في وجهتها.
</p>

<h2 id="-1">
	متطلبات مسبقة للعمل
</h2>

<p>
	نحتاج قبل إكمال هذا المقال إلى ما يلي:
</p>

<ul>
	<li>
		حساب على تطبيق تلغرام
	</li>
	<li>
		حساب جوجل مع إمكانية الوصول إلى تطبيق جداول بيانات جوجل Google Sheets
	</li>
	<li>
		معرفة مسبقة باستعمال Google Sheets، ويمكن الرجوع إلى مقال <a href="https://academy.hsoub.com/apps/productivity/google-drive/google-sheets/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%AC%D9%88%D8%AC%D9%84-google-sheets-r295/" rel="">مقدمة إلى جداول بيانات جوجل Google Sheets</a>
	</li>
	<li>
		معرفة أساسية بلغة جافاسكريبت، ويمكن الرجوع إلى مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%B5%D9%81%D8%B1-%D8%AD%D8%AA%D9%89-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D8%B1%D8%A7%D9%81-r2046/" rel="">تعلم لغة جافا سكريبت JavaScript</a>
	</li>
</ul>

<h2 id="-2">
	التطبيق النموذجي لربط بوت تلغرام مع جداول بيانات جوجل
</h2>

<p>
	سنعتمد على نموذج بيانات بسيط بغرض التعلم حيث سنتعلم من خلاله آلية ربط بوت تلغرام مع جداول بيانات جوجل، والنموذج الذي سنستخدمه هنا هو بيانات الإيرادات والنفقات اليومية كما توضح الصورة التالية:
</p>

<p style="text-align: center;">
	<img alt="001 sheet example" class="ipsImage ipsImage_thumbnailed" data-fileid="169080" data-ratio="57.75" data-unique="ur717qn92" style="width: 400px; height: auto;" width="713" src="https://academy.hsoub.com/uploads/monthly_2025_03/sheet-example.PNG.f8adc43b49a76d58fb1f003f7c0a3af4.PNG">
</p>

<p>
	<strong>ملاحظة</strong>: استخدمنا هذا النموذج بغرض التعلم ونترك لكم تطبيق ما ستتعلمونه على نموذج البيانات المناسب لاحتياجاتكم.
</p>

<h2 id="telegrambot">
	الخطوة الأولى: إنشاء بوت تلغرام Telegram Bot
</h2>

<p>
	سنستعمل تطبيق تلغرام على الويب للسهولة، لذا سنفتح <a href="https://web.telegram.org/a/" rel="external nofollow">تطبيق تلغرام ويب</a> ونسجل الدخول، ثم نبحث في حقل البحث عن BotFather وهو كبيرُ البوتات الذي سيساعدنا على إنشاء بوتات تلغرام وإدراتها. لنتبه إلى أن مُعرِّف كبير البوتات الرسمي هو ‎@botfather إذ ستظهر لنا عدة نتائج في حقل البحث:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169037" href="https://academy.hsoub.com/uploads/monthly_2025_03/1-botfather-telegram.png.0589b6c6c06676598c6f66f64133b97d.png" rel=""><img alt="002 botfather telegram" class="ipsImage ipsImage_thumbnailed" data-fileid="169037" data-unique="g0fy0sh19" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/1-botfather-telegram.thumb.png.7db400dbe17b3bdd07c8bbd318e24104.png"> </a>
</p>

<p>
	لنضغط بعدها على البدء <strong>Start</strong> وستظهر أمامنا رسالة فيها قائمة طويلة من الخيارات اللازمة لإنشاء بوتات تلغرام وإدارتها، بعدها سيسألنا كبيرُ البوتات عن اسم البوت الذي نريد إنشاءه فنجبه باسم البوت وسندخل هنا الاسم <strong>إيرادات ونفقات</strong>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169038" href="https://academy.hsoub.com/uploads/monthly_2025_03/2-telegram-bot-name.png.d43a99ab0455dfb8548549a325b2ab4b.png" rel=""><img alt="003 telegram bot name" class="ipsImage ipsImage_thumbnailed" data-fileid="169038" data-unique="0daalrq9s" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/2-telegram-bot-name.thumb.png.197adafe484b5bd2727a0a0eba7ed797.png"> </a>
</p>

<p>
	سيطلب بعدها كبيرُ البوتات إدخال اسم مُعرِّف أو اسم المستخدم للبوت الجديد، وننتبه لأن هذا الاسم يجب أن ينتهي بالكلمة bot سواءً بالشكل usernameBot أو username_bot، وسنستخدم هنا الاسم <strong>MyIncomeAndExpensesBot</strong>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169039" href="https://academy.hsoub.com/uploads/monthly_2025_03/3-telegram-bot-username.png.2df407170baedda94e1f7b11ca3342c5.png" rel=""><img alt="004 telegram bot username" class="ipsImage ipsImage_thumbnailed" data-fileid="169039" data-unique="eu40ugiuv" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/3-telegram-bot-username.thumb.png.c3ecebad8ccbc2d70cfeb78a0ae7695f.png"> </a>
</p>

<p>
	<strong>ملاحظة</strong>: قد لا يقبل البوت بعض اقتراحات اسم المستخدم مثلًا عندما جربنا إدخال اسم المستخدم IncomeExpensesBot وIncomeAndExpensesBot لم يقبلهما لكونهما محجوزان مسبقً، لذا قد نحتاج لتعديل الاسم حتى نجد اسمًا متاحًا.<br>
	سينُشئ بعدها كبيرُ البوتات البوت الجديد ويرسل لنا رسالة يخبرنا فيها عن رابط الوصول للبوت ورمز الوصول إليه access token عبر <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">طلبات HTTP</a>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169040" href="https://academy.hsoub.com/uploads/monthly_2025_03/4-telegram-bot-access-token.png.650ffad0598593c042b99c6638ca2097.png" rel=""><img alt="005 telegram bot access token" class="ipsImage ipsImage_thumbnailed" data-fileid="169040" data-unique="bio6ejphi" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/4-telegram-bot-access-token.thumb.png.58ba61e43ae3e50cdfc512f3b3af0f86.png"> </a>
</p>

<p>
	لنحفظ هذا الرمز لأننا سنستعمله لاحقًا في التواصل مع هذا البوت.
</p>

<h3 id="-3">
	تجربة التواصل مع بوت تلغرام الجديد
</h3>

<p>
	سنلاحظ في آخر الرسالة الموضحة بالصورة السابقة أن كبير البوتات قد أشار إلى <a href="https://core.telegram.org/bots/api" rel="external nofollow">توثيق واجهة البوت البرمجية</a>، سنحتاج دومًا لفتح هذا التوثيق ونرجع إليه دومًا في آلية ربط بوت تلغرام والتواصل معه، فقد تتغير الآلية وتتعدل الواجهة البرمجة مستقبلًا.<br>
	لنتأكد أن البوت قد أُنشئ وأصبح جاهزًا للتواصل، وذلك بزيارة الرابط التالي في متصفح الويب بعد تبديل <code>{access_token}</code> برمز وصول البوت الذي حصلنا عليه مسبقًا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4199_7" style=""><span class="pln">https://api.telegram.org/bot{access_token}/getMe    </span></pre>

<p>
	إن سارت الأمور على ما يرام، فسنحصل على نتيجة مشابهة للنتيجة التالية تحوي تفاصيل البوت ورده علينا:
</p>

<pre class="ipsCode">{    
  "ok": true,    
  "result": {    
    "id": 7666156735,    
    "is_bot": true,    
    "first_name": "إيرادات ونفقات",    
    "username": "MyIncomeAndExpensesBot",    
    "can_join_groups": true,    
    "can_read_all_group_messages": false,    
    "supports_inline_queries": false,    
    "can_connect_to_business": false,    
    "has_main_web_app": false    
  }    
}    
</pre>

<p>
	نلاحظ أننا حصلنا من الرسالة السابقة على رقم البوت <code>id</code>، واسمه <code>first_name</code>، واسم المستخدم <code>username</code>، وبعض التفاصيل الأخرى التي لن تلزمنا في هذا المقال مثل إمكانية انضمام البوت للمجموعات وغيرها.
</p>

<h3 id="-4">
	ضبط بوت تلغرام
</h3>

<p>
	قبل أن نكمل، نود أن نشير لإمكانية ضبط بوت تلغرام الجديد -وأي بوت آخر- عبر كبير البوتات BotFather مباشرةً، فمثلًا لنجرب كتابة <code>/</code> أو اضغط على زر قائمة Menu بجانب حقل الكتابة في المحادثة وستظهر لنا خيارات كثيرة كان كبير البوتات قد ذكر أهمها في أول رسالة أرسلها لنا بعد الضغط على زر البدء Start:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169041" href="https://academy.hsoub.com/uploads/monthly_2025_03/5-telegram-bot-menu.png.95a70a335906fdc7b678f1bed716f93b.png" rel=""><img alt="006 telegram bot menu" class="ipsImage ipsImage_thumbnailed" data-fileid="169041" data-unique="qp19jmj5b" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/5-telegram-bot-menu.thumb.png.6fe1c8d687f17ce6ee03d45091067117.png"> </a>
</p>

<p>
	سنذكر بعض هذه الأوامر:
</p>

<ul>
	<li>
		 <code>‎<strong>/mybots</strong></code> لعرض بوتات تلغرام الخاصة بنا
	</li>
	<li>
		 <code>‎<strong>/setname</strong></code> لتغيير اسم بوت تلغرام
	</li>
	<li>
		<code>‎<strong>/setdescription</strong></code> لضبط أو تغيير وصف البوت
	</li>
	<li>
		<code>‎<strong>/setuserpic</strong></code> لضبط أو تغيير صورة البوت
	</li>
</ul>

<p>
	يمكن تجربة هذه الأوامر مع بقية الأوامر لتتعلم أكثر عن ضبط وإدارة بوتات تلغرام.
</p>

<h3 id="-5">
	فهم آلية الربط والتواصل بين بوت تلغرام وجداول بيانات جوجل
</h3>

<p>
	قبل أن ننتقل إلى الخطوة التالية من مقالنا، من الضروري فهم آلية ربط بوت تلغرام مع جداول بيانات جوجل -أو أي خدمة أخرى مثلًا- حتى نفهم كيف سنكمل عملية الضبط.<br>
	لنتذكر أن هدف المقال إرسال رسالة بصيغة معينة إلى بوت تلغرام، ليقوم بدوره بتنفيذ أوامر محددة وظيفتها أخذ رسالتنا وإرسالها إلى جداول بيانات جوجل لتخزينها هناك، وبذلك يمكن تقسيم العملية إلى ثلاثة خطوات، الأولى إرسال الرسالة التي تحوي البيانات إلى البوت، و الثانية نقل البيانات، والثالثة تخزينها في وجهتها.<br>
	يمكننا التواصل مع البوت عبر آليتين <span>: الأولى هي </span>التواصل عبر الواجهة البرمجية APIs مباشرةً وسؤاله عما نريد، مثلًا يمكن إرسال طلب للواجهة البرمجية <a href="https://core.telegram.org/bots/api#getme" rel="external nofollow"><code>‎/getMe</code></a> لطلب معلومات عن البوت، أو الواجهة البرمجية<code> </code><a href="https://core.telegram.org/bots/api#getupdates" rel="external nofollow"><code>‎/getUpdates</code></a> لطلب كافة الرسائل المرسلة إلى البوت وغيرها، ويمكنك الرجوع إلى <a href="https://core.telegram.org/bots/api" rel="external nofollow">توثيق واجهات بوت تلغرام البرمجية</a> للاطلاع على القائمة الكاملة.<br>
	الآلية الثانية هي التواصل من خلال <a href="https://academy.hsoub.com/questions/3480-%D9%85%D8%A7-%D9%87%D9%88-web-hook-%D9%88%D9%85%D8%A7-%D9%87%D9%8A-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D8%A7%D8%AA%D9%87%D8%9F/" rel="">خطافات الويب Web Hooks</a> أي أننا نطلب من البوت نفسه أن يتواصل معنا عند وقوع أحداث معينة، وسنجد في <a href="https://core.telegram.org/bots/api#update" rel="external nofollow">توثيق Update</a> قائمة الأحداث المدعومة في بوت تلغرام،  حيث نحدد ما هي الأحداث التي نحتاجها بالضبط.
</p>

<p>
	هذا يشبه آلية تواصلنا نحن البشر، تخيل لو كنا نريد -ونحن في العمل- معرفة إن وصلت طلبية طلبناها إلى منزلنا أم لا، عندها إما أن أتصل بالمنزل ونسأل الأهل إن وصلت الطلبية، أو أن نطلب من الأهل أن يتصلوا بنا متى ما وصلت الطلبية.<br>
	سنعتمد في هذا المقال على آلية خطافات الويب web hooks وسنختار حدث استلام رسالة، فكلما استلم البوت رسالة سيقوم بأمر محدد وهو إرسالها إلى خادم جوجل ننشره على الإنترنت عبر عبر برمجة تطبيقات Google أو Apps Scripts والذي بدوره يستلم البيانات ويُخزِّنها في جدول بيانات جوجل وهذا ما سنعمل على ضبطه.<br>
	<strong>ملاحظة</strong>: ذكرنا أننا سنعتمد على خادم ننشره عبر برمجة تطبيقات جوجل وبذلك لن نحتاج إلى إنشاء خادم خاص للتواصل، ولكن الأمر هنا يعود لما يناسب احتياجنا ومتطلباتنا، فيمكن إنشاء خادم خاص والتواصل مع البوت ومعالجة البيانات وتخزينها بأي لغة برمجة نريدها، وإظهارها بالشكل الذي نريد مما يفتح لنا أفاقًا واسعةً لبناء وتطوير بوتات تلغرام خدمية.
</p>

<h2 id="-6">
	الخطوة الثانية: إنشاء جدول بيانات جوجل والتحكم به برمجيًا
</h2>

<p>
	لننشئ جدول بيانات جديد في درايف لتخزين البيانات الواردة من بوت التلغرام برمجيًا بشكل مؤتمت، وذلك عبر إضافة توفرها جوجل وهي لغة برمجة تطبيقات جوجل Apps Scripts .
</p>

<h3 id="googleappsscripts">
	ما هي لغة Google Apps Scripts
</h3>

<p>
	تعد Google Apps Scripts لغة برمجة هدفها إدارة جميع تطبيقات جوجل كبريد Gmail وتخزين Drive وجداول بيانات جوجل Sheets وغيرها، وهي مبنية على لغة جافاسكربت ولاحقتها ملفاتها البرمجية هي <code>‎.gs</code> وتوفر جوجل بيئة تطوير على السحابة Google Workspace تساعدنا على تنفيذ الأكواد التي نكتبها بهذه اللغة، أي لن نحتاج لبيئة تطوير لمعالجة وتنفيذ الملفات المكتوبة بهذه اللغة محليًا.
</p>

<h3 id="-7">
	التحكم بجدول البيانات برمجيًا
</h3>

<p>
	بعد إنشاء جدول بيانات جديد وتسميته باسم الإيرادات والنفقات، يمكننا ربطه والتحكم به برمجيًا بسهولة عبر <a href="https://academy.hsoub.com/apps/productivity/google-drive/google-sheets/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-apps-script-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%AC%D9%88%D8%AC%D9%84-r746/" rel="">Apps Scripts</a> من خيار الإضافات Extensions ثم برمجة تطبيقات Google كما في الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169042" href="https://academy.hsoub.com/uploads/monthly_2025_03/6-create-app-scripts-file.png.dc618903a08e854c311aa0af14a58f10.png" rel=""><img alt="007 create app scripts file" class="ipsImage ipsImage_thumbnailed" data-fileid="169042" data-unique="2uizngoje" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/6-create-app-scripts-file.png.dc618903a08e854c311aa0af14a58f10.png"> </a>
</p>

<p>
	سيُنشَأ ملف بلغة برمجة تطبيقات جوجل Apps Scripts ويُربط بملف جداول البيانات:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169043" href="https://academy.hsoub.com/uploads/monthly_2025_03/7-apps-scripts-ui.png.9c24a81150d6f9eb19e6fda753c1ccae.png" rel=""><img alt="008 apps scripts ui" class="ipsImage ipsImage_thumbnailed" data-fileid="169043" data-unique="7cz197xzy" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/7-apps-scripts-ui.thumb.png.b0e7fd04a443dd336ea0374090f4cd68.png"> </a>
</p>

<p>
	هنا يمكننا فعل أي شيء بملف جدول البيانات برمجيًا، وحتى الوصول إلى بقية خدمات جوجل وتطبيقاتها مثل البريد Gmail ومستندات جوجل، ويمكننا أيضًا التواصل مع خدمات خارجية وهو ما نريد فعله بربط جدول البيانات مع بوت التلغرام الذي أنشأناه.<br>
	نلاحظ في الجزء الأيمن في قسم الملفات وجود ملف باسم <strong>الرمز.gs</strong>، نلاحظ أيضًا أن هذه البيئة تشبه <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D9%83%D8%A7%D9%85%D9%84%D8%A9-ide-r1513/" rel="">بيئة التطوير</a> إذ توفر لنا مكانًا لكتابة الكود وتنفيذه من الخيارات في الشريط العلوي.<br>
	<strong>ملاحظة</strong>: يمكن تغيير اسم المشروع من الأعلى إلى اسم مناسب وهنا سنضعه بنفس اسم البوت.<br>
	لنجرب بدايةً عرض ناتج تنفيذ الواجهة البرمجية <a href="https://core.telegram.org/bots/api#getme" rel="external nofollow"><code>getMe</code></a> في هذه البيئة، لذا نغيّر اسم الدالة <code>myFunction</code> إلى <code>getMe</code> مثلًا ونكتب محتواها البرمجي التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4199_17" style=""><span class="kwd">function</span><span class="pln"> getMe</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">   
 </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="typ">UrlFetchApp</span><span class="pun">.</span><span class="pln">fetch</span><span class="pun">(</span><span class="str">'https://api.telegram.org/bot7666156735:AAFlJYPgOzT5cTf8CfRx_5vf0GNuVmcvAuk/getMe'</span><span class="pun">);</span><span class="pln">

 </span><span class="typ">Logger</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">getContentText</span><span class="pun">());</span><span class="pln">    
</span><span class="pun">}</span><span class="pln">  </span></pre>

<p>
	استعملنا الدالة <code>UrlFetchApp.fetch</code> لجلب محتوى رابط أو واجهة برمجية، ومررنا لها رابط الواجهة البرمجية ‎<a href="https://core.telegram.org/bots/api#getme" rel="external nofollow"><code>/getMe</code></a> كاملًا مع رمز الوصول access token، ثم استعملنا <code>Logger.log</code> لطباعة الناتج.<br>
	نضغط على زر تنفيذ في الأعلى، وبما أننا ننفذ هذا السكربت لأول مرة فسيُطلَب منا إعطاءه صلاحيات محددة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169044" href="https://academy.hsoub.com/uploads/monthly_2025_03/8-apps-scripts-permissions.png.04f8fd4ca7d23ba94b9c139611664afd.png" rel=""><img alt="009 apps scripts permissions" class="ipsImage ipsImage_thumbnailed" data-fileid="169044" data-unique="8aj7y7rha" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/8-apps-scripts-permissions.thumb.png.7b68b5d29bc1548d775b03ed81ddcfb4.png"> </a>
</p>

<p>
	نضغط على مراجعة الأذونات، ثم نحدد حسابنا ونضغط بعدها على السماح بعد مراجعة الأذونات المطلوبة، وسنجد بعدها ناتج تنفيذ الدالة <code>getMe</code> ضمن السكربت في قسم <strong>سجل التنفيذ</strong> في الأسفل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169045" href="https://academy.hsoub.com/uploads/monthly_2025_03/9-getme-output.png.5bef57e2944243e025ee8735b0a65040.png" rel=""><img alt="009 getme output" class="ipsImage ipsImage_thumbnailed" data-fileid="169045" data-unique="ug6oihgt7" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/9-getme-output.thumb.png.c6afaeb386b8a5496b5e419cd75f4315.png"> </a>
</p>

<p>
	المعلومات هنا هي نفس المعلومات التي حصلنا عليها من فتح الرابط في المتصفح تمامًا، وبذلك نكون قد نجحنا في اختبار بيئة التطوير وجاهزيتها.<br>
	<strong>ملاحظة</strong>: قد نضطر في كل تعديل إلى ضغط زر الحفظ وهو بجانب زر التنفيذ في الشريط العلوي أو ضغط الاختصار ctrl + s لحفظ المشروع.<br>
	سنعيد تنظيم الشيفرة قليلًا ليسهل علينا استدعاء بقية الواجهات البرمجية بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4199_24" style=""><span class="kwd">const</span><span class="pln"> token </span><span class="pun">=</span><span class="pln"> </span><span class="str">'7666156735:AAFlJYPgOzT5cTf8CfRx_5vf0GNuVmcvAuk'</span><span class="pun">;</span><span class="pln">    
</span><span class="kwd">const</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> </span><span class="str">'https://api.telegram.org/bot'</span><span class="pln"> \+ token</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> getMe</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">    
 </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="typ">UrlFetchApp</span><span class="pun">.</span><span class="pln">fetch</span><span class="pun">(</span><span class="pln">url \+ </span><span class="str">'/getMe'</span><span class="pun">);</span><span class="pln">
 </span><span class="typ">Logger</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">getContentText</span><span class="pun">());</span><span class="pln">  
</span><span class="pun">}</span><span class="pln">    </span></pre>

<p>
	يمكن الآن استدعاء <a href="https://core.telegram.org/bots/api#getupdates" rel="external nofollow"><code>‎/getUpdates</code></a> بسهولة بكتابة دالة جديدة باسم <code>getUpdates</code> بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4199_26" style=""><span class="kwd">const</span><span class="pln"> token </span><span class="pun">=</span><span class="pln"> </span><span class="str">'7666156735:AAFlJYPgOzT5cTf8CfRx_5vf0GNuVmcvAuk'</span><span class="pun">;</span><span class="pln">    
</span><span class="kwd">const</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> </span><span class="str">'https://api.telegram.org/bot'</span><span class="pln"> \+ token</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> getMe</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{`</span><span class="pln">    
</span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="typ">UrlFetchApp</span><span class="pun">.</span><span class="pln">fetch</span><span class="pun">(</span><span class="pln">url \+ </span><span class="str">'/getMe'</span><span class="pun">);</span><span class="pln">
</span><span class="typ">Logger</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">getContentText</span><span class="pun">());</span><span class="pln">   
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> getUpdates</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">   
</span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="typ">UrlFetchApp</span><span class="pun">.</span><span class="pln">fetch</span><span class="pun">(</span><span class="pln">url \+ </span><span class="str">'/getUpdates'</span><span class="pun">);</span><span class="pln">
</span><span class="typ">Logger</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">getContentText</span><span class="pun">());</span><span class="pln">  
</span><span class="pun">}</span><span class="pln">    </span></pre>

<p>
	لتنفيذ الدالة، نختار من القائمة الموجودة في الشريط العلوي اسم الدالة، ثم نضغط على تنفيذ ونشاهد الناتج في قسم سجل التنفيذ.<br>
	<strong>ملاحظة</strong>: قد تظهر لنا رسالة تحذير بأن التطبيق غير آمن ولم يتحقق جوجل منه ولا بأس بالإكمال كما توضح الصورة التالية لأننا نحن من نطور السكربت ولكن يجب الحذر إن كان السكريبت من مصدر غير موثوق لأن قد يصل إلى معلومات حساسة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169050" href="https://academy.hsoub.com/uploads/monthly_2025_03/14-google-hasn_t-verify-app-message.png.b9162ef163662b596c8900e437e6ca10.png" rel=""><img alt="014 google hasn t verify app message" class="ipsImage ipsImage_thumbnailed" data-fileid="169050" data-unique="b7uqgqafy" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/14-google-hasn_t-verify-app-message.png.b9162ef163662b596c8900e437e6ca10.png"> </a>
</p>

<h3 id="-8">
	نشر تطبيق ويب يصل بين بوت تلغرام وجداول بيانات جوجل
</h3>

<p>
	فهمنا سابقًا آلية التواصل التي سنطبقها والتي تتم بتواصل بوت التلغرام عبر خطاف ويب web hook مع خادم ويب كلما استلم البوت رسالة جديدة بعدها سيعمل خادم الويب على تخزين البيانات الواردة بعد معالجتها في جدول البيانات كما هو موضح بالصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169046" href="https://academy.hsoub.com/uploads/monthly_2025_03/10-telegram-bot-apps-scripts-google-sheets.png.b2a41a3967c2ce1e0896a8e47c749644.png" rel=""><img alt="10 telegram bot apps scripts google sheets" class="ipsImage ipsImage_thumbnailed" data-fileid="169046" data-unique="hdz10c2o6" style="width: 400px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/10-telegram-bot-apps-scripts-google-sheets.png.b2a41a3967c2ce1e0896a8e47c749644.png"> </a>
</p>

<p>
	السؤال هو كيف سنحصل على <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">خادم ويب</a>؟ يمكننا بناء خادم ويب بأي لغة نريد سواءً بلغة بايثون أو لغة جافاسكربت، أو يمكننا الاستفادة من بيئة تطوير تطبيقات جوجل Apps Script فهي تمكننا من نشر المشروع على أنه تطبيق ويب على الإنترنت مباشرة واستضافته على خوادم جوجل السحابية.
</p>

<p>
	لنشر المشروع، نضغط على زر <strong>النشر </strong>في الأعلى ثم نختار <strong>تطبيق ويب</strong>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169047" href="https://academy.hsoub.com/uploads/monthly_2025_03/11-publish-app-scriptes-wep-app.png.ae30ea7670e801d595adbf892717b6cd.png" rel=""><img alt="011 publish app scriptes wep app" class="ipsImage ipsImage_thumbnailed" data-fileid="169047" data-unique="p52sdfz57" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/11-publish-app-scriptes-wep-app.thumb.png.93cd64167df6f084d9c79012de46228b.png"> </a>
</p>

<p>
	نختار من القائمة التي تظهر من حقل المستخدم الذي لديه الإذن بالوصول إلى عملية النشر الخيار أي شخص لكي يعمل التطبيق ثم نضغط على زر <strong>نشر</strong>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169048" href="https://academy.hsoub.com/uploads/monthly_2025_03/12-publish-app-script-as-web-app.png.c74975932040e9c89f41ee5d81cb4c0b.png" rel=""><img alt="12 publish app script as web app" class="ipsImage ipsImage_thumbnailed" data-fileid="169048" data-unique="916vc59su" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/12-publish-app-script-as-web-app.thumb.png.0f232e53f5bfcdc92884fb1a6da73cc6.png"> </a>
</p>

<p>
	نحصل بعد تمام عملية النشر على رابط URL للتطبيق ويب جاهز ومنشور على الإنترنت، ويمكننا نسخ رابطه ولصقه في المتصفح للتأكد من عمل التطبيق، وإن جربنا في حالتنا فسنحصل على الخطأ التالي:
</p>

<pre class="ipsCode">Script function not found: doGet    
</pre>

<p>
	توضح هذه الرسالة أن السكربت لا يحوي دالة باسم <code>doGet</code> لتنفيذها وهو <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-%D8%B4%D8%B1%D8%AD-%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r74/" rel="">طلب HTTP</a> من النوع get لذا نضيف الدالة <code>doGet</code> إلى السكربت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_12" style=""><span class="kwd">function</span><span class="pln"> doGet</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">HtmlService</span><span class="pun">.</span><span class="pln">createHtmlOutput</span><span class="pun">(</span><span class="str">"Hello "</span><span class="pln"> \+ JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">e</span><span class="pun">));</span><span class="pln">   
</span><span class="pun">}</span><span class="pln"> </span></pre>

<p>
	تعمل الدالة السابقة على إعادة صفحة HTML فيها كلمة مرحبًا Hello وكائن بالمعلومات المرسلة بالطلبية، ولنعد إلى نافذة المتصفح حيث فتحنا رابط التطبيق وحدِّثنا الصفحة ولكن وجدنا أن الرسالة لم تتغير. يحصل ذلك لأننا لم ننشر نسخة جديدة من التطبيق بعد إضافة الدالة الجديدة إليه.<br>
	لنشر نسخة جديدة من التطبيق، نضغط على زر <strong>نشر </strong>في الأعلى ثم نختار <strong>إدارة عمليات النشر</strong> ثم نضغط على زر <strong>التعديل </strong>في قسم الإعداد ثم نختار من قائمة الإصدار خيار <strong>الإصدار الجديد </strong>لنشر إصدار جديد من التطبيق:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169049" href="https://academy.hsoub.com/uploads/monthly_2025_03/13-maneging-apps-scripts-republising.png.cbb1718e53278f3e0874699db11647bc.png" rel=""><img alt="014 maneging apps scripts republising" class="ipsImage ipsImage_thumbnailed" data-fileid="169049" data-unique="l58dtv6b5" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/13-maneging-apps-scripts-republising.thumb.png.7af4e6c30667fb169404d466c8969711.png"> </a>
</p>

<p>
	إن حدثنا الصفحة فسنرى ناتج تنفيذ الدالة <code>doGet</code> التالي:
</p>

<pre class="ipsCode">Hello {"queryString":"","contentLength":-1,"contextPath":"","parameters":{},"parameter":{}}    
</pre>

<p>
	<strong>ملاحظة</strong>: قد يحتاج تطبيق الويب لبعض الوقت بعد نشره أو نشر إصدار جديد منه للعمل.<br>
	أصبحنا الآن جاهزين للانتقال إلى الخطوة التالية وهي ربط خطاف ويب web hook بوت التلغرام وبين تطبيق الويب المنشور الذي أنشأناه للتو.
</p>

<h2 id="-9">
	الخطوة الثالثة: ضبط خطاف الويب بين تطبيق الويب وبوت التلغرام
</h2>

<p>
	يمكن لتطبيق الويب الذي أنشأناه عبر لغة برمجة تطبيقات جوجل أن يستقبل ويعالج طلبات HTTP، وقد رأينا كيف أنه استقبل طلب GET ويمكنه أيضًا أن يستلم طلب POST أيضًا، فعبر ربط خطاف الويب بين تطبيق الويب وبوت التلغرام، سيرسل البوت طلب POST كلما استلم رسالة جديدة.<br>
	ولضبط خطاف الويب في بوت التلغرام، سنستعين بالواجهة البرمجية <a href="https://core.telegram.org/bots/api#setwebhook" rel="external nofollow"><code>setWebhook</code></a> التي تأخذ معاملًا باسم <code>url</code> وهو رابط تطبيق أو خادم الويب الذي ستُرسل إليه التحديثات الواردة من بوت التلغرام.<br>
	نُعرِّف في السكربت دالة جديدةًبالاسم <code>setWebhook</code> والتي سترسل طلبية إلى الواجهة البرمجية تلك مع رابط تطبيق الويب، ويمكن أن نحصل على رابط تطبيق الويب بالضغط على زر <strong>نشر </strong>في الأعلى ثم الضغط على خيار <strong>إدارة عمليات النشر</strong> ثم ننسخ عنوان URL للتطبيق ونضغه في السكريبت بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_15" style=""><span class="pun">…</span><span class="pln">    
</span><span class="kwd">const</span><span class="pln"> webAppUrl </span><span class="pun">=</span><span class="pln"> </span><span class="str">"https://script.google.com/macros/s/AKfycbwkoaVQ6aIWp6R0Pz5Q1EkiIydaKMusCKI7hEUYtPWXau7SLiznaagiAHDqkfiLzhhyXw/exec"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> setWebhook</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">   
 </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="typ">UrlFetchApp</span><span class="pun">.</span><span class="pln">fetch</span><span class="pun">(</span><span class="pln">url \+ </span><span class="str">'/setWebhook?url='</span><span class="pln"> \+ webAppUrl</span><span class="pun">);</span><span class="pln">

 </span><span class="typ">Logger</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">getContentText</span><span class="pun">());</span><span class="pln">  
</span><span class="pun">}</span><span class="pln">    </span></pre>

<p>
	نحصل بعد تنفيذ الدالة <code>setWebhook</code> وحفظ السكربت على الناتج التالي:
</p>

<pre class="ipsCode">{"ok":true,"result":true,"description":"Webhook was set"}    
</pre>

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

<p>
	وللقيام بالمعالجة المطلوبة سنعرِّف دالة باسم <code>doPost</code> التي تعالج طلبيات POST المستقبلة، وسنجرب حاليًا إرسال بريد إلكتروني إلى حسابنا كلما استلمنا رسالة جديدة بالشكل التالي:
</p>

<p>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_17" style=""><span class="kwd">function</span><span class="pln"> doPost</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">   
 </span><span class="typ">GmailApp</span><span class="pun">.</span><span class="pln">sendEmail</span><span class="pun">(</span><span class="typ">Session</span><span class="pun">.</span><span class="pln">getEffectiveUser</span><span class="pun">().</span><span class="pln">getEmail</span><span class="pun">(),</span><span class="pln"> </span><span class="str">"رسالة جديدة من بوت التلغرام"</span><span class="pun">,</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">e</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنحفظ السكريبت ثم ننشر إصدارًا جديدًا من التطبيق والذي سيطلب مرة أخرى قبول أذونات جديدة لأننا أضفنا تطبيقًا جديدًا إلى السكريبت وهو تطبيق البريد <code>GmailApp</code>، لنقبلها بالضغط على رز <strong>تفويض الوصول</strong> ولنجرب مرة أخرى إرسال رسالة إلى البوت.<br>
	إن أرسلنا رسالة إلى البوت، فستصل رسالة إلى البريد تحوي معلومات الطلب كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169051" href="https://academy.hsoub.com/uploads/monthly_2025_03/15-first-telegram-gmail-message.png.5196e507093e8420aaa911837503cdf0.png" rel=""><img alt="016 first telegram gmail message" class="ipsImage ipsImage_thumbnailed" data-fileid="169051" data-unique="a34o7pcos" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/15-first-telegram-gmail-message.thumb.png.4bfa9ee9acde760edcca446845893830.png"> </a>
</p>

<p>
	أصبحت الآن البيانات بحوزتنا وتصلنا إلى تطبيق الويب مباشرة من البوت عند استلامه أي رسالة جديدة، وخطوتنا التالية هي معالجة هذه البيانات وإدخالها ضمن جدول البيانات.
</p>

<h2 id="-10">
	الخطوة الرابعة: إرسال رد على كل رسالة يرسلها بوت التلغرام
</h2>

<p>
	قبل أن نحفظ البيانات في جدول بيانات جوجل، سنعمل على كتابة دالة وظيفتها إرسال رد أو تأكيد على بوت التلغرام ونستفيد منها في التواصل مع البوت والرد عليه كما أرسل لنا تحديثًا.<br>
	كانت خطوة إرسال البريد خطوة تجريبية فقط لعرض المعلومات التي وصلتنا من البوت لنحاول استخلاص بعض المعلومات المهمة منها وهي بالشكل التالي:
</p>

<pre class="ipsCode" id="ips_uid_7588_23">{
    "contextPath": "",
    "queryString": "",
    "contentLength": 295,
    "parameters": {},
    "parameter": {},
    "postData": {
        "contents": "{\"update_id\":412227169,\n\"
message\":{\"message_id\":39,\"from\":{\"id\":957260622,\"is_bot\":false,\"first_name\":\"Jamil\",\"username\":\"jBailony\",\"language_code\":\"en\"},\"chat\":{\"id\":957260622,\"first_name\":\"Jamil\",\"username\":\"jBailony\",\"type\":\"private\"},\"date\":1735667656,\"text\":\"Hi, this is my first message\"}}",
        "length": 295,
        "name": "postData",
        "type": "application/json"
    }
}</pre>

<p>
	ما يهمنا هنا موجود ضمن <code>postData</code> في خاصية اسمها <code>contents</code> ولكنها بصيغة JSON لنفككها إلى كائن بالشكل التالية:
</p>

<pre class="ipsCode"> const contents = JSON.parse(e.postData.contents);   
</pre>

<p>
	والناتج هو كالتالي:
</p>

<pre class="ipsCode">{    
  "update_id": 412227169,    
  "message": {    
    "message_id": 39,    
    "from": {    
      "id": 957260622,    
      "is_bot": false,    
      "first_name": "Jamil",    
      "username": "jBailony",    
      "language_code": "en"    
    },    
    "chat": {    
      "id": 957260622,    
      "first_name": "Jamil",    
      "username": "jBailony",    
      "type": "private"    
    },    
    "date": 1735667656,    
    "text": "Hi, this is my first message"    
  }    
}    
</pre>

<p>
	يلزمنا مُعرِّف المحادثة <code>id</code> والرسالة نفسها التي وصلتنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_27" style=""><span class="pln"> </span><span class="kwd">const</span><span class="pln"> contents </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">contents</span><span class="pun">);</span><span class="pln">   
 </span><span class="kwd">const</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">from</span><span class="pun">.</span><span class="pln">id</span><span class="pun">;</span><span class="pln">   
 </span><span class="kwd">const</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">from</span><span class="pun">.</span><span class="pln">first_name</span><span class="pun">;</span><span class="pln">  </span></pre>

<p>
	نحتاج إلى المُعرِّفلأننا سنعتمد على الواجهة البرمجية <code>‎/sendMessage</code> التي تحتاج إلى المعامل <code>chat_id</code> الذي هو مُعرِّف المحادثة السابق <code>id</code>. لنكتب الدالة <code>sendMessage</code> التي تعتمد على تلك الواجهة البرمجية بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_25" style=""><span class="kwd">function</span><span class="pln"> sendMessage</span><span class="pun">(</span><span class="pln">chatId</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="typ">UrlFetchApp</span><span class="pun">.</span><span class="pln">fetch</span><span class="pun">(</span><span class="pln">$</span><span class="pun">{</span><span class="pln">url</span><span class="pun">}/</span><span class="pln">sendMessage</span><span class="pun">?</span><span class="pln">chat_id</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">chatId</span><span class="pun">}</span><span class="pln">\&amp;text</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">message</span><span class="pun">});</span><span class="pln">
 </span><span class="typ">Logger</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">getContentText</span><span class="pun">());</span><span class="pln">  
</span><span class="pun">}</span><span class="pln">    </span></pre>

<p>
	نلاحظ أن الواجهة البرمجية <code>‎/sendMessage</code> تحتاج أيضًا إلى المعامل <code>text</code> الذي يمثِّل نص الرسالة المراد إرسالها.<br>
	لنستعملها الآن ضمن الدالة <code>doPost</code> بإرسال رد على الرسالة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_34" style=""><span class="kwd">function</span><span class="pln"> doPost</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="typ">GmailApp</span><span class="pun">.</span><span class="pln">sendEmail</span><span class="pun">(</span><span class="typ">Session</span><span class="pun">.</span><span class="pln">getEffectiveUser</span><span class="pun">().</span><span class="pln">getEmail</span><span class="pun">(),</span><span class="pln"> </span><span class="str">"رسالة جديدة من بوت التلغرام"</span><span class="pun">,</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">e</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">));</span><span class="pln">

 </span><span class="kwd">const</span><span class="pln"> contents </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">contents</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">from</span><span class="pun">.</span><span class="pln">id</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">from</span><span class="pun">.</span><span class="pln">first_name</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">text</span><span class="pun">;</span><span class="pln">

 sendMessage</span><span class="pun">(</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> </span><span class="pun">`وصلتنا</span><span class="pln"> </span><span class="pun">رسالتك،</span><span class="pln"> </span><span class="pun">شكرًا</span><span class="pln"> </span><span class="pun">لك</span><span class="pln"> </span><span class="pun">يا</span><span class="pln"> </span><span class="pun">`</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> name</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نحفظ المشروع وننشر إصدارًا جديدًا منه ثم نرسل رسالة إلى البوت وسنجد أنه يرد علينا كما توضح الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169052" href="https://academy.hsoub.com/uploads/monthly_2025_03/16-using-sendMessage-endpoint.png.08506083a4d9a1dd3d1be7d225231dca.png" rel=""><img alt="017 using sendmessage endpoint" class="ipsImage ipsImage_thumbnailed" data-fileid="169052" data-unique="pl7dtf1cl" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/16-using-sendMessage-endpoint.thumb.png.d6082cb8f718c27e132fff6f8922eed5.png"> </a>
</p>

<p>
	ممتاز، كل شيء يعمل على ما يرام، وخطوتنا التالية هي إدخال المعلومات المستلمة ضمن جدول البيانات.
</p>

<h2 id="-11">
	الخطوة الخامسة: حفظ البيانات المستلمة من بوت التلغرام في جدول البيانات
</h2>

<p>
	أصبح لدينا محتوى الرسالة المستلمة من البوت، ونحتاج الآن إلى إدخالها ضمن جدول البيانات ولكن قبل ذلك، لنتعلم استعمال بعض الدوال التي ستلزمنا في التعامل مع جداول بيانات جوجل Google Sheets.
</p>

<h3 id="appsscript">
	التعامل مع جداول بيانات جوجل عبر لغة Apps Script
</h3>

<p>
	سنعتمد على الواجهة <code>SpreadsheetApp</code> في التعامل مع جداول بيانات جوجل، ولنكتب بداية الدالة التالية التي وظيفتها إضافة سطر في جدول البيانات المرتبط بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_37" style=""><span class="kwd">function</span><span class="pln"> appendRow</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{`</span><span class="pln">    
 </span><span class="typ">SpreadsheetApp</span><span class="pun">.</span><span class="pln">getActiveSheet</span><span class="pun">().</span><span class="pln">appendRow</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]);</span><span class="pln">  
</span><span class="pun">}</span></pre>

<p>
	إن حفظنا السكريبت ثم نفذنا الدالة <code>appendRow</code> فسنحصل على الناتج التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169053" href="https://academy.hsoub.com/uploads/monthly_2025_03/17-append-row-to-sheet.png.6065015624c9cbc5bdf838c1b81fa418.png" rel=""><img alt="018 append row to sheet" class="ipsImage ipsImage_thumbnailed" data-fileid="169053" data-unique="ostfbj0nq" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/17-append-row-to-sheet.png.6065015624c9cbc5bdf838c1b81fa418.png"> </a>
</p>

<p>
	<strong>ملاحظة</strong>: سيُطلب منا مرة أخرى قبول أذونات جديدة لأننا استعملنا واجهة تطبيق جديد في السكريبت، وهذا سيحصل كلما أضفنا واجهة جديدة لأحد تطبيقات جوجل.<br>
	عملت الطريقة السابقة بشكل صحيح، ولكنها ليست الطريقة الأفضل، فمن الأفضل استعمال رابط ملف جدول البيانات مباشرة في تحديد الملف بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_40" style=""><span class="kwd">const</span><span class="pln"> fileURL </span><span class="pun">=</span><span class="pln"> </span><span class="str">'https://docs.google.com/spreadsheets/d/1LZlRezLubnD_4mGZulQ7RUZ9-itRefmJNSsSizRh8iY/edit'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> appendRow</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  
 </span><span class="typ">SpreadsheetApp</span><span class="pun">.</span><span class="pln">openByUrl</span><span class="pun">(</span><span class="pln">fileURL</span><span class="pun">).</span><span class="pln">appendRow</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]);</span><span class="pln">  
</span><span class="pun">}</span><span class="pln">    </span></pre>

<p>
	أو يمكننا الاعتماد على مُعرِّف الملف الذي نستخلصه من رابط ملف جدول البيانات السابق بالشكل التالي فهذه الطريقة أفضل وأقصر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_43" style=""><span class="kwd">const</span><span class="pln"> fileId </span><span class="pun">=</span><span class="pln"> </span><span class="str">'1LZlRezLubnD_4mGZulQ7RUZ9-itRefmJNSsSizRh8iY'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> appendRow</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="typ">SpreadsheetApp</span><span class="pun">.</span><span class="pln">openById</span><span class="pun">(</span><span class="pln">fileId</span><span class="pun">).</span><span class="pln">appendRow</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نلاحظ أن هذا الأمر سيضيف سطرًا في أول جدول بيانات أو ورقة sheet في الملف، ولكن ماذا لو أردنا إضافة بيانات في ورقة محددة؟ يمكننا استعمال الطريقة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_45" style=""><span class="kwd">const</span><span class="pln"> fileId </span><span class="pun">=</span><span class="pln"> </span><span class="str">'1LZlRezLubnD_4mGZulQ7RUZ9-itRefmJNSsSizRh8iY'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> sheetName </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Sheet1"</span><span class="pun">;</span><span class="pln">


</span><span class="kwd">function</span><span class="pln"> appendRow</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="typ">SpreadsheetApp</span><span class="pun">.</span><span class="pln">openById</span><span class="pun">(</span><span class="pln">fileId</span><span class="pun">).</span><span class="pln">getSheetByName</span><span class="pun">(</span><span class="str">"Sheet1"</span><span class="pun">).</span><span class="pln">appendRow</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	استعملنا <code>getSheetByName</code> مع تمرير اسم الورقة إليها من أجل تعديلها.<br>
	سنتعلم أخيرًا كيفية جلب قيمة محددة من السطر الأخير وإعادة إضافتها في السطر التالي مع زيادتها بقيمة 1 مثلًا وذلك بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_50" style=""><span class="kwd">const</span><span class="pln"> fileId </span><span class="pun">=</span><span class="pln"> </span><span class="str">'1LZlRezLubnD_4mGZulQ7RUZ9-itRefmJNSsSizRh8iY'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> sheetName </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Sheet1"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> appendRow</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> sheet </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SpreadsheetApp</span><span class="pun">.</span><span class="pln">openById</span><span class="pun">(</span><span class="pln">fileId</span><span class="pun">).</span><span class="pln">getSheetByName</span><span class="pun">(</span><span class="str">"incomes"</span><span class="pun">);</span><span class="pln">
 sheet</span><span class="pun">.</span><span class="pln">appendRow</span><span class="pun">([</span><span class="lit">5</span><span class="pun">]);</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> lastValue </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Number</span><span class="pun">(</span><span class="pln">sheet</span><span class="pun">.</span><span class="pln">getRange</span><span class="pun">(</span><span class="pln">sheet</span><span class="pun">.</span><span class="pln">getLastRow</span><span class="pun">(),</span><span class="pln"> </span><span class="lit">1</span><span class="pun">).</span><span class="pln">getValue</span><span class="pun">());</span><span class="pln">
 sheet</span><span class="pun">.</span><span class="pln">appendRow</span><span class="pun">([</span><span class="pln">lastValue </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تعمل هذه الدالة على إضافة قيمة في آخر سطر ثم جلب هذه القيمة باستعمال الدالة <code>getRange</code> التي تأخذ رقم السطر والعمود إذ رقم السطر هو رقم آخر سطر حصلنا عليه من الدالة <code>getLastRow</code> والعمود مررناه يدويًا، وبعد تنفيذ الدالة حصلنا على الناتج المتوقع:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169054" href="https://academy.hsoub.com/uploads/monthly_2025_03/18-append-value-in-sheet.png.b6c9b1bdd86b57ab63d65ede98eb60d2.png" rel=""><img alt="018 append value in sheet" class="ipsImage ipsImage_thumbnailed" data-fileid="169054" data-unique="j0cbt4aek" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/18-append-value-in-sheet.png.b6c9b1bdd86b57ab63d65ede98eb60d2.png"> </a>
</p>

<p>
	هذا يكفي لمقالنا الحالي، وسمكن استكشاف بقية الدوال والواجهات من <a href="https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app" rel="external nofollow">التوثيق الرسمي</a>.
</p>

<h3 id="-12">
	إضافة البيانات المستلمة من بوت التلغرام إلى جدول البيانات
</h3>

<p>
	سنختار صيغة البيانات المرسلة من البوت إلينا حتى نعالجها ولتكن بالشكل التالي:
</p>

<blockquote class="ipsQuote" data-gramm="false" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix" data-gramm="false">
		<p>
			@مصاريف 123 شراء خضار
		</p>
	</div>
</blockquote>

<p>
	تمثِّل @ وما بعدها اسم الورقة ثم يليها رقم ثم يليها الوصف مباشرةً، ولنعتمد أنه إذا لم نحدد اسم الورقة فلتكن افتراضيًا ورقة المصاريف، ونأخذ هذه المعلومات ونضيفها في الورقة المحددة، لذا سنُعدِّل الدالة <code>doPost</code> والدالة <code>appendRow</code> بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_55" style=""><span class="kwd">function</span><span class="pln"> doPost</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="typ">GmailApp</span><span class="pun">.</span><span class="pln">sendEmail</span><span class="pun">(</span><span class="typ">Session</span><span class="pun">.</span><span class="pln">getEffectiveUser</span><span class="pun">().</span><span class="pln">getEmail</span><span class="pun">(),</span><span class="pln"> </span><span class="str">"رسالة جديدة من بوت التلغرام"</span><span class="pun">,</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">e</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">));</span><span class="pln">


 </span><span class="kwd">const</span><span class="pln"> contents </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">contents</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">from</span><span class="pun">.</span><span class="pln">id</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">from</span><span class="pun">.</span><span class="pln">first_name</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">text</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">let</span><span class="pln"> sheetName </span><span class="pun">=</span><span class="pln"> </span><span class="str">"مصاريف"</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">let</span><span class="pln"> itemValue</span><span class="pun">,</span><span class="pln"> itemName</span><span class="pun">;</span><span class="pln">


 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="str">/^@/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">text</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">const</span><span class="pln"> textArray </span><span class="pun">=</span><span class="pln"> text</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">1</span><span class="pun">).</span><span class="pln">split</span><span class="pun">(</span><span class="str">" "</span><span class="pun">);</span><span class="pln">
   sheetName </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
   itemValue </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
   itemName </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">.</span><span class="pln">splice</span><span class="pun">(</span><span class="lit">2</span><span class="pun">).</span><span class="pln">join</span><span class="pun">(</span><span class="str">" "</span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">const</span><span class="pln"> textArray </span><span class="pun">=</span><span class="pln"> text</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">" "</span><span class="pun">);</span><span class="pln">
   itemValue </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
   itemName </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">.</span><span class="pln">shift</span><span class="pun">().</span><span class="pln">join</span><span class="pun">(</span><span class="str">" "</span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">


 appendRow</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">().</span><span class="pln">toLocaleString</span><span class="pun">(),</span><span class="pln"> itemValue</span><span class="pun">,</span><span class="pln"> itemName</span><span class="pun">]);</span><span class="pln">
 sendMessage</span><span class="pun">(</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> </span><span class="pun">`سجلت</span><span class="pln"> </span><span class="pun">المبلغ</span><span class="pln"> </span><span class="pun">والقيمة</span><span class="pln"> </span><span class="pun">في</span><span class="pln"> </span><span class="pun">جدول</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">sheetName</span><span class="pun">}،</span><span class="pln"> </span><span class="pun">شكرًا</span><span class="pln"> </span><span class="pun">لك!`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="kwd">function</span><span class="pln"> appendRow</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> spreadSheet </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SpreadsheetApp</span><span class="pun">.</span><span class="pln">openById</span><span class="pun">(</span><span class="pln">fileId</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> sheet </span><span class="pun">=</span><span class="pln"> spreadSheet</span><span class="pun">.</span><span class="pln">insertSheet</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">);</span><span class="pln">
 sheet</span><span class="pun">.</span><span class="pln">appendRow</span><span class="pun">(</span><span class="pln">data</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	السكربت المكتوب بسيط وهو يتحقق إن كانت اسم الورقة مضافة ضمن الرسالة فيأخذها ويحلل الرسالة وفقًا لذلك أو إن لم تكن مضافة يحلل الرسالة مباشرة ويستخلص منها البيانات، ثم يدخل البيانات مع التاريخ ولاحظ أننا استعملنا الدالة <code>insertSheet</code> في الدالة <code>appendRow</code> وذلك لإضافة الورقة ضمن الملف.<br>
	نحفظ السكربت ونعيد نشره ثم نرسل للبوت الرسالة التالية <strong>@نفقات 10 شراء فواكه</strong> ونجد أنه قد رد علينا بالرسالة <strong>سجلت المبلغ والقيمة في جدول النفقات، شكرًا لك!</strong> وقد أضاف ورقة جديدة بالاسم <strong>نفقات </strong>وأضاف فيها البيانات:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169055" href="https://academy.hsoub.com/uploads/monthly_2025_03/19-save-message-in-sheet.png.5ad35b988950677972afd8a498eb6fce.png" rel=""><img alt="020 save message in sheet" class="ipsImage ipsImage_thumbnailed" data-fileid="169055" data-unique="7282r5kjd" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/19-save-message-in-sheet.png.5ad35b988950677972afd8a498eb6fce.png"> </a>
</p>

<p>
	تعمل عملية الربط بصورة صحيحة إلى الآن ولكن ماذا لو جربنا أن نرسل الرسالة مرة أخرى نفسها؟
</p>

<h2 id="-13">
	الخطوة السادسة: التقاط الأخطاء ومعرفتها وحلها
</h2>

<p>
	إن جربنا إرسال نفس الرسالة للبوت <strong>@نفقات 10 شراء فواكه،</strong> لن يحصل أي شيء ولن يرد علينا البوت بأي رد، وهذا دليل بأنه حصل خطأ ولكن كيف سنعرفه ونحله؟ سنلتقط الخطأ أولًا عبر كتلة <code>try/catch</code> ونرسل داخل <code>catch</code> رسالة إلى البوت بالخطأ الحاصل بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_60" style=""><span class="kwd">const</span><span class="pln"> chatId </span><span class="pun">=</span><span class="pln"> </span><span class="lit">957260622</span><span class="pun">;</span><span class="pln">


</span><span class="kwd">function</span><span class="pln"> doPost</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">GmailApp</span><span class="pun">.</span><span class="pln">sendEmail</span><span class="pun">(</span><span class="typ">Session</span><span class="pun">.</span><span class="pln">getEffectiveUser</span><span class="pun">().</span><span class="pln">getEmail</span><span class="pun">(),</span><span class="pln"> </span><span class="str">"رسالة جديدة من بوت التلغرام"</span><span class="pun">,</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">e</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">));</span><span class="pln">


   </span><span class="kwd">const</span><span class="pln"> contents </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">contents</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">const</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">from</span><span class="pun">.</span><span class="pln">id</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">const</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">from</span><span class="pun">.</span><span class="pln">first_name</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">const</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">text</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">let</span><span class="pln"> sheetName </span><span class="pun">=</span><span class="pln"> </span><span class="str">"مصاريف"</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">let</span><span class="pln"> itemValue</span><span class="pun">,</span><span class="pln"> itemName</span><span class="pun">;</span><span class="pln">


   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="str">/^@/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">text</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> textArray </span><span class="pun">=</span><span class="pln"> text</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">1</span><span class="pun">).</span><span class="pln">split</span><span class="pun">(</span><span class="str">" "</span><span class="pun">);</span><span class="pln">
     sheetName </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
     itemValue </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
     itemName </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">.</span><span class="pln">splice</span><span class="pun">(</span><span class="lit">2</span><span class="pun">).</span><span class="pln">join</span><span class="pun">(</span><span class="str">" "</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> textArray </span><span class="pun">=</span><span class="pln"> text</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">" "</span><span class="pun">);</span><span class="pln">
     itemValue </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
     itemName </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">.</span><span class="pln">shift</span><span class="pun">().</span><span class="pln">join</span><span class="pun">(</span><span class="str">" "</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">


   appendRow</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="typ">String</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">().</span><span class="pln">toLocaleString</span><span class="pun">()),</span><span class="pln"> itemValue</span><span class="pun">,</span><span class="pln"> itemName</span><span class="pun">]);</span><span class="pln">
   sendMessage</span><span class="pun">(</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> </span><span class="pun">`سجلت</span><span class="pln"> </span><span class="pun">المبلغ</span><span class="pln"> </span><span class="pun">والقيمة</span><span class="pln"> </span><span class="pun">في</span><span class="pln"> </span><span class="pun">جدول</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">sheetName</span><span class="pun">}،</span><span class="pln"> </span><span class="pun">شكرًا</span><span class="pln"> </span><span class="pun">لك!`);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   sendMessage</span><span class="pun">(</span><span class="pln">chatId</span><span class="pun">,</span><span class="pln"> </span><span class="pun">`حصل</span><span class="pln"> </span><span class="pun">خطأ`);</span><span class="pln">
   sendMessage</span><span class="pun">(</span><span class="pln">chatId</span><span class="pun">,</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">e</span><span class="pun">?.</span><span class="pln">message </span><span class="pun">??</span><span class="pln"> e</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">));</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أخذنا رقم المحادثة وحفظناه في المتغير <code>chatId</code> الذي حصلنا عليه من المعلومات المرسلة إلينا في البريد والسبب أن الخطأ قد يحصل في أي مكان، فربما يحصل في البداية ويضيع رقم المحادثة وبالتالي لن يصلنا البريد.<br>
	نلاحظ أيضًا أننا استعملنا التعبير <code>e?.message ?? e</code> وذلك لاستخراج نص الرسالة مباشرة وسبب الخطأ فلا نريد أن تصلنا الكثير من البيانات في الرسالة.<br>
	إن جربنا حفظ السكريبت وإعادة نشر التطبيق ثم إرسال نفس الرسالة للبوت وهي <strong>@نفقات 10 شراء فواكه</strong>، فسنلاحظ أنه لم يتغير شيء ولم تصلنا رسالة بالخطأ الحاصل، وبعد التحقق من السكربت نجد أن الخطأ في الدالة <code>sendMessage</code> إذ يجب ترميز نص الرسالة عبر <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent" rel="external nofollow"><code>encodeURIComponent</code></a> قبل إرسالها في حال إرسال كائن أو شيفرة برمجية وبعد التعديل على الدالة تصبح بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_62" style=""><span class="kwd">function</span><span class="pln"> sendMessage</span><span class="pun">(</span><span class="pln">chatId</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="typ">UrlFetchApp</span><span class="pun">.</span><span class="pln">fetch</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">url</span><span class="pun">}/</span><span class="pln">sendMessage</span><span class="pun">?</span><span class="pln">chat_id</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">chatId</span><span class="pun">}&amp;</span><span class="pln">text</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">encodeURIComponent</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)}`);</span><span class="pln">
 </span><span class="typ">Logger</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">getContentText</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نعيد نشر التطبيق ثم إرسال نفس الرسالة وحينها نحصل على الرد التالي من البوت:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="169056" href="https://academy.hsoub.com/uploads/monthly_2025_03/20-debug-error-code-and-message.png.2f6c7351836b23f154f9ea0143914c67.png" rel=""><img alt="021 debug error code and message" class="ipsImage ipsImage_thumbnailed" data-fileid="169056" data-unique="3pozryi9a" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/20-debug-error-code-and-message.png.2f6c7351836b23f154f9ea0143914c67.png"> </a>
</p>

<p>
	تقول رسالة الخطأ بأن هنالك ورقة بنفس الاسم <strong>نفقات </strong>موجودة سابقًا ولا يمكن إنشاء ورقة جديدة بالاسم نفسه. عرفنا الآن سبب الخطأ ويمكننا حله بتعديل الدالة <code>appendRow</code> بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_65" style=""><span class="kwd">function</span><span class="pln"> appendRow</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> spreadSheet </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SpreadsheetApp</span><span class="pun">.</span><span class="pln">openById</span><span class="pun">(</span><span class="pln">fileId</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> sheet </span><span class="pun">=</span><span class="pln"> spreadSheet</span><span class="pun">.</span><span class="pln">getSheetByName</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">)</span><span class="pln"> </span><span class="pun">?</span><span class="pln">  spreadSheet</span><span class="pun">.</span><span class="pln">getSheetByName</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> spreadSheet</span><span class="pun">.</span><span class="pln">insertSheet</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">);</span><span class="pln">
 sheet</span><span class="pun">.</span><span class="pln">appendRow</span><span class="pun">(</span><span class="pln">data</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تحققنا أن الورقة موجودة، فإن كانت موجودة نستعملها عبر <code>getSheetByName</code> ونضيف فيها أو ننشئها من جديد عبر <code>insertSheet</code> وبذلك تُحل المشكلة إن أعدنا إرسال الرسالة من جديدة وطبعًا بعد نشر إصدار جديد من تطبيق الويب.ن
</p>

<h2>
	الشيفرة الكاملة للبوت
</h2>

<p>
	فيما يلي الشيفرة الكاملة للبوت بعد الانتهاء منه وتنفيذ كافة الخطوات
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7588_67" style=""><span class="kwd">const</span><span class="pln"> token </span><span class="pun">=</span><span class="pln"> </span><span class="str">'7666156735:AAFlJYPgOzT5cTf8CfRx_5vf0GNuVmcvAuk'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> </span><span class="str">'https://api.telegram.org/bot'</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> token</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> webAppUrl </span><span class="pun">=</span><span class="pln"> </span><span class="str">"https://script.google.com/macros/s/AKfycbwkoaVQ6aIWp6R0Pz5Q1EkiIydaKMusCKI7hEUYtPWXau7SLiznaagiAHDqkfiLzhhyXw/exec"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fileId </span><span class="pun">=</span><span class="pln"> </span><span class="str">'1LZlRezLubnD_4mGZulQ7RUZ9-itRefmJNSsSizRh8iY'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> chatId </span><span class="pun">=</span><span class="pln"> </span><span class="lit">957260622</span><span class="pun">;</span><span class="pln">


</span><span class="kwd">function</span><span class="pln"> getMe</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="typ">UrlFetchApp</span><span class="pun">.</span><span class="pln">fetch</span><span class="pun">(</span><span class="pln">url </span><span class="pun">+</span><span class="pln"> </span><span class="str">'/getMe'</span><span class="pun">);</span><span class="pln">
 </span><span class="typ">Logger</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">getContentText</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="kwd">function</span><span class="pln"> getUpdates</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="typ">UrlFetchApp</span><span class="pun">.</span><span class="pln">fetch</span><span class="pun">(</span><span class="pln">url </span><span class="pun">+</span><span class="pln"> </span><span class="str">'/getUpdates'</span><span class="pun">);</span><span class="pln">
 </span><span class="typ">Logger</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">getContentText</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="kwd">function</span><span class="pln"> doGet</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">HtmlService</span><span class="pun">.</span><span class="pln">createHtmlOutput</span><span class="pun">(</span><span class="str">"Hello "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">e</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="kwd">function</span><span class="pln"> setWebhook</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="typ">UrlFetchApp</span><span class="pun">.</span><span class="pln">fetch</span><span class="pun">(</span><span class="pln">url </span><span class="pun">+</span><span class="pln"> </span><span class="str">'/setWebhook?url='</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> webAppUrl</span><span class="pun">);</span><span class="pln">
 </span><span class="typ">Logger</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">getContentText</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="kwd">function</span><span class="pln"> appendRow</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> spreadSheet </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SpreadsheetApp</span><span class="pun">.</span><span class="pln">openById</span><span class="pun">(</span><span class="pln">fileId</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> sheet </span><span class="pun">=</span><span class="pln"> spreadSheet</span><span class="pun">.</span><span class="pln">getSheetByName</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">)</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> spreadSheet</span><span class="pun">.</span><span class="pln">getSheetByName</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> spreadSheet</span><span class="pun">.</span><span class="pln">insertSheet</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">);</span><span class="pln">
 sheet</span><span class="pun">.</span><span class="pln">appendRow</span><span class="pun">(</span><span class="pln">data</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="kwd">function</span><span class="pln"> sendMessage</span><span class="pun">(</span><span class="pln">chatId</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> </span><span class="typ">UrlFetchApp</span><span class="pun">.</span><span class="pln">fetch</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">url</span><span class="pun">}/</span><span class="pln">sendMessage</span><span class="pun">?</span><span class="pln">chat_id</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">chatId</span><span class="pun">}&amp;</span><span class="pln">text</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">encodeURIComponent</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)}`);</span><span class="pln">
 </span><span class="typ">Logger</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">getContentText</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="kwd">function</span><span class="pln"> doPost</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="typ">GmailApp</span><span class="pun">.</span><span class="pln">sendEmail</span><span class="pun">(</span><span class="typ">Session</span><span class="pun">.</span><span class="pln">getEffectiveUser</span><span class="pun">().</span><span class="pln">getEmail</span><span class="pun">(),</span><span class="pln"> </span><span class="str">"رسالة جديدة من بوت التلغرام"</span><span class="pun">,</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">e</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">));</span><span class="pln">


   </span><span class="kwd">const</span><span class="pln"> contents </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">postData</span><span class="pun">.</span><span class="pln">contents</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">const</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">from</span><span class="pun">.</span><span class="pln">id</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">const</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">from</span><span class="pun">.</span><span class="pln">first_name</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">const</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> contents</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">text</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">let</span><span class="pln"> sheetName </span><span class="pun">=</span><span class="pln"> </span><span class="str">"مصاريف"</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">let</span><span class="pln"> itemValue</span><span class="pun">,</span><span class="pln"> itemName</span><span class="pun">;</span><span class="pln">


   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="str">/^@/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">text</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> textArray </span><span class="pun">=</span><span class="pln"> text</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">1</span><span class="pun">).</span><span class="pln">split</span><span class="pun">(</span><span class="str">" "</span><span class="pun">);</span><span class="pln">
     sheetName </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
     itemValue </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
     itemName </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">.</span><span class="pln">splice</span><span class="pun">(</span><span class="lit">2</span><span class="pun">).</span><span class="pln">join</span><span class="pun">(</span><span class="str">" "</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> textArray </span><span class="pun">=</span><span class="pln"> text</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">" "</span><span class="pun">);</span><span class="pln">
     itemValue </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
     itemName </span><span class="pun">=</span><span class="pln"> textArray</span><span class="pun">.</span><span class="pln">shift</span><span class="pun">().</span><span class="pln">join</span><span class="pun">(</span><span class="str">" "</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">


   appendRow</span><span class="pun">(</span><span class="pln">sheetName</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="typ">String</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">().</span><span class="pln">toLocaleString</span><span class="pun">()),</span><span class="pln"> itemValue</span><span class="pun">,</span><span class="pln"> itemName</span><span class="pun">]);</span><span class="pln">
   sendMessage</span><span class="pun">(</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> </span><span class="pun">`سجلت</span><span class="pln"> </span><span class="pun">المبلغ</span><span class="pln"> </span><span class="pun">والقيمة</span><span class="pln"> </span><span class="pun">في</span><span class="pln"> </span><span class="pun">جدول</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">sheetName</span><span class="pun">}،</span><span class="pln"> </span><span class="pun">شكرًا</span><span class="pln"> </span><span class="pun">لك!`);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   sendMessage</span><span class="pun">(</span><span class="pln">chatId</span><span class="pun">,</span><span class="pln"> </span><span class="pun">`حصل</span><span class="pln"> </span><span class="pun">خطأ`);</span><span class="pln">
   sendMessage</span><span class="pun">(</span><span class="pln">chatId</span><span class="pun">,</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">e</span><span class="pun">?.</span><span class="pln">message </span><span class="pun">??</span><span class="pln"> e</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">));</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2 id="-14">
	أفكار لتطوير البوت
</h2>

<p>
	انتهينا من تطوير تطبيقنا وربطنا بوت تلغرام مع جداول البيانات بطريقة صحيحة ولكن هنالك الكثير من الأفكار والتطويرات التي يمكن إجراؤها على التطبيق وفيما يلي أبرز الأفكار التحسينية.
</p>

<h3 id="-15">
	تحليل الرسالة المستقبلة من البوت
</h3>

<p>
	لم نعمل على تحليل الرسالة التي يستقبلها البوت، لذا يُفضَّل إضافة خطوة تحقق validation منها، فإن كانت لا تُطابق شروطًا معينة مثلًا لا تحوي على قيمة عددية تمثل المصروف أو النفقات سنرفض الرسالة ونعيد إرسال رسالة للبوت توضح أن هنالك شيء ناقص في الرسالة مع توضيح كيفية استعمال البوت وصيغة التواصل معه.
</p>

<h3 id="-16">
	إضافة قائمة أوامر للبوت
</h3>

<p>
	بعد فترة قد ننسى كيفية استعمال البوت ولا نريد أن تفتح الشيفرة البرمجية لذا يمكن أن نضيف قائمة أوامر commands للبوت عبر ‎<code>/setcommands</code> فيها مثلًا كيفية الاستعمال ليرد علينا البوت بكيفية الاستعمال وغيرها من الأوامر السريعة التي توسِّع عمل البوت وتسهِّل استخدامه.
</p>

<h3 id="-17">
	تحسين تنسيق الرسالة المرسلة للبوت
</h3>

<p>
	أرسلنا في تطبيقنا رسالة نصية عادية text ولكن يمكننا الاستفادة من المعامل <code>parse_mode</code> في الواجهة البرمجية <a href="https://core.telegram.org/bots/api#sendmessage" rel="external nofollow"><code>‎/sendMessage</code></a> لإرسال الرسالة بتنسيق <a href="https://academy.hsoub.com/apps/productivity/%D9%83%D9%8A%D9%81-%D8%AA%D9%83%D8%AA%D8%A8-%D8%A8%D8%B5%D9%8A%D8%BA%D8%A9-%D9%85%D8%A7%D8%B1%D9%83%D8%AF%D8%A7%D9%88%D9%86-%D8%A8%D8%A8%D8%B3%D8%A7%D8%B7%D8%A9-r290/" rel="">مارك داون</a> أو <a href="https://academy.hsoub.com/programming/html/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D8%B3%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D9%88%D9%82%D8%B9-%D8%A7%D9%84%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%A7%D8%AA-html-r1909/" rel="">بتنسيق HTML</a>، وللمزيد انظر <a href="https://core.telegram.org/bots/api#formatting-options" rel="external nofollow">التنسيقات المدعومة</a> في التوثيق.
</p>

<h3 id="-18">
	تحسين تنسيق جدول البيانات وتطويره
</h3>

<p>
	يمكنك إضافة جداول أو أوراق sheets مسبقة وتعديل تنسيقها بصيغة معينة، فنحن لم نركز في هذا المقال على تنسيق الجداول لأنه خارج نطاقنا- كما يمكنك أيضًا إضافة أعمدة أو صفوف جامعة sum أو متوسط حسابي average بحيث نربطها مع أمر في البوت يجلب لنا مجموع المصروف أو النفقات الكلية أو لشهر محدد مثلً،ا وهكذا نطور تطبيق المصاريف والنفقات أو نتوسع وتُطوِّر من التطبيق أكثر ليصبح تطبيق محاسبي يفيد المستخدم. كما يمكننا  إضافة أوامر تجلب لنا صورة عن مخططات شهرية مثلًا ثم إرسال هذه الصورة إلى البوت عبر الواجهة البرمجية <a href="https://core.telegram.org/bots/api#sendphoto" rel="external nofollow"><code>‎/sendPhoto</code></a> وغيرها من الأفكار العديدة الأخرى.
</p>

<h3 id="chatgpt">
	ربط البوت مع ChatGPT
</h3>

<p>
	هذه فكرة مميزة جدًا إذ يمكننا تحليل البيانات ومعالجتها عبر ربط البوت مع أحد نماذج الذكاء الاصطناعي مثل التفاعل مع الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> لروبوت الدردشة الشهير <a href="https://academy.hsoub.com/apps/web/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-chatgpt-api-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%AE%D8%AF%D9%85%D8%A7%D8%AA%D9%83-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-r909/" rel="">ChatGPT</a><span style="display: none;"> </span>، وبهذا يمكن تمرير الرسائل التي يرسلها المستخدم للبوت، وتحويلها إلى ChatGPT للحصول على رد وإعادة الناتج إلى البوت من جديد وبذلك نطوِّر من قدرات البوت وإمكانياته.
</p>

<h2 id="-19">
	خاتمة
</h2>

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

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/apps/productivity/google-drive/google-sheets/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%A3%D9%88%D8%B1%D8%A7%D9%82-%D9%88%D8%A7%D9%84%D9%86%D8%B7%D8%A7%D9%82%D8%A7%D8%AA-%D9%85%D9%86-%D8%AE%D9%84%D8%A7%D9%84-apps-script-r747/" rel="">التعامل مع جداول البيانات والأوراق والنطاقات من خلال Apps Script</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/apps/productivity/google-drive/google-sheets/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-apps-script-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%AC%D9%88%D8%AC%D9%84-r746/" rel="">أساسيات برمجة التطبيقات Apps Script باستخدام جداول بيانات جوجل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/laravel/%D8%A8%D9%86%D8%A7%D8%A1-%D8%A8%D9%88%D8%AA-bot-%D8%AA%D9%84%D8%BA%D8%B1%D8%A7%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%84%D8%A7%D8%B1%D8%A7%D9%81%D9%8A%D9%84-%D9%88%D8%A8%D9%88%D8%AA%D9%85%D8%A7%D9%86-botman-r1740/" rel="">بناء بوت Bot تلغرام باستخدام لارافيل وبوتمان BotMan</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/os-embedded-systems/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%B1%D9%88%D8%A8%D9%88%D8%AA-%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r1855/" rel="">برمجة الروبوت: الدليل الشامل</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2526</guid><pubDate>Thu, 06 Mar 2025 12:01:03 +0000</pubDate></item><item><title>&#x627;&#x62E;&#x62A;&#x628;&#x627;&#x631; &#x645;&#x638;&#x647;&#x631; &#x627;&#x644;&#x645;&#x648;&#x642;&#x639; &#x627;&#x644;&#x645;&#x631;&#x626;&#x64A; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x623;&#x62F;&#x627;&#x62A;&#x64A; Chromatic &#x648; Playwright</title><link>https://academy.hsoub.com/programming/advanced/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D9%85%D8%B8%D9%87%D8%B1-%D8%A7%D9%84%D9%85%D9%88%D9%82%D8%B9-%D8%A7%D9%84%D9%85%D8%B1%D8%A6%D9%8A-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D8%AF%D8%A7%D8%AA%D9%8A-chromatic-%D9%88-playwright-r2500/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_01/ChromaticPlaywright.png.c5b76b6993a488ebb90e71c35c461140.png" /></p>
<p>
	يبذل مطورو الويب عند إنشاء مواقع إلكترونية جهدًا كبيرًا في بناء واجهات أنيقة ويحرصون على تصميم تجربة مستخدم UI سلسة، لكن حتى أجمل المواقع تصميمًا قد يشوبها خلل في التناسق أو أخطاء مرئية، لذا لا يقل اختبار مظهر الموقع visual test أهمية عن اختبار وظائفه وآلية عمله.
</p>

<p>
	سنشرح في هذا المقال كيفية استخدام أداتي <span ipsnoautolink="true">كروماتيك <a href="https://www.chromatic.com/" rel="external nofollow">Chromatic</a> </span> وبلاي رايت <a href="https://playwright.dev/" rel="external nofollow">Playwright</a> في اختبار المواقع المرئي لضمان تناسق واجهات الموقع وخلوها من الأخطاء.
</p>

<p>
	بالطبع لن تفيدنا أدوات الاختبارات الوظيفية مثل الاختبارات الشاملة end-to-end tests أو اختبارات الوحدات <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%87%D9%8A%D9%83%D9%84-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%AE%D9%84%D9%81%D9%8A%D8%A9-%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-unit-tests-r1132/" rel=""> unit tests</a>، فهي ممتازة في اختبار منطق صفحات الموقع وسلوكها، لكنه لا تستطيع اكتشاف الأخطاء في مظهر واجهة المستخدم UI، وذلك لأن الاختبارات الوظيفية عاجزة عن رؤية البكسلات pixels التي تخرجها واجهات المستخدم في مواقعنا.
</p>

<p>
	على سبيل المثال قد يؤدي خطأ في كتابة شيفرة <a href="https://academy.hsoub.com/programming/css/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-css/" rel="">CSS</a> إلى حجب شريط إعلاني مثلًا لزر الشراء. فلو أجرينا اختبارًا وظيفيًا للموقع سيشير أن زر الشراء يعمل تقنيًا فهو قابل للنقر، مع أن المستخدمين لن يتمكنوا من الوصول إليه أصلًا لينقروا عليه.
</p>

<p>
	لذا سنتعرف في هذا المقال على طريقة لمعالجة النتائج الإيجابية الزائفة وكشف أخطاء العرض rendering قبل مرحلة التشغيل باستخدام سير عمل يدعى الاختبار المرئي visual testing، وسنتعلم آلية عمله وتطبيقه عمليًا.
</p>

<h2 id="">
	آلية عمل الاختبار المرئي
</h2>

<p>
	يمكن تصوّر الاختبار المرئي بأنه لقطات تقارن مظهر الموقع قبل وبعد التغيير، إذ يبدأ بالتقاط صورة مثالية للموقع قبل التعديل ويجعلها المرجعية له، وعندما يطرأ أي تغيير في الشيفرة البرمجية يقارن كل بكسل في اللقطة الجديدة بعد التعديل بالبكسل المقابل من اللقطة المرجعية ليكشف أي تغييرات مرئية حدثت.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="gif" data-fileid="166372" href="https://academy.hsoub.com/uploads/monthly_2025_01/001visual-testing-greenbg.gif.1d47781094c4ede7ca9b024bf4dabf34.gif" rel=""><img alt="001visual-testing-greenbg.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="166372" data-ratio="100.00" data-unique="afbmmtwj0" style="width: 300px; height: auto;" width="640" src="https://academy.hsoub.com/uploads/monthly_2025_01/001visual-testing-greenbg.thumb.gif.51d64b6074a3a91ed61243179191e9f2.gif"></a>
</p>

<p>
	إن مهمة أدوات الاختبار المرئي مثل كروماتيك <span ipsnoautolink="true">Chromatic هي </span>أتمتة هذه العمليات التي تشمل أخذ اللقطات، وإجراء التحقق من الاختلافات في جميع واجهات المستخدم في الموقع.
</p>

<p>
	يتمثل سير العمل في أداة كروماتيك <span ipsnoautolink="true">Chromatic </span>بأربع خطوات :
</p>

<ol>
	<li>
		<strong>العرض السحابي</strong> <strong>Cloud Rendering</strong>: أداة كروماتيك واجهة المستخدم لديك في متصفح سحابي
	</li>
	<li>
		<strong>أخذ لقطات</strong>: تأخذ كروماتيك لقطة عند كل اختبار، وتجري جميع الاختبارات بآن واحد لتوفير الوقت
	</li>
	<li>
		<strong>المقارنة المؤتمتة</strong>: تولّد كروماتيك لقطات جديدة كلما حدّثنا الشيفرة البرمجية وتقارنها باللقطات المرجعية
	</li>
	<li>
		<strong>المراجعة والتحقق</strong>: عندما ترصد كروماتيك تغييرات معنية تطلب مراجعتها للتأكد من أنها تغييرات متعمدة، وفي حال وجدت تغييرات غير متوقعة فإنها تفعّل إشعارات تنبهك حتى تسرع في معالجتها
	</li>
</ol>

<p>
	دعنا نقدم مثالًا عمليًا عن هذه التقنية، سنختبر واجهة المستخدم لصفحة الويب التي شُرحت في مقال <a href="https://tympanus.net/codrops/2023/11/08/dynamic-tooltip-reveal-animations/" rel="external nofollow">Dynamic Tooltip Reveal Animations</a>. والتي تتوفر شيفرتها البرمجية على <a href="https://github.com/winkerVSbecks/PixelGooeyTooltip" rel="external nofollow">غيت هب</a>. فهي مصمَّمة بتخطيط شبكي وتتضمن عناصر متنوعة، وعندما نمرر المؤشر فوق الروابط في المحتوى الرئيسي تظهر أدوات تلميح tooltips متحركة.
</p>

<p>
	لفهم المشروع أكثر يمكن الحصول على الشيفرة البرمجية لهذه الصفحة وتشغيلها من خلال التعليمات التالية:
</p>

<pre class="ipsCode">$ npx degit winkerVSbecks/PixelGooeyTooltip#starting-point PixelGooeyTooltip
$ cd PixelGooeyTooltip
$ npm install
</pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="166364" href="https://academy.hsoub.com/uploads/monthly_2025_01/002tooltip.png.b2d5ea74704944d330fa7d35bb83e28e.png" rel=""><img alt="002tooltip" class="ipsImage ipsImage_thumbnailed" data-fileid="166364" data-unique="t4le74zzy" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_01/002tooltip.png.b2d5ea74704944d330fa7d35bb83e28e.png"> </a>
</p>

<h2>
	كيف ندمج أداتي Chromatic و Playwright
</h2>

<p>
	يمكننا دمج أداة كروماتيك Chromatic بسلاسة مع أدوات الاختبار الرائجة مثل <a href="https://www.chromatic.com/storybook?ref=chromaticblog.ghost.io" rel="external nofollow">Storybook</a> و<a href="https://www.chromatic.com/playwright?ref=chromaticblog.ghost.io" rel="external nofollow">Playwright</a> و<a href="https://www.chromatic.com/cypress?ref=chromaticblog.ghost.io" rel="external nofollow">Cypress</a>. سنستفيد من الدمج بين أداتي كروماتيك Chromatic وبلاي رايت Playwright لإجراء اختبارات مرئية على الصفحات الثابتة المكتوبة بلغة HTML مما يضمن استقرار التصميم عبر مختلف التغييرات، أما أداة ستوري بوك Storybook فهي الأداة المثلى لاختبار المواقع المبينة بالاعتماد على المكونات component- based حيث توفر بيئة متكاملة لاختبار وتوثيق واجهات المستخدم.
</p>

<p>
	أما Playwright، فهي أداة مفتوحة المصدر تؤتمت الاختبارات الشاملة من خلال محاكاة تفاعلات المستخدم، مثل النقرات وتحريك المؤشر وإدخال النصوص مباشرة في المتصفح، مما يضمن دقة الأداء عبر مختلف البيئات والمنصات.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="166365" href="https://academy.hsoub.com/uploads/monthly_2025_01/003playwrightworkflow.png.6a9c0e5fede281f861bcf429937adc12.png" rel=""><img alt="003playwrightworkflow" class="ipsImage ipsImage_thumbnailed" data-fileid="166365" data-unique="w9eo8f1w1" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_01/003playwrightworkflow.png.6a9c0e5fede281f861bcf429937adc12.png"> </a>
</p>

<p>
	أثناء إجراء بلاي رايت لاختباراتها تعمل أداة كروماتيك خلف الكواليس، إذ تلتقط أرشيفًا لكل صفحة متضمنًا تنسيقها وأصولها وشجرة <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">DOM</a> الخاصة بها، ثم ترفعه إلى السحابة حيث تولّد لقطات وتجري مقارنات لكل بكسل لتحديد أية تغييرات مرئية غير متعمدة.
</p>

<h2 id="-1">
	سير العمل
</h2>

<p>
	سنجزئ سير العمل إلى قسمين، سنبدأ في الجزء الأول بإعداد أداة بلاي رايت وكتابة اختبارات شاملة لتفعيل أداة التلميح tooltip، ثم سنستخدم أداة كروماتيك في الجزء الثاني لتحويل تلك الاختبارات الشاملة إلى اختبارات مرئية.
</p>

<h3 id="-2">
	إعداد أداة بلاي رايت
</h3>

<p>
	لننفّذ الأمر التالي لإعداد أداة بلاي رايت:
</p>

<pre class="ipsCode">$ npm init playwright@latest
</pre>

<p>
	سيضيف هذا الأمر أداة بلاي رايت إلى ملف <code>package.json</code> ويولّد ملف <code>playwright.config.js</code> وهو ملف الإعدادات الذي يتيح لنا تخصيص طريقة تشغيل الاختبارات، مثل المتصفحات المستهدفة، وطرق التعامل مع المهام المتزامنة، وخيارات أخرى. وينشئ مجلد باسم <code>tests</code> متضمنًا مثالًا بسيطًا للاختبار، كما سيضيف مجلد <code>tests-examples</code> لمزيد من الأمثلة.
</p>

<h3 id="-3">
	كتابة اختبار شامل
</h3>

<p>
	سنعيد تسمية الملف <code>tests/example.spec.js</code> ليصبح <code>tests/segmented-tooltip.spec.js</code> ونعدّل محتوياته وفق الخطوات التالية كي يختبر عنوان الصفحة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1810_16" style=""><span class="com">// tests/segmented-tooltip.spec.js</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> test</span><span class="pun">,</span><span class="pln"> expect </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'@playwright/test'</span><span class="pun">);</span><span class="pln">

test</span><span class="pun">(</span><span class="str">'has title'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> page </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> page</span><span class="pun">.</span><span class="kwd">goto</span><span class="pun">(</span><span class="str">'http://127.0.0.1:8080'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> expect</span><span class="pun">(</span><span class="pln">page</span><span class="pun">).</span><span class="pln">toHaveTitle</span><span class="pun">(</span><span class="str">/Segmented Tooltip Animation/</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	ننفّذ الأمر <code>. npx http-server</code> لتشغيل الموقع على خادم تطوير محلي، ثم نشغل اختبارات بلاي رايت باستخدام الأمر:
</p>

<pre class="ipsCode">$ npx playwright test
</pre>

<p>
	تظهر نتائج الاختبارات عادة على الطرفية، وسنلاحظ وجود نتائج 3 اختبارات في الخرج، ويرجع ذلك إلى أن أداة بلاي رايت تجري افتراضيًا كل اختبار في 3 متصفحات هي Chromium و WebKit و Firefox.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="166366" href="https://academy.hsoub.com/uploads/monthly_2025_01/004playwrighttestoutput.png.c96428a451b9e18d604184ce13df029d.png" rel=""><img alt="004playwrighttestoutput" class="ipsImage ipsImage_thumbnailed" data-fileid="166366" data-unique="tb94qwh1t" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_01/004playwrighttestoutput.png.c96428a451b9e18d604184ce13df029d.png"> </a>
</p>

<h3 id="-4">
	اختبار أدوات التلميح التي تنبثق عند تمرير المؤشر
</h3>

<p>
	أنجزنا في الفقرة السابقة اختبار للتحقق من عنوان الموقع، وسنضيف الآن اختبار آخر بأداة Playwright آخر كي يتحقق من مكان ظهور أحد أدوات التلميح tooltip التي تنبثق عند تمرير المؤشر فوق عنصر محدد.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1810_19" style=""><span class="com">// tests/segmented-tooltip.spec.js</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> test</span><span class="pun">,</span><span class="pln"> expect </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'@playwright/test'</span><span class="pun">);</span><span class="pln">

test</span><span class="pun">(</span><span class="str">'has title'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> page </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> page</span><span class="pun">.</span><span class="kwd">goto</span><span class="pun">(</span><span class="str">'http://127.0.0.1:8080'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> expect</span><span class="pun">(</span><span class="pln">page</span><span class="pun">).</span><span class="pln">toHaveTitle</span><span class="pun">(</span><span class="str">/Segmented Tooltip Animation/</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

test</span><span class="pun">(</span><span class="str">'displays tooltip'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> page </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> page</span><span class="pun">.</span><span class="kwd">goto</span><span class="pun">(</span><span class="str">'http://127.0.0.1:8080'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> page</span><span class="pun">.</span><span class="pln">locator</span><span class="pun">(</span><span class="str">'css=.trigger'</span><span class="pun">).</span><span class="pln">first</span><span class="pun">().</span><span class="pln">hover</span><span class="pun">({</span><span class="pln"> force</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">});</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> expect</span><span class="pun">(</span><span class="pln">
    page</span><span class="pun">.</span><span class="pln">locator</span><span class="pun">(</span><span class="str">'css=#tooltip-1 .tooltip__content-desc.glitch'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">).</span><span class="pln">toHaveCSS</span><span class="pun">(</span><span class="str">'opacity'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	سنشغل الاختبارات هذه المرة في <a href="https://playwright.dev/docs/test-ui-mode" rel="external nofollow">وضع واجهة المستخدم</a> لنشاهد فعليًا كيف تعمل في المتصفح.
</p>

<pre class="ipsCode">$ npx playwright test --ui
</pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="166367" href="https://academy.hsoub.com/uploads/monthly_2025_01/005playwrighttest.png.a286c5aa42dc5dc887ecf21e898f51f0.png" rel=""><img alt="005playwrighttest" class="ipsImage ipsImage_thumbnailed" data-fileid="166367" data-unique="is9dy39ui" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_01/005playwrighttest.png.a286c5aa42dc5dc887ecf21e898f51f0.png"> </a>
</p>

<p>
	أصبحت اختباراتنا الشاملة تعمل، دعنا نرى الآن كيف سنستخدمها للاختبار المرئي.
</p>

<h3 id="-5">
	إنشاء مشروع جديد
</h3>

<p>
	نحتاج للاشتراك بحساب كروماتيك مجاني باستخدام حسابنا في GitHub أو GitLab أو Bitbucket أو باستخدام البريد الإلكتروني، يمنحنا هذا الحساب 5000 لقطة مجانية في الشهر. نضغط بعدها على إضافة مشروع أو Add project ثم نتبع التعليمات لإنشاء مشروع جديد، وفي النهاية ننسخ المفتاح token الخاص بمشروعنا، وفق التالي:
</p>

<p style="text-align: center;">
	<img alt="006codrops-auth-and-create-project.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="166373" data-ratio="67.50" data-unique="ghziieu1w" style="width: 400px; height: auto;" width="640" src="https://academy.hsoub.com/uploads/monthly_2025_01/006codrops-auth-and-create-project.gif.fc812cb0fe33e3ec93dbc0749ea40458.gif">
</p>

<h3 id="-6">
	إضافة كروماتيك إلى اختبارات بلايرايت
</h3>

<p>
	نحتاج بعدها لتثبيت الحزم الخاصة بأداة كروماتيك
</p>

<pre class="ipsCode">$ npm install -D chromatic @chromatic-com/playwright
</pre>

<p>
	ثم نبدل أدوات الاختبار الخاصة ببلاي رايت بالأدوات الخاصة بكروماتيك وذلك بإضافة <code>@chromatic-com/playwright</code>ضمن الشيفرة وفق ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1810_21" style=""><span class="com">// tests/segmented-tooltip.spec.js</span><span class="pln">

</span><span class="com">// <span class="ipsEmoji">➖</span> Remove this line</span><span class="pln">
</span><span class="com">// import { test, expect } from '@playwright/test';</span><span class="pln">
</span><span class="com">// <span class="ipsEmoji">➕</span> Add this line</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> test</span><span class="pun">,</span><span class="pln"> expect </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"@chromatic-com/playwright"</span><span class="pun">;</span><span class="pln">

test</span><span class="pun">(</span><span class="str">'has title'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> page </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> page</span><span class="pun">.</span><span class="kwd">goto</span><span class="pun">(</span><span class="str">'http://127.0.0.1:8080'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> expect</span><span class="pun">(</span><span class="pln">page</span><span class="pun">).</span><span class="pln">toHaveTitle</span><span class="pun">(</span><span class="str">/Segmented Tooltip Animation/</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

test</span><span class="pun">(</span><span class="str">'displays tooltip'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> page </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> page</span><span class="pun">.</span><span class="kwd">goto</span><span class="pun">(</span><span class="str">'http://127.0.0.1:8080'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> page</span><span class="pun">.</span><span class="pln">locator</span><span class="pun">(</span><span class="str">'css=.trigger'</span><span class="pun">).</span><span class="pln">first</span><span class="pun">().</span><span class="pln">hover</span><span class="pun">({</span><span class="pln"> force</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">});</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> expect</span><span class="pun">(</span><span class="pln">
    page</span><span class="pun">.</span><span class="pln">locator</span><span class="pun">(</span><span class="str">'css=#tooltip-1 .tooltip__content-desc.glitch'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">).</span><span class="pln">toHaveCSS</span><span class="pun">(</span><span class="str">'opacity'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'1'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	وهذا كل ما علينا فعله، فبتغيير سطر واحد من الشيفرة البرمجية نستطيع تحويل هذه الاختبارات الشاملة إلى اختبارات مرئية.
</p>

<h3 id="-7">
	إجراء اختبارات مرئية
</h3>

<p>
	نشغل اختبارات بلاي رايت كما نفعل عادة، وأثناء عملها تلتقط كروماتيك أرشيفًا لصفحة الموقع عند كل اختبار.
</p>

<pre class="ipsCode">$ npx playwright test
</pre>

<p>
	ثم نستخدم مفتاح المشروع token وننفذ الأمر التالي في مجلده، عندها سترفع كروماتيك الملف المضغوط الخاص بالمشروع إلى بنيتها التحتية السحابية وتنفذ الاختبارات المرئية.
</p>

<pre class="ipsCode">$ npx chromatic --playwright -t=&lt;TOKEN&gt;
</pre>

<p>
	سنستلم رسالة تأكيد بنجاح إجراء الاختبارات عندما ينتهي تنفيذ الأمر السابق، وبما أن هذه أول مرة نجري فيها الاختبارات ستنشئ كروماتيك اللقطات المرجعية لهذه الاختبارات.
</p>

<pre class="ipsCode">✔ Started build 1
  → Continue setup at https://www.chromatic.com/setup?appId=...
✔ Build 1 auto-accepted
  → Tested 2 stories across 2 components; captured 2 snapshots in 17 seconds
</pre>

<h3 id="-8">
	ملاحظة التغيرات المرئية
</h3>

<p>
	الآن بعد أن أنشأنا اللقطات المرجعية ستلاحظ كروماتيك أية تغيرات تطرأ على واجهة المستخدم هذه. لنفترض أننا عدلنا ألوان خلفية أداة التلميح tooltip ونصها في ملف <code>tooltip.css</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_1810_23" style=""><span class="com">/* css/tooltip.css */</span><span class="pln">

</span><span class="pun">.</span><span class="pln">tooltip </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">--</span><span class="kwd">tt-width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">200px</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">--</span><span class="kwd">tt-height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">250px</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">--</span><span class="kwd">tt-columns</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">--</span><span class="kwd">tt-rows</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">--</span><span class="kwd">tt-bg-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#FF6B6C</span><span class="pun">;</span><span class="pln"> </span><span class="com">/* <span class="ipsEmoji">👈</span> this one */</span><span class="pln">
    </span><span class="pun">--</span><span class="kwd">tt-text-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#000</span><span class="pun">;</span><span class="pln"> </span><span class="com">/* <span class="ipsEmoji">👈</span> and this one */</span><span class="pln">

  </span><span class="com">/* ... */</span></pre>

<p>
	الآن لنشغل الاختبار مرة أخرى:
</p>

<pre class="ipsCode"># First Playwright
$ npx playwright test
# Then Chromatic
$ npx chromatic --playwright -t=&lt;TOKEN&gt;
</pre>

<p>
	إذا كان هناك تغيير بصري، ستزودنا كروماتيك بموجز للتغيرات التي جرت، نضغط الرابط لمعاينتها.
</p>

<pre class="ipsCode">✖ Found 1 visual change: Review the changes at https://www.chromatic.com/build?appId=...&amp;number=3
</pre>

<p>
	يمكننا مراجعة التغيير عبر الرابط المقدم من Chromatic.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="166368" href="https://academy.hsoub.com/uploads/monthly_2025_01/007displaytooltip.png.e941e924625df9cea263587e053cb084.png" rel=""><img alt="007displaytooltip" class="ipsImage ipsImage_thumbnailed" data-fileid="166368" data-unique="hyl2p6lio" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_01/007displaytooltip.png.e941e924625df9cea263587e053cb084.png"> </a>
</p>

<p>
	الغاية من ذلك مراجعة هذه التغييرات للتحقق ما إذا كانت متعمَّدة أو خاطئة. عندما نقبل جميع التغييرات سيشار إلى النسخة الحالية بأنها مقبولة passed<span class="ipsEmoji">🟢</span>. عندها ستُحدَّث اللقطات المرجعية لهذه الاختبارات، وهذا سيضمن بأننا نقارن اللقطات التي ستُأخذ في الاختبارات المستقبلية بأحدث نسخة مقبولة من اللقطات المرجعية.
</p>

<h3 id="responsivedesign">
	الاختبار المرئي للتصميم المتجاوب Responsive Design
</h3>

<p>
	تحدثنا عن أساس سير عمل الاختبار المرئي، ولم يتبقَ لنا سوى أن نتحدث عن جانب آخر وهو التصميم المتجاوب، إذ لا تظهر بعض عناصر واجهة المستخدم عند عرضها في إطار عرض viewport بأحجام صغيرة، لذا يجب أن ننتبه إلى هذه النقطة عندما نجري اختباراتنا.
</p>

<p style="text-align: center;">
	<img alt="006codrops-auth-and-create-project.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="166374" data-ratio="67.50" data-unique="4r1l69u08" style="width: 400px; height: auto;" width="640" src="https://academy.hsoub.com/uploads/monthly_2025_01/006codrops-auth-and-create-project.gif.05f17cea953a17b2169b276dbf15d673.gif">
</p>

<p>
	يمكننا الاستفادة من خاصية أداة بلاي رايت التي تحاكي الإطار المعروض والتي تمكننا من من محاكاة مختلف أحجام الشاشات وتلتقط لقطات لجميع الإطارات المعروضة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3707_12" style=""><span class="com">// tests/segmented-tooltip.spec.js</span><span class="pln">

</span><span class="com">// ...</span><span class="pln">

test</span><span class="pun">.</span><span class="pln">use</span><span class="pun">({</span><span class="pln">
  viewport</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">800</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">900</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

test</span><span class="pun">(</span><span class="str">'meta is hidden on smaller screens'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> page </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> page</span><span class="pun">.</span><span class="kwd">goto</span><span class="pun">(</span><span class="str">'http://127.0.0.1:8080'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> meta </span><span class="kwd">of</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> page</span><span class="pun">.</span><span class="pln">locator</span><span class="pun">(</span><span class="str">'css=.meta'</span><span class="pun">).</span><span class="pln">all</span><span class="pun">())</span><span class="pln">
    </span><span class="kwd">await</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> expect</span><span class="pun">(</span><span class="pln">meta</span><span class="pun">).</span><span class="pln">toBeHidden</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	بإجراء اختباراتنا ستلتقط كروماتيك لقطات للموقع بحجم إطار معروض أصغر، حتى نتحقق من تنسيق الموقع في الشاشات الصغيرة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="166369" href="https://academy.hsoub.com/uploads/monthly_2025_01/009portviewtest.png.0b80f7a1d8776a884dfd8af001e85ae4.png" rel=""><img alt="009portviewtest" class="ipsImage ipsImage_thumbnailed" data-fileid="166369" data-unique="s27kaqy52" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_01/009portviewtest.png.0b80f7a1d8776a884dfd8af001e85ae4.png"> </a>
</p>

<h2 id="-9">
	تطوير الاختبارات المرئية
</h2>

<p>
	تحدثنا في هذا المقال عن أساسيات الاختبار المرئي باستخدام أداتي كروماتيك وبلاي رايت، لكن هذا غيض من فيض، فحتى تطور مستوى اختباراتك يُنصح بدمج أداة كروماتيك في خطوط أنابيب التكامل المستمر <a href="https://academy.hsoub.com/tags/ci/cd/" rel="">CI pipeline</a> لوصول إشعار بأية تغييرات مرئية يستحدثها طلب سحب pull request، وضمان الحفاظ على الشكل المثالي لواجهة المستخدم.
</p>

<p>
	ترجمة -وبتصرّف- للمقال <a href="https://tympanus.net/codrops/2024/06/17/how-to-visual-test-websites-with-chromatic-and-playwright/" rel="external nofollow">How to Visual Test Websites with Chromatic and Playwright</a>.
</p>

<h2 id="-10">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/questions/11823-%D8%B4%D8%B1%D8%AD-%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D8%A3%D8%B3%D8%A7%D9%84%D9%8A%D8%A8-%D9%8A%D8%AA%D9%85-%D8%A5%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87%D8%A7-%D9%84%D9%84%D8%AA%D8%A3%D9%83%D8%AF-%D9%85%D9%86-%D8%B3%D9%84%D8%A7%D9%85%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D9%88%D8%A8%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1/" rel="">شرح مفاهيم أساليب يتم إستخدامها للتأكد من سلامة البرامج المكتوبة من الأخطاء</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/design/user-experience/%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D9%85%D8%AA%D8%AC%D8%A7%D9%88%D8%A8-responsive-design-r751/" rel="">التصميم المتجاوب Responsive Design</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/design/user-interface/%D9%85%D8%A7-%D9%87%D9%88-%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-ui-r854/" rel="">ما هو تصميم واجهة المستخدم UI</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/deployment/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D9%83%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%85%D8%B1-%D9%88%D8%A7%D9%84%D9%86%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%85%D8%B1-cicd-r644/" rel="">مدخل إلى التكامل المستمر والنشر المستمر CI/CD</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2500</guid><pubDate>Mon, 27 Jan 2025 15:00:00 +0000</pubDate></item><item><title>&#x643;&#x644; &#x645;&#x627; &#x62A;&#x62D;&#x62A;&#x627;&#x62C; &#x625;&#x644;&#x649; &#x645;&#x639;&#x631;&#x641;&#x62A;&#x647; &#x62D;&#x648;&#x644; GraphQL &#x645;&#x639; &#x627;&#x644;&#x623;&#x645;&#x62B;&#x644;&#x629; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629;</title><link>https://academy.hsoub.com/programming/advanced/%D9%83%D9%84-%D9%85%D8%A7-%D8%AA%D8%AD%D8%AA%D8%A7%D8%AC-%D8%A5%D9%84%D9%89-%D9%85%D8%B9%D8%B1%D9%81%D8%AA%D9%87-%D8%AD%D9%88%D9%84-graphql-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r2456/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_11/------GraphQL---.png.0455870f435a4fdb5b5a733e676231d7.png" /></p>
<p>
	نشرح في هذا المقال مفهوم GraphQL بالتفصيل وكيفية استخدامه تحسين أداء واجهة برمجة التطبيقات الخاصة بك، وتكتشف أبرز مزايا وعيوب استخدامه وتتمكن من تحديد فيما إذا كانت هي الأداة الأفضل لمشروع واجهة برمجة التطبيقات الخلفية الخاصة بك أم لا.
</p>

<p>
	فإذا كنت تفكر في تصميم واجهة برمجة تطبيقات خلفية ولديك فكرة مسبقة بواجهة برمجة التطبيقات REST فستكون على دراية أيضًا بأن تقنية REST ليست مناسبة للتطبيقات المعقدة وقد تواجهك بعض الصعوبات في التعامل معها لاسيما عند الحاجة إلى الإجابة على الأسئلة التالية:
</p>

<ul>
	<li>
		ما مقدار البيانات التي يجب أن تعيدها مع كل نقطة وصول؟
	</li>
	<li>
		هل ستعيد نقطة الوصول نفس البيانات، سواء على الأجهزة المحمولة أو على أجهزة سطح المكتب؟
	</li>
	<li>
		هل ترغب في إجراء تحديثات فورية في الوقت الفعلي إذا تغير شيء ما؟ لذا من الأفضل لك في هذه الحالة التعامل مع GraphQL.
	</li>
</ul>

<h2 id="rest">
	مشكلات استخدام معمارية REST
</h2>

<p>
	المصطلح REST هو اختصار لعبارة Representational state transfer أي نقل الحالة التمثيلية، وهو أسلوب لتصميم واجهات برمجة التطبيقات APIs، تنظم نقاط الوصول endpoints في معمارية REST بالاعتماد على مجموعة من الموارد كالمستخدمين والتعليقات وغيرها بحيث تتيح لك كل نقطة الوصول إلى مجموعة معينة من البيانات أو تمكنك من إجراء عملية معينة عليها. وبالرغم من كون معمارية <a href="https://academy.hsoub.com/programming/general/%D8%B4%D8%B1%D8%AD-%D9%81%D9%84%D8%B3%D9%81%D8%A9-restful-%D8%AA%D8%B9%D9%84%D9%85-%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-rest-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r635/" rel="">REST</a> مستخدمة من فترة طويلة، ولا تزال مستخدمة حتى اليوم على نطاق واسع لكتابة واجهات برمجة التطبيقات، إلا أنها تعاني من مشكلة رئيسية وهي تحديد البيانات التي يتوجب إعادتها بالضبط بواسطة نقاط الوصول الخاصة بك. فقد تواجه حالة تستلزم منك على سبيل المثال عرض قائمة بالمستخدمين، وقائمة بأحدث تعليقات كتبها كل مستخدم من هؤلاء المستخدمين، بمعنى آخر، ستحتاج في هذه الحالة لجلب قائمة من الكيانات، وجلب قائمة أخرى مرتبطة بكل كيان، وهناك طريقتان لحل هذه المشكلة باستخدام REST سنوضحهما تاليًا.
</p>

<h2 id="">
	نمذجة نقاط الوصول بالاعتماد على الموارد
</h2>

<p>
	إن وجود نقطة وصول واحدة لكل مورد أو مصدر بيانات يسهل على المطورين صيانة وتطوير الواجهة الخلفية، ولكنه يعقد الأمور من طرف العميل، فالعميل سيحتاج إلى إجراء طلبات متعددة لواجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> لجلب البيانات المطلوبة. على سبيل المثال لجلب كافة المستخدمين والتعليقات الخاصة بكل مستخدم يمكن أن يكون لديك نقاط الوصول التالية:
</p>

<ul>
	<li>
		<code>getUsers()</code> نقطة وصول تعيد لنا قائمة بكافة المستخدمين
	</li>
	<li>
		<code>getCommentsByUserId(userId: string)‎‎‎</code> نقطة وصول تعيد قائمة بالتعليقات المرتبطة بمعرف مستخدم محدد.
	</li>
</ul>

<p>
	لكن يمكن أن ينتج عن استخدام هذا الأسلوب مشكلة شائعة تعرف بمشكلة N + 1، حيث يحتاج العميل إلى استدعاء الخادم N + 1 مرة لجلب مجموعة واحدة من الموارد الأساسية، وجلب N مجموعة من الموارد الفرعية. يجلب الكود التالي قائمة المستخدمين، ثم يجلب التعليقات لكل مستخدم عن طريق استدعاءات HTTP إضافية. ومع أنه يعمل بشكل صحيح، فإنه يعاني من مشكلة الأداء، فاستدعاء التعليقات لكل مستخدم يتم بشكل متسلسل synchronous بدلاً من كونه متوازيًا parallel مما يتسبب في زمن انتظار طويل بسبب كل استدعاء منفصل.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7610_6" style=""><span class="kwd">const</span><span class="pln"> getUserWithComments </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">():</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">&lt;</span><span class="typ">User</span><span class="pun">[]&gt;</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// fetch a list of users</span><span class="pln">
  </span><span class="kwd">let</span><span class="pln"> users </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> getUsers</span><span class="pun">();</span><span class="pln">

  </span><span class="com">// new HTTP request for each user</span><span class="pln">
  </span><span class="kwd">for</span><span class="pun">(</span><span class="kwd">let</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> users</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> user </span><span class="pun">=</span><span class="pln"> users</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
    </span><span class="com">// loading comments from the server</span><span class="pln">
    user</span><span class="pun">.</span><span class="pln">comments </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> getCommentsByUserId</span><span class="pun">(</span><span class="pln">user</span><span class="pun">.</span><span class="pln">id</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> users</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نعم كما تتوقع، ستبطئ نقاط الوصول المعتمدة على الموارد من عملية عرض البيانات فتحميل كافة البيانات يستغرق وقتًا طويلًا، ويزيد الحمل على الخادم.
</p>

<h2 id="-1">
	إرجاع الشكل الدقيق للبيانات المطلوبة
</h2>

<p>
	يمكن حل مشكلة زيادة حمل الخادم وبطء عرض البيانات بتنظيم نقاط الوصول بحيث تعيد البيانات الدقيقة التي يريد العميل التعامل معها بالضبط، بدل تحميل كافة البيانات. فقد يكون لديك نقطة وصول مثل <code>()getUsersWithComments</code> تعيد المستخدمين مع تعليقاتهم من خلال استدعاء واحد فقط عبر بروتوكول <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-%D8%B4%D8%B1%D8%AD-%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r74/" rel="">HTTP</a>.
</p>

<p>
	يتطلب هذا الأسلوب وجود صلة وثيقة بين العميل والخادم، فكلما احتجنا لإجراء تغيير ما في واجهة المستخدم من أجل عرض بيانات إضافية أو إزالة بيانات معينة، سيتعين على مطوري الواجهة الأمامية في هذه الحالة التواصل مع مطوري الواجهة الخلفية من جديد من أجل ضبط البيانات المعادة من نقطة الوصول المحددة.
</p>

<p>
	لكن التعامل بهذا الأسلوب صعب أيضًا، لاسيما في حال وجود عدة نقاط وصول تعيد كل منها أنواعًا مختلفة من البيانات، أو في حال وجود نقاط نهاية منفصلة مخصصة للهواتف المحمولة، وقد ينجم عنه وجود العديد من نقاط الوصول التي تعيد مجموعة من الكائنات، وعدم معرفة ما هي النقاط المستخدمة فعليًا.
</p>

<h2 id="graphql-1">
	هل GraphQL خيار مناسب؟
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="163128" href="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-1.png.1db6b0443aab1f8217970a2214898584.png" rel=""><img alt="graphql 1" class="ipsImage ipsImage_thumbnailed" data-fileid="163128" data-unique="rlsatwqp3" style="width: 400px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-1.png.1db6b0443aab1f8217970a2214898584.png"> </a>
</p>

<p>
	في حال تفويض مسؤولية تحديد شكل البيانات المعادة إلى الخادم، سيصادف <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A3%D9%85%D8%A7%D9%85%D9%8A%D8%A9-frontend-web-development/" rel="">مطور الواجهة الأمامية</a> واحدة من المشكلات السابقة في مرحلة ما من مراحل العمل على المشروع، وقد يصادف مشكلات إضافية عند استخدام معمارية REST مثل:
</p>

<ul>
	<li>
		الحاجة لإرسال بيانات أقل لمستخدمي الهواتف المحمولة
	</li>
	<li>
		الحاجة لوجود تواصل في الزمن الحقيقي بين الخادم والعميل لتحديث البيانات بشكل فوري
	</li>
	<li>
		نقص وجود وثائق واجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> توضح كيفية استخدامها بشكل صحيح وبسبب وجود مثل هذه المشكلات عند التعامل مع معمارية REST ظهرت الحاجة لابتكار حلول أفضل مثل <a href="https://academy.hsoub.com/programming/general/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-graphql-r2208/" rel="">GraphQL</a>
	</li>
</ul>

<h2 id="graphql-2">
	ما هي GraphQL؟
</h2>

<p>
	GraphQL هي لغة استعلام توكل مسؤولية تحديد شكل البيانات المعادة من الخادم إلى جانب العميل، وتوفر للخادم مساحة للعمل playground، ووثائق لواجهات برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> الخاصة به، كما تتيح للعميل الاطلاع على جميع التوابع المتاحة القابلة للتنفيذ executable ، كما تمكنه من استخدام الاستعلامات للحصول على بيانات محددة، أو تعديل البيانات، كما توفر ميزة تسمى الاشتراكات subscriptions التي تسهل التواصل في الزمن الحقيقي.
</p>

<p>
	فعند استدعاء تابع من طرف الخادم، سيُضمَّن مع هذا الطلب هيكل بيانات جيسون JSON في حمولة الرسالة، وسيكون دور بيانات جيسون وصف كافة الحقول الخاصة بالكائن المطلوب إرجاعه، ويتوجب على الخادم أن يلبي الطلب ويعيد قيم الحقول المحددة ضمن هذا الطلب.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="163129" href="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-2.png.d4e4eafc2660652bdc3795e45ffb2189.png" rel=""><img alt="graphql 2" class="ipsImage ipsImage_thumbnailed" data-fileid="163129" data-unique="np3q38xo4" style="width: 400px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-2.png.d4e4eafc2660652bdc3795e45ffb2189.png"> </a>
</p>

<p>
	بهذه الطريقة يحل GraphQL مشكلة معرفة ما هي البيانات التي يرغب العميل في استردادها ويحددها بدقة داخل الطلب الذي يُرسله إلى الخادم ويجلب له هذه البيانات بالضبط دون زيادة أو نقصان، إذَا يحدد العميل بدقة ما هي الحقول التي يريد الحصول عليها في طلب HTTP، ويسترجع هذه البيانات من الخادم فقط.
</p>

<h2 id="graphql-3">
	العلاقات في نماذج GraphQL
</h2>

<p>
	تُمثل النماذج في models في GraphQL العلاقات بين الكائنات، وتتيح لك الاستعلام عن البيانات المتشابكة بكفاءة عبر استعلام واحد. فعند تمكين العلاقات بين النماذج في GraphQL ستتضمن الاستعلامات معلومات عن العلاقات بين الكيانات وتمكنك من استرجاع كافة البيانات المطلوبة في طلب HTTP واحد ينتقل مرة واحدة ذهابًا وإيابًا.
</p>

<p>
	ما نقصده هنا بنمذجة العلاقة أنه إذا كان لدى كيان ما علاقة واحد إلى متعدد ‎‎‎1 إلى N‎‎‎‎ مع كيان آخر، فيمكن طلب جميع البيانات المرتبطة بكيان معين في طلب واحد فقط مرسل إلى الخادم، وبهذا يمكنك الحصول على كافة البيانات المطلوبة دون الحاجة لإجراء عدة طلبات، وهذا يجعل استخدام GraphQL مفيدًا للاستعلام عن البيانات بشكل فعال وبسيط، خاصة عندما يكون هناك علاقات معقدة بين الكائنات.
</p>

<h2 id="resolversgraphql">
	المحللات Resolvers في GraphQL
</h2>

<p>
	عندما ترسل من العميل إلى خادم GraphQL، يحتاج الخادم إلى طريقة لمعرفة كيفية جلب البيانات المطلوبة، وتستخدم GraphQL تقنية المحللات Resolvers وهي دوال أو وظائف تحدد طريقة استجابة الخادم لاستعلامات GraphQL وهي لبنة البناء الرئيسية التي تجعلنا نفكر في استخدام GrapQL في المقام الأول. فالمحللات Resolvers هي المسؤولة عن ملء وتعبئة البيانات لحقل واحد في نوع الكائن، تستدعى المحللات لكل حقل في استعلام GraphQL، وتكون مسؤولية المحلل تحديد الطريقة المناسبة للحصول على البيانات المطلوبة وإرجاعها بالصيغة الصحيحة بناءً على العلاقات بين حقول الكائنات.
</p>

<p>
	تحسن المحللات Resolvers تقنية GraphGL وتسمح لنا بإضافة حقول إضافية للكيانات إذا لم تكن هذه الحقول متواجدة في قاعدة البيانات ولكن يمكن حسابها على الخادم. لنفترض أن لدينا نموذج بيانات يحتوي على المستخدمين وتعليقاتهم، ولكن بعض المعلومات التي نحتاج إليها قد لا تكون مخزنة مباشرة في قاعدة البيانات، ويتعين حسابها على الخادم.
</p>

<p>
	على سبيل المثال، قد يكون لدينا في قاعدة البيانات جداول لكل من المستخدمين وتعليقاتهم لكن لا يوجد حقل مباشر في جدول المستخدمين يخزن عدد التعليقات التي كتبها كل مستخدم، في هذه الحالة، يمكن استخدام المحللات Resolvers لحساب عدد التعليقات عند الطلب. وقد نحتاج أيضًا إلى حقل يحتوي على تفاصيل مستخدم معين مثل عنوان المستخدم والذي قد لا يكون مخزنًا مباشرة في قاعدة البيانات وفي هذه الحالة يمكن استخدام محلل Resolver لجلب هذا العنوان من مصدر خارجي.
</p>

<p>
	إذا طورنا نظام GraphQL باستخدام إطار عمل NestJS على سبيل المثال، فيمكن في هذه الحالة تحديد الدوال المسؤولة عن حساب هذه المعلومات أو جلبها باستخدام دوال مسجلة على كمحللات Resolvers وتكمن فائدة المحللات في آلية تنفيذها، إذ لن تنفذ أي دالة مسجلة كمحلل Resolver حتى تستدعى في طلب GraphQL.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="163130" href="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-3.png.229e234c26683f2f028152948e7ba9ab.png" rel=""><img alt="graphql 3" class="ipsImage ipsImage_thumbnailed" data-fileid="163130" data-ratio="172.97" data-unique="yqwikkwlh" style="width: 259px; height: auto;" width="332" src="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-3.png.229e234c26683f2f028152948e7ba9ab.png"></a>
</p>

<p>
	يعرض الكود التالي جزء من تطبيق يستخدم GraphQL و<a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">إطار عمل</a> NestJS. وهو يعبر عن جزءًا من مخطط قاعدة البيانات <a href="https://www.google.com/url?q=https://academy.hsoub.com/programming/workflow/%25D9%2585%25D8%25AE%25D8%25B7%25D8%25B7%25D8%25A7%25D8%25AA-%25D8%25A7%25D9%2584%25D9%2581%25D8%25A6%25D8%25A7%25D8%25AA-class-diagram-%25D9%2581%25D9%258A-%25D9%2584%25D8%25BA%25D8%25A9-%25D8%25A7%25D9%2584%25D9%2586%25D9%2585%25D8%25B0%25D8%25AC%25D8%25A9-%25D8%25A7%25D9%2584%25D9%2585%25D9%2588%25D8%25AD%25D8%25AF%25D8%25A9-uml-r307/&amp;sa=D&amp;source=docs&amp;ust=1713552214778047&amp;usg=AOvVaw0dP6WUe-MDKv1ecTtmGRNO" rel="external nofollow">UML</a> السابق ويتضمن تعريفًا لصنف باسم <code>Movie</code> ومحلل Resolver لحقل التعليقات <code>MovieComment</code> يحدد أن الحقل الذي سيعاد هو بيانات التعليقات عن الفيلم بالاعتماد على المعرف <code>id</code> الخاص بكل فيلم.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7610_9" style=""><span class="lit">@ObjectType</span><span class="pun">()</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Movie</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="lit">@Field</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Int</span><span class="pun">)</span><span class="pln">
                id</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">

                </span><span class="lit">@Field</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pun">)</span><span class="pln">
                createdAt</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">;</span><span class="pln">

                </span><span class="lit">@Field</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pun">)</span><span class="pln">
                updatedAt</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">;</span><span class="pln">

                </span><span class="lit">@Field</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                                nullable</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
                                description</span><span class="pun">:</span><span class="pln"> </span><span class="str">"User's title to the movie"</span><span class="pun">,</span><span class="pln">
                                defaultValue</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">,</span><span class="pln">
                </span><span class="pun">})</span><span class="pln">
                title</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">

                </span><span class="lit">@Field</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                                nullable</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
                                description</span><span class="pun">:</span><span class="pln"> </span><span class="str">"User's description to the movie"</span><span class="pun">,</span><span class="pln">
                </span><span class="pun">})</span><span class="pln">
                description</span><span class="pun">:</span><span class="pln"> string</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="lit">@Resolver</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Movie</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">MovieResolver</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">constructor</span><span class="pun">(</span><span class="kwd">private</span><span class="pln"> movieCommentService</span><span class="pun">:</span><span class="pln"> </span><span class="typ">MovieCommentService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

                </span><span class="lit">@ResolveField</span><span class="pun">(</span><span class="str">'movieComment'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">[</span><span class="typ">MovieComment</span><span class="pun">])</span><span class="pln">
                </span><span class="kwd">async</span><span class="pln"> getMovieComment</span><span class="pun">(</span><span class="lit">@Parent</span><span class="pun">()</span><span class="pln"> movie</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Movie</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                                </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> movie</span><span class="pun">;</span><span class="pln">
                                </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">movieCommentService</span><span class="pun">.</span><span class="pln">getAllMovieCommetsByMovieId</span><span class="pun">(</span><span class="pln">id</span><span class="pun">);</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	` تكمن فائدة هذا الكود في توفير واجهة GraphQL تتيح للعميل الحصول على بيانات الفيلم وتعليقاته بشكل فعال، وبهذا يمكن للعميل الاستعلام عن الفيلم والتعليقات حوله في نفس الاستعلام دون الحاجة لطلب استعلامات متعددة.
</p>

<p>
	يتطلب تحميل تعليقات كائن الفيلم عادة إجراء عملية استعلام مكلفة على قاعدة البيانات. أضف إلى ذلك فأنت لا تحتاج دائمًا إلى تحميل التعليقات الخاصة بالفيلم، وإنما تحتاج لتحميلها فقط عندما يطلبها المستخدم. لذا يفيد وضع <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%B2%D8%AE%D8%B1%D9%81%D8%A7%D8%AA-decorators-%D9%88%D8%A7%D9%84%D8%AA%D9%85%D8%B1%D9%8A%D8%B1-forwarding-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r878/" rel="">المزخرف</a> <code>ResolveField@</code> قبل اسم الدالة في إطار العمل NestJS إلى تنفيذ المنطق الداخلي لهذه الدالة فقط عندما يطلب العميل بيانات لاسم الحقل <code>movieComment</code>.
</p>

<p>
	<strong>ملاحظة</strong>: يجب أن يوفر منشئ واجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> في حال استخدام المحللات وصفًا لكافة الحقول التي توفرها هذه المحللات، لاسيما تلك التي تنفذ عمليات مكلفة بالنسبة للخادم، مثل الاستعلامات من قاعدة البيانات، كي لا يطلب المستخدمون حقولًا غير ضرورية دون داعٍ.
</p>

<h2>
	فوائد استخدام GraphQL
</h2>

<p>
	كما شرحنا سابقًا في حال استخدام واجهة برمجة تطبيقات REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> يكون السؤال الدائم ما هي البيانات التي ترجعها كل نقطة وصول؟ ويمكن توفير إجابة على هذا السؤال إما من خلال اختبار نقاط الوصول بواسطة أداة Postman التي تسمح بإنشاء طلبات <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-r73/" rel="">HTTP</a> مخصصة وإرسالها إلى خوادم الويب وتلقي الردود لاختبار الواجهات البرمجية، أو من خلال توفير إطار عمل Swagger لتوثيق كل ما يتعلق بالواجهات الخلفية للتطبيقات.
</p>

<p>
	لا تضمن لك هذه الحلول معرفة البيانات المعادة من قبل كل نقطة وصول، فقد تكون البيانات المعادة من نقاط النهاية غير معرفة وغير واضحة، وقد تختلف عن الوثائق أو التوقعات.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="163131" href="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-4.png.f48b26f8a9e95115ea265feb2dad4f47.png" rel=""><img alt="graphql 4" class="ipsImage ipsImage_thumbnailed" data-fileid="163131" data-unique="q2a0bjz6i" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-4.thumb.png.62560c042df784ac627ba4a5a9ca3019.png"> </a>
</p>

<p>
	يقدم استخدام GraphQL لك حلًا أفضل ويوفر تجربة تطوير محسنة والعديد من المميزات وأهمها:
</p>

<ul>
	<li>
		ميزة GraphQL Playground التي توفر بيئة تفاعلية لتجريب استعلامات GraphQL
	</li>
	<li>
		توفير مخطط مسبق التعريف Predefined schema
	</li>
	<li>
		إنشاء أنواع TypeScript تلقائيًا استنادًا إلى مخطط بيانات GraphQL
	</li>
	<li>
		التواصل في الزمن الحقيقي
	</li>
	<li>
		التكيف مع مختلف أنواع الأجهزة
	</li>
	<li>
		تنفيذ عدة استعلامات دفعة واحدة Batch query execution
	</li>
</ul>

<p>
	سنشرح تباعًا كل ميزة من هذه الميزات بالتفصيل، ولعل أهم مميزات GraphQL هي ميزة فصل التبعية dependency بين الخادم والعميل، حيث يتلقى العميل البيانات التي يطلبها في استعلام GraphQL المرسل للخادم فقط، ولا يتلقى أي بيانات غير مطلوبة، فحتى إن كان لدى الخادم هيكلية بيانات معقدة للغاية تتضمن أكثر من 60 خاصية يمكن الاستعلام عنها، فلن يتلقى العميل سوى قيم الحقول التي تضمنها استعلام GraphQL وهذا سيقلل كمية البيانات المرسلة ويزيد كفاءة وسرعة استجابة الطلبات.
</p>

<h3 id="playgroundgraphql">
	ميزة بيئة العمل التفاعلية في GraphQL
</h3>

<p>
	توفر GraphQL ميزة البيئة التفاعلية Playground وهي بيئة تطوير متكاملة طورتها شركة Prisma لتوفر بيئة لتنفيذ وتجربة الاستعلامات بشكل فوري، كما توفر وثائق للواجهات البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> لكافة الاستعلامات عن البيانات Queries وتعديلها Mutations واشتراكاتها Subscriptions.
</p>

<p>
	توفر هذه البيئة إمكانية فحص نوع البيانات التي يرجعها كل طلب، مما يسهل التأكد من صحة الاستجابة. كما يمكن اختبار وظائف الخادم مباشرة باستخدام واجهة GraphQL، مثل واجهة <a href="https://studio.apollographql.com/public/SpaceX-pxxbxen/variant/current/home" rel="external nofollow">SpaceX</a> مفتوحة المصدر مما يسهل تطوير التطبيقات التي تستخدم واجهات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> ويوفر وثائق تعزز فهم كيفية استخدام <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> بشكل صحيح وكتابة الاستعلامات والاستفادة من البيانات المتاحة بالشكل الأمثل.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="163132" href="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-5.png.da817232ec14679b2bf829fc3b996162.png" rel=""><img alt="graphql 5" class="ipsImage ipsImage_thumbnailed" data-fileid="163132" data-unique="6t55su7hw" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-5.thumb.png.c42b126f84ec3117ba748c9e5c964e12.png"> </a>
</p>

<h2 id="predefinedschema">
	المخطط مسبق التعريف Predefined Schema
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="163133" href="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-6.png.f65bef1326f562ff360abbb9688b7ded.png" rel=""><img alt="graphql 6" class="ipsImage ipsImage_thumbnailed" data-fileid="163133" data-unique="j6z3wuvvj" style="width: 300px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-6.thumb.png.bf8ee390ea20260faa11a658a5c39e37.png"> </a>
</p>

<p>
	عند فحص أنواع بيانات GraphQL، قد تلاحظ أن بعض الحقول تتضمن علامة تعجب <code>!</code> وهي بمثابة ضمان بأن الخادم يجب أن يوفر نوع البيانات الصحيح ولا يمكنه إرجاع قيمة خالية <code>null</code> لهذا الحقل.
</p>

<p>
	بإلقاء نظرة على الصورة المرفقة التي تستعلم عن حقول مثل <code>User.id</code> و <code>User.createdAt</code>، لن يرجع الخادم أبدًا قيمة <code>null</code> لهذه الحقول، وفي حال حدث هذا الأمر فستتلقى الخطأ التالي من عملية الاستعلام.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7610_13" style=""><span class="pun">{</span><span class="pln">
  </span><span class="str">"errors"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"message"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Cannot return null for non-nullable field User.id."</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"statusCode"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">500</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وعند استخدام علامة التعجب الخارجية مع المصفوفات <code>![ ]</code> كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7610_15" style=""><span class="pln"> movieCommentsUserLeft</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">MovieComment</span><span class="pun">!]!،</span></pre>

<p>
	فهذا يعني أن واجهة برمجة التطبيقات <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel=""><abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> ستعيد دومًا قائمة من القيم، وقد تكون إحدى هذه القيم <code>null</code> لكن لن تكون القيمة النهاية المعادة <code>null</code> أبدًا. من ناحية أخرى، تدل علامة التعجب الداخلية مع المصفوفات مثل <code>[MovieComment!]</code> بأن العناصر الموجودة داخل القائمة ستكون دائمًا كائنات. ومن المستحيل أن تكون القيمة الخالية <code>null</code> جزءًا من القائمة وإذا حدث ذلك، فسيفشل الاستعلام بأكمله، ولن يعيد أي بيانات.
</p>

<h3 id="typescript">
	توليد أنواع TypeScript
</h3>

<p>
	من الفوائد الرئيسية لمخطط GraphQL تمكين المطورين من تحديد استعلامات GraphQL واستخدام المكتبات المفيدة، مثل مكتبة توليد الكود <a href="https://www.graphql-code-generator.com/" rel="external nofollow">graphql-code-generator</a>، التي تسمح بإنشاء أنواع TypeScript من المخطط من جانب الخادم. ويؤدي استخدام مكتبة توليد الكود إلى منع الكتابة اليدوية لواجهات TypeScript لجميع استجابات واجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، وإذا تغير المخطط من جانب الخادم، فيمكن إعادة إنشاء المخطط بالكامل باستخدام أمر واحد فقط.
</p>

<p>
	في الصورة أدناه، نرى مثالًا على هذه الميزة. فعلى اليسار، يوجد استعلام launchesPast الذي يتوقع إرجاع جزء يسمى LaunchLast. وعلى اليمين، نرى أنواع TypeScript التي أنشأتها المكتبة <span ipsnoautolink="true">graphql-code-generator</span>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="163134" href="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-7.png.638c225927d32b8e45342cd0c97dd748.png" rel=""><img alt="graphql 7" class="ipsImage ipsImage_thumbnailed" data-fileid="163134" data-unique="75znzbqk0" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-7.thumb.png.7af291fa5e02573cfe6256667d294930.png"> </a>
</p>

<p>
	فباستخدام الأجزاء GraphQL fragments، يمكننا إعادة استخدام أجزاء من الاستعلامات والتعديلات في أماكن متعددة طالما كانت سترجع نفس نوع البيانات. وهذا يسهل علينا كتابة الاستعلامات وصيانتها. كما يمكننا استخدام الأجزاء المولدة لفحص الأنواع على جانب العميل للتأكد من توافق البيانات المستلمة مع الأنواع المتوقعة.
</p>

<h3 id="-2">
	التواصل في الزمن الحقيقي
</h3>

<p>
	في حال استخدام معمارية REST التقليدية، ستحتاج لاستخدام <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%8A-%D8%AA%D9%82%D9%86%D9%8A%D8%A9-websocket-r660/" rel="">WebSockets</a> لتحقيق الاتصال في الوقت الفعلي كي تعرف باستمرار ما هي نقطة الوصول التي يجب أن تستمع إليها، وما هي البيانات التي ستحصل عليها، وهل الاتصال نشط أم لا، بدل الاستعلام المتكرر عن البيانات من الخادم، يمكن استخدام تقنية تسمى الاشتراكات للاستماع إلى الأحداث المنبعثة غير المتزامنة، حيث تحافظ الاشتراكات على اتصال نشط بالخادم، وتستمع وتنتظر حتى يقوم الخادم بإرسال التحديثات.
</p>

<p>
	الفائدة الرئيسية لاستخدام الاشتراكات في GraphQL هي زيادة الأمان. فهي تتيح للمستخدم تحديد الحقول التي سيستقبل فيها القيم المحدثة، تمامًا كما يحدث في الاستعلامات. وهذا يسمح بتقليل البيانات غير المرغوب فيها و غير الضرورية ويجعل التفاعل مع البيانات أكثر فعالية.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="163135" href="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-8.png.c5d482aa8db4bf1c4ea3f5259f4b372a.png" rel=""><img alt="graphql 8" class="ipsImage ipsImage_thumbnailed" data-fileid="163135" data-unique="2mkz56mvb" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-8.thumb.png.c96c963e19ea0f1a8af5c593dfbd95e7.png"> </a>
</p>

<h3 id="-3">
	التوافق مع مختلف أنواع الأجهزة
</h3>

<p>
	عند العمل على جهاز ذي شاشة صغيرة، مثل الهاتف المحمول، لن نحتاج في الغالب لعرض كمية كبيرة من البيانات مقارنة بجهاز ذي شاشة كبيرة مثل حاسوب مكتبي أو محمول، لذا ليس من الضروري استرجاع البيانات بكمية كبيرة في الشاشات الصغيرة.
</p>

<p>
	الحل المقترح في هذه الحالة هو إنشاء استعلامين منفصلين لنفس العملية، أحدهما لجهاز سطح المكتب يسترجع كمية كبيرة من البيانات والثاني للهاتف المحمول يسترجع بيانات أقل واستخدام العبارات الشرطية لتنفيذها بحسب جهاز العميل.
</p>

<h3 id="-4">
	تنفيذ عدة استعلامات بنفس الوقت
</h3>

<p>
	تتيح لك واجهة برمجة التطبيقات GraphQL تنفيذ عدة استعلامات في وقت واحد عن طريق إرسال طلب HTTP واحد. على سبيل المثال في حال واجهة برمجة التطبيقات SpaceX، يمكنك تحديد عدة استعلامات مستقلة واسترجاع البيانات في طلب واحد كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7610_17" style=""><span class="pun">{</span><span class="pln">
  launchesPast</span><span class="pun">(</span><span class="pln">limit</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    mission_name
    launch_date_local
    launch_site </span><span class="pun">{</span><span class="pln">
                                site_name
                </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  historiesResult</span><span class="pun">(</span><span class="pln">limit</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    data </span><span class="pun">{</span><span class="pln">
      details
  </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  missions</span><span class="pun">(</span><span class="pln">limit</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    name
    twitter
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2 id="graphql-4">
	سلبيات GraphQL
</h2>

<p>
	لا تخلو أي تقنية من بعض الجوانب السلبية. دعونا نلقي نظرة على بعض الاعتبارات التي تحتاج للانتباه لها عند استخدام معمارية GraphQL.
</p>

<h3 id="n1">
	مشكلة N+1
</h3>

<p>
	عندما تبدأ في تعلم GraphQL، فإن أول مشكلة ستسمع عنها هي مشكلة N+1 ولتفهم كيفية حدوث هذه المشكلة لنأخذ المثال التالي حول واجهة برمجة التطبيقات SpaceX فإذا كنت تريد استرجاع الإصدارات السابقة من جدول واحد، وتريد الحصول على اسم الموقع لكل منها من جدول آخر باستخدام REST، قد تكون لديك نقطة وصول كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7610_19" style=""><span class="pln">route</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/launches/past'</span><span class="pun">,</span><span class="pln">
method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'GET'</span><span class="pun">,</span><span class="pln">
queryParam</span><span class="pun">:</span><span class="pln"> </span><span class="str">'limit'</span></pre>

<p>
	هذا يمكن أن يؤدي إلى الاستعلامات التالية في قاعدة البيانات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7610_21" style=""><span class="pln">SELECT </span><span class="pun">*</span><span class="pln"> FROM launches limit </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">limit</span><span class="pun">};</span><span class="pln">

</span><span class="pun">--</span><span class="pln"> pass the fetched launches</span><span class="pun">.</span><span class="pln">ids the the second query
SELECT </span><span class="pun">*</span><span class="pln"> FROM launch_sites WHERE launch_id in </span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">);</span></pre>

<p>
	يمكنك الحصول على النتيجة المطلوبة باستخدام استعلامين. لكن انتبه لأن هذا يعمل لأنك قمت بالفعل بتحميل قائمة من الإصدارات، ومررت معرفات الإصدارات إلى الاستعلام الثاني. وفي حال GraphQL، لتحميل <code>launch_sites</code> من جدول مختلف، يحتمل أن تستخدم ملحقة بكيان Launch، كما في الكود التالي.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7610_23" style=""><span class="pln">  type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
launchesPast</span><span class="pun">(</span><span class="pln">limit</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Int</span><span class="pun">!):</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Launch</span><span class="pun">!]!</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="pun">#</span><span class="pln"> from table launches
  type </span><span class="typ">Launch</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Int</span><span class="pun">!</span><span class="pln">
    mission_name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
    launch_site</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Launch_sites</span><span class="pun">!]!</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="pun">#</span><span class="pln"> from table launch_sites
  type </span><span class="typ">Launch_sites</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Int</span><span class="pun">!</span><span class="pln">
    site_name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}`</span><span class="pln">

resolvers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    launchesPast</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">(</span><span class="pln">_</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> limit</span><span class="pun">:</span><span class="pln"> number </span><span class="pun">})</span><span class="pln">  </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> ORM</span><span class="pun">.</span><span class="pln">getAllLaunches</span><span class="pun">(</span><span class="pln">limit</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="typ">Launch</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   launch_site</span><span class="pun">:</span><span class="pln">  </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">(</span><span class="pln">launchObj</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">return</span><span class="pln"> ORM</span><span class="pun">.</span><span class="pln">getLaunchSitesById</span><span class="pun">(</span><span class="pln">launchObj</span><span class="pun">.</span><span class="pln">id</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	في هذه الحالة، ستجري استعلامات متعددة للحصول على اسم الموقع لكل إصدار من قاعدة البيانات، لأن كل محلل في GraphQL يعرف فقط الآباء المرتبطين به، وستنفذ وظيفة محلل منفصلة لكل حقل.
</p>

<p>
	بمعنى آخر، تعالج GraphQL كل حقل من البيانات بشكل منفصل بناء على العلاقات بين الحقول، مما يتطلب تنفيذ استعلامات متعددة للحصول على المعلومات المطلوبة بشكل صحيح.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7610_25" style=""><span class="pln">query </span><span class="typ">LaunchesPastQuery</span><span class="pun">(</span><span class="pln">$limit</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Int</span><span class="pun">!)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  launchesPast</span><span class="pun">(</span><span class="pln">limit</span><span class="pun">:</span><span class="pln"> $limit</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  </span><span class="pun">#</span><span class="pln"> fetches </span><span class="typ">Launch</span><span class="pln"> entities </span><span class="pun">(</span><span class="lit">1</span><span class="pln"> query</span><span class="pun">)</span><span class="pln">
      id
      mission_name
      launch_site </span><span class="pun">{</span><span class="pln">       </span><span class="pun">#</span><span class="pln"> fetches </span><span class="typ">Sites</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> each </span><span class="typ">Launch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">N queries </span><span class="kwd">for</span><span class="pln"> N </span><span class="typ">Launches</span><span class="pun">)</span><span class="pln">
                                                site_name           
                                </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">                           </span><span class="pun">#</span><span class="pln"> </span><span class="typ">Therefore</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> N </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> round trips</span></pre>

<p>
	سيترجم الكود أعلاه إلى كود <a href="https://academy.hsoub.com/programming/sql/%D9%84%D9%85%D8%A7%D8%B0%D8%A7-%D9%8A%D8%AC%D8%A8-%D8%B9%D9%84%D9%8A%D9%83-%D8%AA%D8%B9%D9%84%D9%85-sql-r2215/" rel="">SQL</a> الزائف التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7610_27" style=""><span class="pln">SELECT </span><span class="pun">*</span><span class="pln"> FROM launches limit </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">limit</span><span class="pun">};</span><span class="pln">

</span><span class="pun">--</span><span class="pln"> </span><span class="kwd">pass</span><span class="pln"> the fetched launches</span><span class="pun">.</span><span class="pln">ids the the second query
SELECT </span><span class="pun">*</span><span class="pln"> FROM launch_sites WHERE launch_id </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
SELECT </span><span class="pun">*</span><span class="pln"> FROM launch_sites WHERE launch_id </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
SELECT </span><span class="pun">*</span><span class="pln"> FROM launch_sites WHERE launch_id </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span></pre>

<p>
	تتفاقم مشكلة N+1 بشكل أكبر في حال معمارية GraphQL إذ لا يمكن للعملاء أو الخوادم التنبؤ بمدى تكلفة الطلب حتى يتم تنفيذه. أما في معمارية REST، فيمكن التنبؤ بالكلفة نظرًا لوجود رحلة واحدة مطلوبة لكل نقطة وصول. في حين توجد نقطة وصول واحدة في GraphQL لكنها لا تملك دلالة على الحجم المحتمل للطلبات الواردة إليها.
</p>

<p>
	قدمت فيسبوك حلاً لمشكلة N+1 من خلال مكتبة تسمى DataLoader، وهي مكتبة مخصصة لجافا سكريبت تجمع الطلبات معًا. وأنشأت شوبيفاي مكتبة مشابهة باسم GraphQL Batch Ruby. تعمل هذه المكتبات عن طريق الانتظار حتى ينتهي تحميل كافة البيانات في مفاتيحها، ثم تنفذ طلب واحد إلى قاعدة البيانات بدلاً من إجراء طلبات متعددة. وبهذا يساعد تكديس الطلبات على تحسين الأداء وتقليل عدد الاستعلامات المنفذة.
</p>

<h3 id="-5">
	تبعية المخطط مسبق التعريف
</h3>

<p>
	إن وجود مخطط محدد مسبقًا بين الخادم والمستهلك يمكن أن يكون مفيدًا، فهذا المخطط سيحدد لنا البيانات المتوقعة بشكل واضح، لكن عند استخدام علامة التعجب <code>!</code> في حقول الكتابة لمعرفة الحقول التي لن تحتوي أبدًا على قيمة خالية <code>null</code> فيمكن في هذه الحالة لعلامات التعجب أو القيم غير الخالية أن تتسبب في فشل الاستعلام بأكمله.
</p>

<p>
	لنأخذ مثالاً على هذا الاستعلام عن الإصدارات، فعمود المعرف <code>id</code> المحفوظ في قاعدة البيانات، أو أي عمود يجب أن يحتوي على قيمة سيصبح فجأة فارغًا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7610_29" style=""><span class="pln">schema </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`{</span><span class="pln">
  type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    launchesPast</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Launch</span><span class="pun">!]!</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="pun">#</span><span class="pln"> from table launches
  type </span><span class="typ">Launch</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Int</span><span class="pun">!</span><span class="pln">
    mission_name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}`</span><span class="pln">

resolvers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    launchesPast</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="pun">()</span><span class="pln">  </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> allLaunches </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> ORM</span><span class="pun">.</span><span class="pln">getAllLaunches</span><span class="pun">()</span><span class="pln">
      allLaunches</span><span class="pun">[</span><span class="lit">3</span><span class="pun">].</span><span class="pln">id </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln"> </span><span class="com">// &lt;-- will cause the query to fail</span><span class="pln">

      </span><span class="kwd">return</span><span class="pln"> allLaunches</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	فإذا طلب المستخدم قائمة بالإصدارات من خلال استعلام LaunchPast، فسيفشل الاستعلام بأكمله وستحصل على رسالة الخطأ التالية التي تشير لأنه لا يمكن إرجاع قيمة خالية للحقل Launch.id:
</p>

<pre class="ipsCode">Cannot return null for non-nullable field Launch.id
</pre>

<p>
	أخيرًا، إذا استعلمت عن قائمة من الكائنات، وكان واحد منها فقط لا يتطابق مع المخطط مسبق التعريف لكونه يحتوي على قيمة خالية <code>null</code> لحقل مسبوق بعلامة التعجب <code>!</code>، فسيفشل استعلامك بالكامل، ولن تتلقى عندها أي بيانات.
</p>

<h2 id="nestedrecursion">
	التعاودية المتداخلة Nested Recursion
</h2>

<p>
	نظرًا لكون GraphQL لغة استعلام لواجهات برمجة التطبيقات، فهي توفر القدرة على تنفيذ استعلامات متداخلة وتمكن المطورين من الحصول على ما يحتاجونه من بيانات بالضبط. ولكن ماذا لو أرسل العميل استعلامًا يطلب فيه العديد من البيانات المتداخلة كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7610_31" style=""><span class="pln">authors </span><span class="pun">{</span><span class="pln">
  name
  books </span><span class="pun">{</span><span class="pln">
    title
    authors </span><span class="pun">{</span><span class="pln">
      name
      books </span><span class="pun">{</span><span class="pln">
        title
        authors </span><span class="pun">{</span><span class="pln">
          name
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاشك أن المشكلة الناجمة عن هذا المثال واضحة هنا، لذا يجب تصميم واجهة برمجة تطبيقات GraphQL بعناية لمنع تحميل بيانات متداخلة، كما ينصح باستخدام مكتبات مثل <a href="https://github.com/pa-bru/graphql-cost-analysis" rel="external nofollow">graphql-cost-analysis</a> لتعيين حد الكلفة العظمى <code>maximumCost</code> لمخططك بالكامل وتحديد مقدار التعقيد <code>complexity</code> لكل عملية.
</p>

<h3 id="http">
	رموز حالة HTTP
</h3>

<p>
	عندما يواجه الخادم Apollo Server أخطاء معينة أثناء معالجة استعلامات GraphQL، ستتضمن الاستجابة التي يعيدها للعميل مصفوفة أخطاء تحتوي على كل خطأ وقع. وبالرغم من حدوث خطأ، فإن رمز الحالة لكل طلب سيكون دائمًا  200 مما يعني استلام الطلب  بنجاح. ومع ذلك، ستتضمن الحمولة المعادة returned payload قائمة بالأخطاء التي حدثت مع <code>statusCode</code> يساوي 500.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="163136" href="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-9.png.ab0c665e2a70c4e8687372b92f3c6f01.png" rel=""><img alt="graphql 9" class="ipsImage ipsImage_thumbnailed" data-fileid="163136" data-unique="ne9wjoxqv" style="width: 400px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-9.png.ab0c665e2a70c4e8687372b92f3c6f01.png"> </a>
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="163137" href="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-10.png.6398f682831147514fc1cf3342a0df09.png" rel=""><img alt="graphql 10" class="ipsImage ipsImage_thumbnailed" data-fileid="163137" data-unique="1wosbqc8t" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-10.thumb.png.5d2b4ec06758278d2df7534cb2701716.png"> </a>
</p>

<p>
	وقد لا يكون رمز الخطأ 500 أفضل طريقة لإخبار العميل بحدوث خطأ ما، لذا قد ترغب باستخدام رموز الحالة خاصة بالطلبات غير المصرح بها، وأخرى خاصة بعمليات إعادة التوجيه.
</p>

<h2 id="subscriptions">
	توسيع نطاق الخادم من خلال الاشتراكات
</h2>

<p>
	كما شرحنا سابقًا، تعتبر الاشتراكات Subscriptions في GraphQL ميزة مهمة للتواصل مع الخادم في الزمن الحقيقي، فهي تسمح لك بتلقي تحديثات فورية من الخادم عندما تحدث تغييرات. وهذه الميزة مفيدة بشكل خاص للتعامل مع الأحداث غير المتزامنة، مثل التحديثات أو التغييرات في البيانات.
</p>

<p>
	لكن تظهر المشكلة هنا عندما يحتاج الخادم إلى التوسع أفقيًا -أي إضافة مثيلات متعددة من الخادم- من أجل التعامل معل زيادة حجم الطلبات. حيث يحتاج الخادم إلى آلية لتوزيع الطلبات ومعالجة البيانات عبر عدة مثيلات، وهذا يزيد من تعقيد تنفيذ الاشتراكات بفعالية.
</p>

<p>
	فإذا كنت تستخدم الاشتراكات من أجل إرسال تحديثات أو إشعارات إلى عدة عملاء في وقت واحد، وكان لديك مثيلات متعددة لتطبيق الواجهة الخلفية الخاص بك، فلن ترغب في أن يتصل العملاء بمثيل معين من الخادم، لأن ذلك قد يؤدي لعدم مزامنة التحديثات بين المثيلات المختلفة. وفي مثل هذه الحالة يمكن أن تفكر في استخدام <a href="https://redis.io/" rel="external nofollow">Redis</a> لتوزيع الحمل.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="163138" href="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-11.png.637ec61bed41cfe568bd11b96f9ff575.png" rel=""><img alt="graphql 11" class="ipsImage ipsImage_thumbnailed" data-fileid="163138" data-unique="jfa1h4z1e" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_11/GraphQL-11.thumb.png.30e351722a99510b5396dd96e02a9929.png"> </a>
</p>

<h2 id="graphql-5">
	متى أحتاج لخادم GraphQL؟
</h2>

<p>
	احتدمت المنافسة بين GraphQL و REST باعتبارهما أشهر نمطين لواجهات برمجة التطبيقات، إذ يقدم كل منهما أساليب مختلفة للتعامل مع نقل البيانات عبر بروتوكولات الإنترنت. وهذا قد  يدفعك للتساؤل هل أختار REST أم GraphQL، يعتمد القرار على متطلبات المشروع مثل حجم البيانات، والتحديثات الفورية، والمرونة في الاستعلامات.
</p>

<p>
	فعليًا، لن يكون هناك أي زيادة في الأداء بمجرد الانتقال إلى GraphQL بالنسبة للتطبيقات العادية، لكن عندما يتوسع التطبيق ستظهر فائدة الانتقال لاستخدام GraphQL الذي يوفر حلاً قويًا ومرنًا مقارنة باستخدام REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> التقليدية.
</p>

<p>
	كما تتمثل فائدة GraphQL في تبسيط التعامل مع الأنظمة المعقدة أو البنى التحتية القديمة، مثل واجهات برمجة التطبيقات الضخمة وصعبة الصيانة التابعة لجهات خارجية. فهي تتيح لك إرسال استعلامات مرنة وتحميل البيانات اللازمة فقط وتقلل حاجتك للتعامل مع كميات كبيرة من البيانات، وقد اعتمدت شركات كبرى مثل PayPal و Shopify على GraphQL لتحسين كفاءة واجهات برمجة التطبيقات الخاصة بها ولعل هذه الأمثلة تؤكد لنا على فعالية GraphQL في تحسين أداء التطبيقات مقارنة بالأساليب التقليدية.
</p>

<h2 id="-6">
	الخلاصة
</h2>

<p>
	لا شك أن اختيار التقنية المناسبة لتطبيقك ليس أمرًا سهلًا، حيث يتوجب عليك التفكير في المشكلات التي تحلها كل تقنية ومميزاتها وسلبياتها لاتخاذ أفضل قرار. نأمل أن يكون هذا المقال قد قدم لك فهمًا أفضل لتقنية GraphQL، ووضح لك مميزاتها وسبب تفضيلها من قبل المطورين لاسيما مطوري الواجهة الأمامية.
</p>

<p>
	ترجمة وبيتصرف لمقال  <a href="https://dev.to/bitovi/graphql-everything-you-need-to-know-1nbm" rel="external nofollow">GraphQL: Everything You Need to Know</a> لكاتبه Eduard Krivanek
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-graphql-r2208/" rel="">مقدمة إلى GraphQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A9-graphql-%D9%88%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D8%A7%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%AB%D8%A9-r1208/" rel="">مدخل إلى المكتبة GraphQL واستعمالاتها في بناء تطبيقات الويب الحديثة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%87%D9%85-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D9%86%D9%88%D8%B9-%D9%81%D9%8A-graphql-r2195/" rel="">فهم نظام النوع في GraphQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%AE%D8%A7%D8%AF%D9%85-graphql-%D9%81%D9%8A-%D8%A8%D9%8A%D8%A6%D8%A9-nodejs-r2189/" rel="">إعداد خادم GraphQL في بيئة Node.js</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2456</guid><pubDate>Wed, 27 Nov 2024 15:04:00 +0000</pubDate></item><item><title>&#x639;&#x634;&#x631;&#x629; &#x623;&#x62E;&#x637;&#x627;&#x621; &#x634;&#x627;&#x626;&#x639;&#x629; &#x64A;&#x642;&#x639; &#x628;&#x647;&#x627; &#x645;&#x637;&#x648;&#x631;&#x648; &#x627;&#x644;&#x648;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/advanced/%D8%B9%D8%B4%D8%B1%D8%A9-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%8A%D9%82%D8%B9-%D8%A8%D9%87%D8%A7-%D9%85%D8%B7%D9%88%D8%B1%D9%88-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r2445/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_11/10.png.499ef6dc166e52779854f15b5f16edd3.png" /></p>
<p>
	منذ صياغة مصطلح شبكة الويب العالمية World Wide Web عام 1990، تحولت مواقع الويب من مجرد صفحات HTML ثابتة إلى تطبيقات متقدمة تؤدي وظائف فعالة ومعقدة للغاية. يتوفر بين أيدينا اليوم آلاف الموارد الرقمية والمطبوعة التي تشرح لنا بالتفصيل كيفية تطوير أنواع مختلفة من تطبيقات الويب. كما أن بيئات التطوير ذكية إلى الحد الذي يمكّنها من اكتشاف وإصلاح العديد من الأخطاء التي عانى المطورون فيما مضى كثيرًا لإصلاحها.
</p>

<p>
	علاوةً على ذلك، هناك الكثير من منصات التطوير المختلفة التي تحوّل بسهولة <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%8A-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r2094/" rel="">صفحات الويب</a> الثابتة البسيطة إلى تطبيقات تفاعلية. تشترك جميع أنماط وممارسات ومنصات التطوير هذه في عوامل مشتركة، وجميعها معرضة لأخطاء متشابهة ناجمة عن طبيعة تطبيقات الويب.
</p>

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

<p>
	يتناول هذا المقال بعض المواضيع العامة المشتركة بين جميع مطوري الويب، مثل التحقق Validation والأمان Security وقابلية التوسع Scalability وتحسين محركات البحث <a href="https://academy.hsoub.com/marketing/search-engine-optimisation/" rel="">SEO</a>. وبالتأكيد فإنك لست مضطرًا إلى التقيد بالأمثلة الواردة في هذا المقال، فالهدف منها تقديم فكرة عن المشكلات المحتملة التي قد يواجهها مطورو الويب والانتباه لها عند تطوير موقعك.
</p>

<h2 id="-1">
	الخطأ الأول: عدم التحقق من صحة المدخلات Validation
</h2>

<p>
	إن التحقق من صحة المدخلات من جانبي العميل والخادم أمر ضروري لا غنى عنه، وجميعنا سمع بالنصيحة الذهبية «لا تثق أبدًا في البيانات التي يدخلها المستخدم» ومع ذلك، فإن الأخطاء الناجمة عن التحقق من صحة المدخلات هي أخطاء متكررة الحدوث.
</p>

<p>
	من العواقب الشائعة لهذا الخطأ هو حقن إس كيو إل <a href="https://academy.hsoub.com/devops/security/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%A3%D9%85%D9%8A%D9%86-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D8%B3%D8%AD%D8%A7%D8%A8%D9%8A%D8%A9-%D8%B6%D8%AF-%D9%87%D8%AC%D9%85%D8%A7%D8%AA-%D8%AD%D9%82%D9%86-sql-r98/" rel="">SQL Injection</a>، إذ يتواجد هذا الخطأ سنويًا في <a href="https://owasp.org/www-project-top-ten/" rel="external nofollow">قائمة أواسب OWASP</a> التي تحتوي على أهم 10 مخاطر في أمن تطبيقات الويب. لذا توفر معظم أطر عمل <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A3%D9%85%D8%A7%D9%85%D9%8A%D8%A9-frontend-web-development/" rel="">تطوير الواجهات الأمامية</a> قواعد تحقق مميزة وسهلة الاستخدام، كما تستخدم معظم أطر عمل <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%AE%D9%84%D9%81%D9%8A%D8%A9-backend-web-development/" rel="">تطوير الواجهات الخلفية</a> تعليقات توضيحية بسيطة لضمان التزام البيانات المقدمة بالقواعد المتوقعة.
</p>

<p>
	قد تستغرق عملية التحقق وقتًا طويلًا، ولكن مع ذلك، يجب أن يأخذ التحقق من صحة الإدخال حقه من الشيفرات البرمجية لتطبيقك، ويحب ألا تتجاهل ذلك أبدًا لتحقيق <a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D9%85%D8%A7%D9%86-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r2020/" rel="">أمان مواقع الويب</a>.
</p>

<h2 id="authenticationauthorization">
	الخطأ الثاني: الاستيثاق Authentication دون الحصول على تصريح Authorization
</h2>

<p>
	قبل أن نخوض عميقًا في شرح هذا الخطأ، لا بُدّ من شرح المصطلحين السابقين وتوضيح كل منهما:
</p>

<ul>
	<li>
		<strong>الاستيثاق Authentication</strong>: يُقصد به التحقق من أن الشخص الذي يدخل البيانات هو مستخدم محدد، من خلال تقديم بيانات الأمان الخاصة به على النحو الصحيح، مثل كلمة المرور، والإجابة على أسئلة الأمان، ومسح بصمات الأصابع، ونحو ذلك.
	</li>
	<li>
		<strong>التصريح Authorization</strong>: هو التأكد من أن مستخدمًا معينًا لديه حق الوصول إلى مورد محدد، أو أن لديه التصريح لتنفيذ إجراء ما.
	</li>
</ul>

<p>
	ويمكن اختصار الأمر بأن الاستيثاق هو التحقق من شخصية الكائن، بينما التصريح هو تحديد ما يمكن لهذا الكائن فعله. أما لتوضيح الخطأ الذي يحدث هنا، فدعونا نأخذ المثال التالي: ضع في الحسبان أن متصفحك يحتفظ بمعلومات المستخدم المسجلة حاليًا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8139_8" style=""><span class="pun">{</span><span class="pln">
    username</span><span class="pun">:</span><span class="str">'elvis'</span><span class="pun">,</span><span class="pln">
    role</span><span class="pun">:</span><span class="str">'singer'</span><span class="pun">,</span><span class="pln">
    token</span><span class="pun">:</span><span class="str">'123456789'</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عند تغيير كلمة المرور، يجري تطبيقك طلب من النوع POST كما يلي:
</p>

<pre class="ipsCode">POST /changepassword/:username/:newpassword
</pre>

<p>
	في التابع <code>‎/‎changepassword‎</code>، يمكنك التحقق من تسجيل دخول المستخدم وعدم انتهاء صلاحية المفتاح token، ومن ثم يمكنك إيجاد ملف تعريف المستخدم بناءً على المعامل <code>:‎‎‏‏‏‏‎username‎</code> ومن ثم تغير كلمة مرور المستخدم.
</p>

<p>
	لقد تحققت من أن المستخدم سجل دخوله بطريقة صحيحة، ومن ثم عملت على تنفيذ طلبه بتغيير كلمة المرور الخاصة به. إلى هنا تبدو الأمور وكأنها على ما يُرام، أليس كذلك؟ للأسف الجواب هو لا، وإليك التوضيح.
</p>

<p>
	في هذه المرحلة، من المهم التحقق من أن المستخدم الذي ينفذ الإجراء والمستخدم الذي تغيرت كلمة مرور حسابه هو الشخص نفسه، إذ يمكن التلاعب بالمعلومات الموجودة في المتصفح، ويمكن لأي مستخدم ذي خبرة تحديث اسم المستخدم <code>username:'elvis'‎</code> بسهولة إلى اسم المستخدم <code>username:'Administrator'‎</code> باستخدام أدوات المتصفح المضمّنة فيه.
</p>

<p>
	لذا فإننا في هذه الحالة ركزنا فقط على الاستيثاق Authentication معتمدين على أن المستخدم قد قدم اعتماديات الأمان. يمكننا أيضًا إضافة التحقق من أنه لا يمكن تنفيذ تابع <code>‎/‎‎‎‎‎‎changepassword</code> إلا من قِبل المستخدمين الذين تجاوزوا عملية الاستيثاق Authentication. ومع ذلك، ما تزال الخطوات غير كافية لحماية المستخدمين من محاولات الاختراق الضارة.
</p>

<p>
	يجب أن تحرص على أن تتحقق من الهوية الفعلية للشخص الذي يقدم الطلب، وكذلك محتوى الطلب ضمن تابع <code>‎/changepassword‎‎</code> إضافةً إلى حصول المستخدم على التصريح Authorization المناسب للطلب، مع التأكد من أن المستخدم لا يمكنه تغيير سوى بياناته.
</p>

<p>
	الاستيثاق Authentication والتصريح Authorization هما وجهان لعملة واحدة، لذا لا تعاملهما على نحو منفصل أبدًا.
</p>

<h2 id="-2">
	الخطأ الثالث: عدم الجاهزية للتوسع
</h2>

<p>
	في ظل عالمنا اليوم الذي يتسم بالتطور السريع، و<a href="https://blog.mostaql.com/accelerators-vs-incubators/" rel="external">مسرعات الأعمال startup accelerators</a> والوصول العالمي السريع للأفكار المميزة، تسعى العديد من الشركات إلى طرح <a href="https://academy.hsoub.com/entrepreneurship/business/%D9%85%D8%A7-%D8%AA%D8%AD%D8%AA%D8%A7%D8%AC-%D8%A5%D9%84%D9%89-%D9%85%D8%B9%D8%B1%D9%81%D8%AA%D9%87-%D8%AD%D9%88%D9%84-%D8%A7%D9%84%D8%AD%D8%AF-%D8%A7%D9%84%D8%A3%D8%AF%D9%86%D9%89-%D9%84%D9%84%D9%85%D9%86%D8%AA%D8%AC-mvp-r1071/" rel="">الحد الأدنى من منتجاتها <abbr title="Minimum Viable Product | المنتج الفعال القاعدي"><abbr title="Minimum Viable Product | المنتج الفعال القاعدي">MVP</abbr></abbr></a> في السوق في أقرب وقت ممكن، لذا فإن ضغط الوقت المستمر هذا يدفع حتى فرق تطوير الويب الجيدة إلى تجاهل بعض المشكلات أحيانًا.
</p>

<p>
	يُعَد التوسع من الأهداف الأساسية للكثير من فرق العمل، وهنا تبرز فكرة مفهوم الحد الأدنى من المنتج <abbr title="Minimum Viable Product | المنتج الفعال القاعدي"><abbr title="Minimum Viable Product | المنتج الفعال القاعدي">MVP</abbr></abbr>، لكن إذا بالغت في إطلاق أقل حد ممكن من منتجك، فإنك ستقع ضحيةً للعديد من المشكلات الخطيرة.
</p>

<p>
	لسوء الحظ، لا يكفي اختيار قاعدة بيانات وخادم ويب قابليْن للتطوير، أو فصل مكونات التطبيق على خوادم مستقلة، فهناك الكثير من التفاصيل الأخرى التي يجب أن تهتم بها لكي تتجنب إعادة بناء أجزاء مهمة من تطبيقك فيما بعد، وهو ما يصبح مشكلةً كبيرةً في تطوير الويب.
</p>

<p>
	لنفترض مثلًا أنك اخترت تخزين صور الملفات الشخصية لمستخدمي تطبيقك مباشرةً على خادم الويب، والذي يُعد حلًا جيدًا للغاية، إذ يمكن للتطبيق الوصول إلى الملفات بسرعة، مع توفر طرق لمعالجة الملفات في كل منصة تطوير، كما يمكنك تقديم هذه الصور كمحتوى ثابت، مما يؤدي إلى الحد الأدنى من التحميل على تطبيقك.
</p>

<p>
	ولكن ما الذي سيحدث عندما ينمو تطبيقك وتضطر حينها إلى استخدام عدد من خوادم الويب من خلال <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D8%AA%D9%88%D8%B2%D9%8A%D8%B9-%D8%A7%D9%84%D8%AD%D9%90%D9%85%D9%84-load-balancing-r319/" rel="">موزع الأحمال Load balancer</a>، حينها ستفشل عملية التوسع لسبب بسيط مثل صور الملف الشخصي، على الرغم من التوسيع الجيد الذي أجريته على مساحة تخزين قاعدة البيانات وخوادم حالة الجلسة وخوادم الويب.
</p>

<p>
	ستضطر في الحالة السابقة إلى تنفيذ نوع ما من خدمة مزامنة الملفات، والتي ستؤدي إلى تأخير في التحميل، ناهيك عن أخطاء 404 التي ستتسبب بها، أو قد تجد حلًا آخر لتوزيع الملفات على خوادم الويب لديك.
</p>

<p>
	لقد كان بالإمكان تجاوز كل المشكلات السابقة من خلال استخدام موقع تخزين ملفات مشترك أو قاعدة بيانات أو أي حل آخر للتخزين السحابي. ربما كان بإمكانك تنفيذ كل ذلك خلال بضعة ساعات عمل إضافية، والأمر يستحق ذلك المجهود.
</p>

<h2 id="seo">
	الخطأ الرابع: عدم تحسين محركات البحث SEO بطريقة صحيحة
</h2>

<p>
	أحد أهم الأسباب وراء فشل تحسين صفحات الويب وملاءمتها مع سياسات <a href="https://academy.hsoub.com/files/28-%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A5%D9%84%D9%89-%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-seo/" rel="">تحسين محركات البحث SEO</a> هو التضليل الذي يمارسه بعض الأشخاص الذين ينتحلون صفة خبراء تحسين محركات البحث.
</p>

<p>
	يظن الكثير من مطوري الويب أن لديهم المعرفة الكافية عن مجال تحسين محركات البحث SEO، وأن الأمر ليس معقدًا ولا يحتاج إلى خبرات متخصصة، ولكن هذا ليس صحيحًا، إذ يتطلب إتقان تحسين محركات البحث قضاء وقت طويل في البحث عن أفضل الممارسات والقواعد المتغيرة باستمرار حول كيفية فهرسة صفحات الويب في محركات البحث جوجل Google وبينغ Bing وياهو Yahoo.
</p>

<p>
	إذا لم تجرب بنفسك باستمرار وتعمل على تتبع النتائج وتحليلها بدقة، فكن على يقين بأنك لست <a href="https://academy.hsoub.com/marketing/search-engine-optimisation/%D9%83%D9%8A%D9%81-%D8%AA%D8%B5%D8%A8%D8%AD-%D8%A7%D9%84%D8%AE%D8%A8%D9%8A%D8%B1-%D9%81%D9%8A-%D9%85%D8%AC%D8%A7%D9%84-%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-seo%D8%9F-r627/" rel="">خبيرًا في تحسين محركات البحث SEO</a>، ويجب ألا تدّعي أنك متخصص في هذا المجال.
</p>

<p>
	تحسين محركات البحث ليس مجرد إعداد محتوى جيد، وإضافة الوسوم tags و<a href="https://academy.hsoub.com/marketing/search-engine-optimisation/%D9%85%D8%A7-%D9%87%D9%8A-%D8%AE%D8%B1%D9%8A%D8%B7%D8%A9-%D8%A7%D9%84%D9%83%D9%84%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%81%D8%AA%D8%A7%D8%AD%D9%8A%D8%A9%D8%9F-r630/" rel="">الكلمات المفتاحية</a> والبيانات الوصفية meta-data، ونصوص بديلة عن الصور، وإعداد خريطة الموقع ونحو ذلك. بل يشمل أيضًا التخلص من المحتوى المتكرر، والحصول على بنية موقع يمكن لمحركات البحث الزحف بداخلها لفهرستها، مع <a href="https://academy.hsoub.com/marketing/search-engine-optimisation/%D9%83%D9%84-%D9%85%D8%A7-%D9%8A%D8%AC%D8%A8-%D8%A3%D9%86-%D8%AA%D8%B9%D8%B1%D9%81%D9%87-%D8%B9%D9%86-%D8%B2%D9%8A%D8%A7%D8%AF%D8%A9-%D8%B3%D8%B1%D8%B9%D8%A9-%D8%A7%D9%84%D8%B5%D9%81%D8%AD%D8%A9-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-r523/" rel="">زيادة سرعة تحميل صفحات الموقع</a>، وتربيط داخلي جيد.
</p>

<p>
	كما هو الحال مع قابلية التوسع، يجب أن تفكر في تحسين محركات البحث منذ اللحظة التي تبدأ فيها بإنشاء تطبيقك على الويب، لأنك إن أجلت الأمر إلى أوقات متأخرة، فقد تجد أن الأمر يتطلب إعادة برمجة تطبيقك بالكامل.
</p>

<h2 id="-3">
	الخطأ الخامس: التأخر في معالجة الطلبات
</h2>

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

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

<p>
	قد تكون هذه الطريقة مجدية في البداية، لكن ما الذي سيحدث عندما يتسع نشاطك التجاري، وتتلقى فجأةً آلاف الطلبات التي سيؤدي كل منها إلى إرسال رسائل تأكيد عبر البريد الإلكتروني؟ سيؤدي حينها إلى تأخر عمليات تأكيد الطلبات، أو سيتراجع وقت استجابة تطبيقك إلى حد كبير، لأنه سيتعامل مع رسائل البريد الإلكتروني عوضًا عن المستخدمين.
</p>

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

<h2 id="bandwidth">
	الخطأ السادس: عدم تحسين استخدام حيز النطاق التراسلي Bandwidth
</h2>

<p>
	تجري معظم عمليات التطوير والاختبار في بيئة تجريبية على شبكة محلية، لذلك عندما تعمل على تنزيل 5 صور خلفية يبلغ حجم كل منها 3 ميجابايت عبر شبكتك المحلية السريعة، فقد لا تكتشف أي مشكلة تتعلق بسرعة التحميل، ولكن عندما يبدأ المستخدمون بتحميل صفحة رئيسية بحجم 15 ميجابايت عبر شبكة 3G على هواتفهم الذكية، ستبدأ حينها بتلقي العديد من شكاوى العملاء.
</p>

<p>
	سيساعدك تحسين استخدام حيز النطاق التراسلي Bandwidth على تعزيز أداء تطبيقك بدرجة كبيرة، ولكي تتمكن من الحصول على هذا التعزيز، ستحتاج إلى إجراء بعض الحيل التي يستخدمها العديد من مطوري الويب الخبراء، ومن الأمثلة على هذه الحيل:
</p>

<ol>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B6%D8%BA%D8%B7-gzipping-%D9%88%D8%A7%D9%84%D8%AA%D8%B5%D8%BA%D9%8A%D8%B1-minification-r679/" rel="">تصغير minification</a> كامل نصوص جافا سكريبت.
	</li>
	<li>
		تصغير كامل نصوص CSS.
	</li>
	<li>
		ضغط compression نصوص HTTP من الخادم.
	</li>
	<li>
		تحسين حجم الصور ودقتها.
	</li>
</ol>

<h2 id="-4">
	الخطأ السابع: عدم تهيئة التطبيق لأحجام شاشات مختلفة
</h2>

<p>
	لقد كان <a href="https://academy.hsoub.com/design/user-experience/%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D9%85%D8%AA%D8%AC%D8%A7%D9%88%D8%A8-responsive-design-r751/" rel="">التصميم المتجاوب Responsive design</a> مع أحجام شاشات مختلفة موضوعًا مهمًا في السنوات الأخيرة، إذ أدى ازدياد استخدام الهواتف الذكية ذات الأحجام المختلفة إلى ظهور العديد من الطرق الجديدة للوصول إلى المحتوى عبر الإنترنت، وقد رافقت هذه الطرق الجديدة مجموعة من مشكلات تطوير الويب.
</p>

<p>
	يتزايد يوميًا عدد زيارات المواقع الإلكترونية التي تأتي من الهواتف الذكية والأجهزة اللوحية، لذا من أجل ضمان التنقل السلس في صفحات موقعك الإلكتروني، والوصول إلى كامل المحتوى، يجب أن تتيح للمستخدمين الوصول إلى موقعك من جميع أنواع الأجهزة.
</p>

<p>
	هناك العديد من الأنماط والممارسات التي يمكن اتباعها لبناء تطبيقات ويب متجاوبة مع مختلف أنواع الأجهزة. الأكثر شهرة بين أطر العمل هذه هو <a href="https://academy.hsoub.com/programming/css/bootstrap/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D9%84%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D8%A8%D9%88%D8%AA%D8%B3%D8%AA%D8%B1%D8%A7%D8%A8-bootstrap-r2256/" rel="">إطار العمل بوتستراب Bootstrap</a>، وهو إطار عمل مجاني ومفتوح المصدر مُعتمَد من كل منصة تطوير رئيسية، لذا كل ما عليك فعله هو الالتزام بأنماط وممارسات Bootstrap عند إنشاء تطبيقك، وستحصل على تطبيق ويب متجاوب دون أي مشكلة.
</p>

<p>
	لاستخدام إطار العمل بوتستراب Bootstrap بكفاءة عالية، يُفضَّل أن يكون لديك خبرة جيدة في كيفية التعامل مع لغات تطوير الويب الأساسية، مثل HTML و CSS و JavaScript، إضافةً إلى إدراك جيد لمفهوم التصميم المتجاوب. إذا لم يكن لديك معرفة كافية بها، فيمكنك التعرف عليها في المسار الأول من <a href="https://academy.hsoub.com/learn/front-end-web-development/" rel="">دورة تطوير واجهات المستخدم</a> التي تقدمها أكاديمية حسوب.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير واجهات المستخدم
		</p>

		<p class="banner-subtitle">
			ابدأ عملك الحر بتطوير واجهات المواقع والمتاجر الإلكترونية فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/front-end-web-development/" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<a href="https://academy.hsoub.com/learn/front-end-web-development/" rel=""><img alt="دورة تطوير واجهات المستخدم" src="https://academy.hsoub.com/learn/assets/images/courses/front-end-web-development.png"></a>
	</div>
</div>

<h2 id="-5">
	الخطأ الثامن: عدم التوافق بين المتصفحات
</h2>

<p>
	تخضع عملية التطوير في كثير من الحالات إلى جدول زمني ضيق، إذ يسعى المطورون إلى إصدار التطبيقات في أسرع وقت ممكن، وحتى مطورو الويب الجيدون غالبًا ما يركزون على تقديم الوظائف أكثر مما يركزون على التصميم، صحيح أن معظم المطورين لديهم عدد من المتصفحات المثبتة على أجهزتهم، مثل المتصفح كروم Chrome وفايرفوكس Firefox وغيرها.. لكنهم في معظم الحالات يستخدمون برنامجًا واحدًا فقط من هذه البرامج.
</p>

<p>
	من الممارسات الشائعة بين المطورين هي استخدام متصفح واحد أثناء التطوير، وعندما يقترب التطبيق من الاكتمال، يبدأ المطورون في اختباره على متصفحات أخرى، وهو أمر منطقي تمامًا، على افتراض أن لديك ما يكفي من الوقت لاختبار وإصلاح المشكلات التي تظهر في هذه المرحلة. ومع ذلك، هناك بعض النصائح التي يمكنك اتباعها في تطوير الويب، والتي يمكن أن توفر لك وقتًا كبيرًا عندما يصل تطبيقك إلى مرحلة الاختبار على المتصفحات، وهي:
</p>

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

<h2 id="portability">
	الخطأ التاسع: عدم التخطيط لقابلية النقل Portability
</h2>

<p>
	الافتراضات العشوائية هي رأس كل المشكلات، وعند الحديث عن قابلية النقل Portability، فإن ذلك ينطبق عليها أكثر من أي شيء آخر. والمقصود بقابلية النقل هو القدرة على تشغيل التطبيق على أي بيئة أو عتاد أو نظام تشغيل.
</p>

<p>
	لعلك صادفت الكثير من المشكلات في تطوير الويب، مثل تحديد مسارات الملفات أو قيم أخرى مثل السلاسل النصية المخصصة للاتصال بقاعدة البيانات بشكل ثابت داخل الشيفرة البرمجية، أو افتراض أن مكتبة معينة ستكون متاحة على الخادم الذي سيتضيف التطبيق، وغير ذلك من المشكلات.
</p>

<p>
	من الخطأ افتراض أن بيئة تطوير التطبيق (خلال مرحلة برمجته) ستتوافق مع بيئة الإنتاج (بعد الانتهاء منه ونشره للمستخدمين)، والتطبيق المثالي هو التطبيق الذي يحتاج الحد الأدنى من الصيانة والإصلاح maintenance-free، لذلك:
</p>

<ol>
	<li>
		تأكد من إمكانية توسيع نطاق تطبيقك وتشغيله على بيئة خوادم متعددة عبر توزيع الأحمال.
	</li>
	<li>
		توفير ملفات إعداد configuration file بسيطة وواضحة، وربما في ملف إعداد واحد.
	</li>
	<li>
		تعامل مع الحالات الاستثنائية عندما لا تكون إعدادات خادم الويب كما يجب.
	</li>
</ol>

<h2>
	الخطأ العاشر: استخدامات غير فعالة لواجهة RESTful <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>
</h2>

<p>
	قد يستخدم المطورون حلولًا غير مجدية لا تهدف إلى تحقيق أي فائدة ويظنون أنها ستعمل بشكل جيد، لكنها تنتهي بوقوع مشكلات أكبر، على سبيل المثال احتلت واجهات برمجة التطبيقات المعتمدة على <a href="https://academy.hsoub.com/programming/general/%D8%B4%D8%B1%D8%AD-%D9%81%D9%84%D8%B3%D9%81%D8%A9-restful-%D8%AA%D8%B9%D9%84%D9%85-%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-rest-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r635/" rel="">فلسفة RESTful APIs</a> مكانة عالية في تطوير الويب، حتى أن كل تطبيق ويب تقريبًا يعتمد على إحدى أنواع خدمات REST، سواءً للاستخدام الداخلي أو بالتكامل مع النظام الخارجي، لكن ما يزال هناك الكثير من أنماط وخدمات RESTful معطلة ولا تلتزم بالممارسات المتوقعة.
</p>

<p>
	لكن ثمة خطآن شائعان عند كتابة RESTful <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، هما:
</p>

<ol>
	<li>
		<strong>استخدام أفعال أو توابع HTTP Verbs خاطئة</strong>: كما هو الحال مثلًا عند استخدام طريقة الطلب GET لكتابة البيانات، إذ أن طريقة الطلب GET في بروتوكول HTTP تُستخدم فقط لجلب البيانات وليس للتعديل عليها، وهي مصممة لتكون ثابتة وآمنة، لذا فإنه بغض النظر عن عدد المرات التي تُستخدم فيها طريقة GET لاستدعاء البيانات من نفس المورد، فإن الاستجابة ستكون دائمًا هي نفسها، ويجب ألا يحدث أي تغيير في حالة التطبيق.
	</li>
	<li>
		<strong>عدم إرسال رموز حالة status codes صحيحة</strong>: ومن أفضل الأمثلة على هذا الخطأ الشائع هو إرسال رسائل خطأ مع رمز إجابة 200، إذ يشير هذا الرمز في الأساس إلى أن كل شيء على ما يُرام.
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8139_10" style=""><span class="pln">HTTP </span><span class="lit">200</span><span class="pln"> OK
 </span><span class="pun">{</span><span class="pln">
     message</span><span class="pun">:</span><span class="str">'there was an error'</span><span class="pln">
 </span><span class="pun">}</span></pre>

<p>
	يجب إرسال الرمز HTTP 200 OK عندما لا ينتج عن الطلب أي خطأ، أما في حالة وجود خطأ ما، فيجب إرسال الرموز 400، 401، 500 أو أي رمز حالة آخر مناسب للخطأ الذي حدث.
</p>

<h2 id="-6">
	في الختام
</h2>

<p>
	يُعد تطوير الويب مصطلحًا واسعًا يمكن أن يشمل الكثير من المجالات، مثل تطوير المواقع الإلكترونية، أو خدمات الويب، أو تطبيقات الويب المعقدة.
</p>

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

<p>
	ترجمة -وبتصرّف- للمقال <a href="https://www.toptal.com/web/top-10-mistakes-that-web-developers-make" rel="external nofollow">The 10 Most Common Mistakes Web Developers Make: A Tutorial for Developers</a> لصاحبه Demir Selmanovic.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-oauth-2-r72/" rel="">مدخل إلى OAuth 2</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%AA%D9%8A-%D8%AA%D8%B8%D9%87%D8%B1-%D8%B9%D9%84%D9%89-%D9%85%D9%88%D9%82%D8%B9-%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A%D8%8C-%D9%88%D9%83%D9%8A%D9%81-%D9%86%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7%D8%9F-r2417/" rel="">ما أنواع الأخطاء التي تظهر على موقع إلكتروني، وكيف نتعامل معها؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/marketing/search-engine-optimisation/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D9%8A-%D8%AA%D8%B6%D8%B1-%D8%A8%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-r644/" rel="">الأخطاء الشائعة في تصميم المواقع الإلكترونية والتي تضر بتحسين محركات البحث</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/freelance/jobs/%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%A7%D9%84%D9%82%D8%A7%D8%AA%D9%84%D8%A9-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D8%A8-%D8%B9%D9%84%D9%89-%D9%85%D8%B5%D9%85%D9%85%D9%8A-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-%D8%AA%D8%AC%D9%86%D8%A8%D9%87%D8%A7-r72/" rel="">الأخطاء القاتلة الواجب على مصممي مواقع الإنترنت تجنبها</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%8A%D8%AB%D8%A7%D9%82-%D8%B9%D8%A8%D8%B1-%D9%85%D9%81%D8%AA%D8%A7%D8%AD-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D9%85%D8%B4%D9%81%D8%B1-token-authentication-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-nodejs-%D9%88-react-r1135/" rel="">الاستيثاق عبر token authentication في تطبيق Node.js و React</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2445</guid><pubDate>Mon, 11 Nov 2024 15:08:00 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x637;&#x631;&#x642; &#x645;&#x62A;&#x639;&#x62F;&#x62F;&#x629; &#x644;&#x625;&#x631;&#x633;&#x627;&#x644; &#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x627;&#x633;&#x62A;&#x645;&#x627;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x625;&#x644;&#x649; &#x627;&#x644;&#x62E;&#x627;&#x62F;&#x645;</title><link>https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%B7%D8%B1%D9%82-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF%D8%A9-%D9%84%D8%A5%D8%B1%D8%B3%D8%A7%D9%84-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r2407/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_09/-----.png.271c73ee59bbec028a6fc552d13b8d25.png" /></p>
<p>
	بعد أن شرحنا كيفية <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%AD%D9%82%D9%82-%D9%85%D9%86-%D8%B5%D8%AD%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A9-%D9%88%D9%8A%D8%A8-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r2406/" rel="">التحقق من صحة البيانات المدخلة من طرف العميل</a>، ستفكر بعدها في كيفية إرسال هذه البيانات إلى الخادم، وهذا ما نغطيه في هذا المقال. إذ سنناقش ما يحدث عندما ينقر المستخدم على زر اﻹرسال في استمارات أو نماذج الويب <a href="https://academy.hsoub.com/programming/html/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-web-forms-r2368/" rel="">Web Forms</a>، وأين تذهب البيانات التي نرسلها وما الذي نفعله بها عندما تصل وجهتها، كما نلقي نظرة على بعض الاعتبارات اﻷمنية المتعلقة بإرسال بيانات استمارة الويب إلى الخادم.
</p>

<p>
	ننصحك قبل المضي معنا في هذا المقال الاطلاع على <a href="https://academy.hsoub.com/programming/html/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-html-r1687/" rel="">أساسيات HTML</a> وكذلك <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-r73/" rel="">أساسيات بروتوكول HTTP</a> وأن تلقي نظرة على <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r783/" rel="">أساسيات البرمجة في طرف الخادم</a>.
</p>

<p>
	ما سنفعله تاليًا هو شرح ما يحدث للبيانات عندما تُرسل بيانات  <a href="https://academy.hsoub.com/programming/html/%D8%A7%D9%84%D9%86%D9%85%D8%A7%D8%B0%D8%AC-forms-%D9%81%D9%8A-html5-r370/" rel="">نموذج</a> من العميل client (وهو عادة متصفح الويب) إلى الخادم server.
</p>

<h2 id="-1">
	معمارية عميل-خادم
</h2>

<p>
	<a href="https://academy.hsoub.com/programming/general/%D9%83%D9%8A%D9%81-%D9%8A%D8%B9%D9%85%D9%84-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-web%D8%9F-r1690/" rel="">يعمل الويب</a> وفق معمارية تسمى عميل-خادم لتبادل البيانات ويمكن تلخيصها على النحو التالي: يُرسل العميل طلبًا إلى الخادم (<a href="https://academy.hsoub.com/devops/servers/web/%D8%A3%D9%81%D8%B6%D9%84-5-%D8%AE%D9%88%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D9%81%D8%AA%D9%88%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-r370/" rel="">خادم ويب</a> في أغلب اﻷحيان مثل <a href="https://academy.hsoub.com/devops/servers/web/apache/" rel="">Apache  </a>أو <a href="https://academy.hsoub.com/devops/servers/web/nginx/" rel="">Nginx</a> أو IIS و Tomcat وغيرها) باستخدام <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-r73/" rel="">بروتوكول HTTP</a>، ثم يجيب الخادم على الطلب باستخدام نفس البروتوكول كما توضح الصورة التالية"
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="157548" href="https://academy.hsoub.com/uploads/monthly_2024_09/01_client-server.png.45a78af10d36917fe8408b29051fab76.png" rel=""><img alt="01 client server" class="ipsImage ipsImage_thumbnailed" data-fileid="157548" data-unique="gf4jfqa50" src="https://academy.hsoub.com/uploads/monthly_2024_09/01_client-server.png.45a78af10d36917fe8408b29051fab76.png"> </a>
</p>

<p>
	وهكذا ستكون الاستمارة في صفحة ويب طريقة ملائمة لتجميع البيانات التي يدخلها المستخدمون، وتهيئة طلب HTTP ﻹرسال تلك البيانات إلى الخادم.
</p>

<p>
	<strong>ملاحظة</strong>: لتطلع أكثر على آلية عمل معمارية عميل-خادم، اطلع على مقال <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r783/" rel="">أساسيات البرمجة في طرف الخادم</a>.
</p>

<h2 id="-2">
	كيفية إرسال البيانات في طرف العميل
</h2>

<p>
	يُعرّف <a href="https://wiki.hsoub.com/HTML/form" rel="external">العنصر &lt;form&gt;</a> كيفية إرسال البيانات، وقد صممت جميع خاصياته كي تسمح لك بإعداد طلب اﻹرسال عندما ينقر المستخدم على زر اﻹرسال submit. وتُعد <code>action</code> و <code>method</code> أهم خاصيتين لتهيئة عملية إرسال الطلب وإليك شرحًا لكل منهما.
</p>

<h3 id="action">
	الخاصية <code>action</code>
</h3>

<p>
	تُعرِّف هذه الخاصية وجهة البيانات التي نرسلها، وتكون قيمتها <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87-r1435/" rel="">عنوان URL</a> مطلق أو نسبي صالح. وإن لم تخصص قيمة لهذه الخاصية ستُرسل الاستمارة إلى عنوان URL لصفحة الويب التي تضمها. تُرسل البيانات في المثال التالي إلى عنوان مطلق Absolute URL وهو  <code><a href="https://example.com" ipsnoembed="true" rel="external nofollow">https://example.com</a></code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2723_9" style=""><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">action</span><span class="pun">=</span><span class="atv">"https://example.com"</span><span class="tag">&gt;</span><span class="pln">…</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	بينا نستخدم في هذا المثال عنوان نسبي Relative URL ﻹرسال البيانات إلى عنوان URL آخر ضمن نفس النطاق، أي أنه يعتمد على موقع المورد الحالي.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2723_11" style=""><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">action</span><span class="pun">=</span><span class="atv">"/somewhere_else"</span><span class="tag">&gt;</span><span class="pln">…</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	وفي حال لم نستخدم هذه الخاصية، ستُرسل بيانات الاستمارة إلى نفس الصفحة التي تضم الاستمارة أي لنفس عنوان URL الذي تم تحميل الاستمارة منه، ويمكن أن تُستخدم هذه الطريقة في بعض الحالات البسيطة مثل معالجة البيانات وعرض النتيجة على نفس الصفحة.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2723_13" style=""><span class="tag">&lt;form&gt;</span><span class="pln">…</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	<strong>ملاحظة</strong>: من الممكن تخصيص <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87-r1435/" rel="">عنوان URL</a> يستخدم النسخة اﻵمنة من بروتوكول HTTP (بروتوكول HTTPS). وعندها تُشفَّر البيانات مع بقية أجزاء الطلب حتى لو كانت الاستمارة ضمن صفحة غير آمنة تستخدم بروتوكول HTTP. لكن، ومن ناحية أخرى، إن كانت الاستمارة ضمن صفحة آمنة وكان عنوان URL الذي خصصته للخاصية <code>action</code> غير آمن (يستخدم HTTP) سيعرض المتصفح رسالة تحذير في كل مرة يحول فيها المستخدم إرسال الاستمارة لأن البيانات لن تكون مشفَّرة.
</p>

<p>
	تُرسَل بيانات الاستمارة إن لم تكن ملفًا على شكل أزواج مكونة من اسم و قيمة <code>name=value</code> تربط بينها علامة <code>&amp;</code>، ويجب أن تكون قيمة الخاصية <code>action</code> ملفًا على الخوادم التي تستطيع التعامل مع هذا النوع من الملفات وتتأكد من صلاحيتها. يستجيب الخادم بعد ذلك، وعادة ما يعالج البيانات ويحمّل عنوان URL الذي تحدده الخاصية <code>action</code> مسببًا إعادة تحميل الصفحة (أو تحديث الصفحة إن كانت موجودة، إن أشارت الخاصية <code>action</code> إلى نفس الصفحة). لكن كيف ترسل البيانات؟ يعتمد اﻷمر هنا على قيمة الخاصية <code>method</code>.
</p>

<h3 id="method">
	الخاصية <code>method</code>
</h3>

<p>
	تعرف هذه الخاصية كيفية إرسال البيانات. إذ يوفر بروتوكول HTTP عدة طرق لتنفيذ الطلبات، ويمكن للاستمارة أن تستخدمها في إرسال بياناتها إلى الوجهة المحددة، وأكثر هذه الطلبات شيوعًا <code>GET</code> و <code>POST</code>.
</p>

<p>
	ولفهم الاختلاف بين الطلبين السابقين، علينا معرفة آلية عمل بروتوكول HTTP. ففي كل مرة تحاول الحصول فيها على مورد من الويب، يرسل المتصفح طلبًا إلى عنوان URL. ويتكون هذا الطلب من قسمين:
</p>

<ol>
	<li>
		ترويسة الطلب header تضم مجموعة من البيانات الوصفية العامة حول إمكانيات المتصفح.
	</li>
	<li>
		جسم الطلب body الذي يضم البيانات الضرورية التي يحتاجها الخادم لمعالجة الطلب.
	</li>
</ol>

<h4 id="get">
	الطلب <code>GET</code>
</h4>

<p>
	يستخدم المتصفح هذا الطلب كي يطلب من الخادم موردًا محددًا: " مرحبا! أريد هذا المورد". فيُرسل المتصفح في هذه الحالة جسم طلب فارغ، وطالما أن الجسم فارغ ستُرسل بيانات الاستمارة إلى الخادم ملحقةً بعنوان URL. لنتأمل الحالة التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2723_16" style=""><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">action</span><span class="pun">=</span><span class="atv">"http://www.foo.com"</span><span class="pln"> </span><span class="atn">method</span><span class="pun">=</span><span class="atv">"GET"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"say"</span><span class="tag">&gt;</span><span class="pln">What greeting do you want to say?</span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"say"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"say"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Hi"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"to"</span><span class="tag">&gt;</span><span class="pln">Who do you want to say it to?</span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"to"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"to"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Mom"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;button&gt;</span><span class="pln">Send my greetings</span><span class="tag">&lt;/button&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	طالما أننا استخدمنا الطلب <code>GET</code>، سيكون عنوان URL كالتالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2723_18" style=""><span class="pln"> www.foo.com/?say=Hi&amp;to=Mom</span></pre>

<p>
	وسيظهر في شريط عنوان المتصفح بهذا الشكل عند النقر على زر اﻹرسال:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="157549" href="https://academy.hsoub.com/uploads/monthly_2024_09/02_url-parameters.png.b66c9fde637407a465f385d900a4ffd5.png" rel=""><img alt="02 url parameters" class="ipsImage ipsImage_thumbnailed" data-fileid="157549" data-unique="6ibwsesyv" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_09/02_url-parameters.thumb.png.591d02ae313276cb541c6070dc890e10.png"> </a>
</p>

<p>
	لاحظ كيف ألحقت البيانات بعنوان URL على شكل أزواج اسم=قيمة تأتي بعد إشارة <code>?</code> ويفصل بين كل زوج المحرف <code>&amp;</code>. وقد مرنا في حالتنا البيانات التالية إلى الخادم:
</p>

<ul>
	<li>
		الاسم <code>say</code> قيمته <code>Hi</code>.
	</li>
	<li>
		الاسم <code>to</code> قيمته <code>Mom</code>.
	</li>
</ul>

<p>
	سيدو عنوان URL عندها بالشكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2723_20" style=""><span class="pln">GET </span><span class="pun">/?</span><span class="pln">say</span><span class="pun">=</span><span class="typ">Hi</span><span class="pun">&amp;</span><span class="pln">to</span><span class="pun">=</span><span class="typ">Mom</span><span class="pln"> HTTP</span><span class="pun">/</span><span class="lit">2.0</span><span class="pln">
</span><span class="typ">Host</span><span class="pun">:</span><span class="pln"> foo</span><span class="pun">.</span><span class="pln">com</span></pre>

<p>
	<strong>ملاحظة</strong>: لن تُلحق البيانات إن لم يكن عنوان URL المخصص قادرًا على التعامل مع الاستعلامات (أن يكون ملفًا مثلًا).
</p>

<h4 id="post">
	الطلب <code>POST</code>
</h4>

<p>
	يختلف هذا الطلب قليلًا عن <code>GET</code>، ويستخدمه المتصفح ليطلب من الخادم استجابة تتعلق بالبيانات التي يرسلها ضمن جسم طلب HTTP: " مرحبًا! ألق نظرة على هذه البيانات وأعد نتيجة مناسبة". تلحق الاستمارة البيانات بجسم طلب HTTP عند استخدام <code>POST</code>، ولكي نميز الفرق، ألق نظرة على نفس استمارة المثال السابق لكن باستخدام الطلب <code>POST</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2723_22" style=""><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">action</span><span class="pun">=</span><span class="atv">"http://www.foo.com"</span><span class="pln"> </span><span class="atn">method</span><span class="pun">=</span><span class="atv">"POST"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"say"</span><span class="tag">&gt;</span><span class="pln">What greeting do you want to say?</span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"say"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"say"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Hi"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"to"</span><span class="tag">&gt;</span><span class="pln">Who do you want to say it to?</span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"to"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"to"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Mom"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;button&gt;</span><span class="pln">Send my greetings</span><span class="tag">&lt;/button&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	لا تُلحق بيانات الاستمارة بعنوان URL في حال استخدمت الطلب <code>POST</code> بل ستكون ضمن جسم الطلب الذي سيبدو بالشكل:
</p>

<pre class="ipsCode">POST / HTTP/2.0
Host: foo.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13

say=Hi&amp;to=Mom
</pre>

<p>
	تشير الترويسة <code>Content-Length</code> إلى حجم جسم الطلب، والترويسة <code>Content-Type</code> إلى نوع المصدر الذي ترسله إلى الخادم، وسنناقش هاتين الترويستين لاحقًا.
</p>

<p>
	<strong>ملاحظة</strong>:سيُستخدم الطلب <code>GET</code> إن لم يكن عنوان URL المخصص قادرًا على التعامل مع جسم الطلب (كأن يكون على الشكل <code>:data</code> مثلًا).
</p>

<h3 id="http">
	عرض طلبات HTTP
</h3>

<p>
	لا تُعرض طلبات HTTP للمستخدم أبدًا، ولكن إن أردت رؤيتها ومعرفة تفاصيل الطلبات واستجابة الخادم عليك استخدام بعض اﻷدوات مثل <a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D8%B7%D9%88%D8%B1%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D9%85%D8%AF%D9%85%D8%AC%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1439/" rel="">أدوات مطوري ويب</a> على كروم أو مراقب الشبكة على فايرفوكس. وكمثال على ذلك، بإمكانك استعراض بيانات المثال السابق ضمن نافذة شبكة كروم Chrome Network بعد إرسال الاستمارة كالتالي:
</p>

<ol>
	<li>
		<p>
			افتح نافذة أدوات مطوري ويب developer tools.
		</p>
	</li>
	<li>
		<p>
			اختر "شبكة Network".
		</p>
	</li>
	<li>
		<p>
			اختر "الكل All".
		</p>
	</li>
	<li>
		<p>
			اختر الموقع الذي تريد وليكن "foo.com" في النافذة "اسم Name".
		</p>
	</li>
	<li>
		<p>
			اختر "ترويسات Headers".
		</p>

		<p>
			يمكنك عندها الحصول على بيانات النموذج Form Data كما في لقطة الشاشة التالية:
		</p>
	</li>
</ol>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="157550" href="https://academy.hsoub.com/uploads/monthly_2024_09/03_network-monitor.png.10ee6a6a536d8681c826b1b9abdbdcfe.png" rel=""><img alt="03 network monitor" class="ipsImage ipsImage_thumbnailed" data-fileid="157550" data-unique="2v6kw4tbg" src="https://academy.hsoub.com/uploads/monthly_2024_09/03_network-monitor.png.10ee6a6a536d8681c826b1b9abdbdcfe.png"> </a>
</p>

<p>
	ما عُرض على المستخدم هنا هو عنوان URL الذي استدعاه فقط. وكما قلنا سابقًا، سيرى المستخدم البيانات ملحقة بعنوان URL ضمن شريط عنوان المتصفح عندما يستخدم الطلب <code>GET</code>، لكنه لن يرى شيئًا إن كان الطلب هو <code>POST</code>، ولهذا اﻷمر أهميته لسببين:
</p>

<ol>
	<li>
		لا تستخدم <code>GET</code> على اﻹطلاق إن أردت إرسال كلمة مرور (أو غيرها من البيانات الحساسة)، لأن هذه البيانات ستُعرض ضمن العنوان وهذا غير آمن.
	</li>
	<li>
		يُفضّل استخدام <code>POST</code> إن احتجت إلى إرسال كمية كبيرة من البيانات. إذ تحدد بعض المتصفحات حجم عناوين URL، كما تحدد الخوادم أيضًا طول عناوين URL التي تقبلها.
	</li>
</ol>

<h2 id="-3">
	كيف تستخلص البيانات في طرف الخادم
</h2>

<p>
	أيًا كان طلب HTTP الذي استخدمته، ستصل البيانات إلى الخادم على شكل نصي يُفسره لاستخلاص البيانات على شكل قائمة من الأزواج مفتاح/قيمة. أما طريقة التعامل مع هذه القائمة، فتعود إلى بيئة التطوير أو <a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">إطارات العمل</a> التي تستخدمها.
</p>

<h3 id="php">
	مثال على إرسال البيانات للخادم باستخدام لغة PHP صرفة
</h3>

<p>
	تقدم <a href="https://wiki.hsoub.com/PHP" rel="external">PHP</a> بعض الكائنات العامة للوصول إلى البيانات في طرف الخادم. فلو افترضنا أنك استخدمت الطلب <code>POST</code> كما في المثال التالي، سيعرض هذا المثال البيانات المرسلة للمستخدم فقط. وبالطبع، يعود اﻷمر إليك فيما تفعله بهذه البيانات، فقد تعرضها أو تخزنها في قاعدة بيانات أو ترسلها ضمن بريد إلكتروني وهكذا..
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_2723_25" style=""><span class="pun">&lt;?</span><span class="pln">php
  </span><span class="com">// الوصول إلى البيانات المستلمة باسمها $_POST يتيح لك المتغير العام</span><span class="pln">
  </span><span class="com">//$_GET نستخدم المتغير GET وللوصول إلى البيانات المرسلة باستخدام الطلب</span><span class="pln">
  $say </span><span class="pun">=</span><span class="pln"> htmlspecialchars</span><span class="pun">(</span><span class="pln">$_POST</span><span class="pun">[</span><span class="str">'say'</span><span class="pun">]);</span><span class="pln">
  $to  </span><span class="pun">=</span><span class="pln"> htmlspecialchars</span><span class="pun">(</span><span class="pln">$_POST</span><span class="pun">[</span><span class="str">'to'</span><span class="pun">]);</span><span class="pln">

  echo  $say</span><span class="pun">,</span><span class="pln"> </span><span class="str">' '</span><span class="pun">,</span><span class="pln"> $to</span><span class="pun">;</span><span class="pln">
</span><span class="pun">?&gt;</span></pre>

<p>
	يعرض <a href="https://github.com/mdn/learning-area/blob/main/html/forms/sending-form-data/php-example.html" rel="external nofollow">هذا المثال</a> صفحة ويب تضم البيانات التي أرسلناها، يضم ملف المثال نفس استمارة التمرين السابق وتكون قيمة الخاصية <code>method</code> هي <code>POST</code> وقيمة الخاصية <code>action</code> هي <code>php-example.php</code>. عند النقر على زر اﻹرسال، يُرسل المتصفح البيانات إلى الملف <code>php-example.php</code> الذي يضم الشيفرة التي عرضناها سابقًا، وستكون نتيجة تنفيذ هذه الشيفرة كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="157551" href="https://academy.hsoub.com/uploads/monthly_2024_09/04_php-result.png.30abddc3018ab2eb5d1e1e0ce03678ff.png" rel=""><img alt="04 php result" class="ipsImage ipsImage_thumbnailed" data-fileid="157551" data-unique="pjsjv9046" src="https://academy.hsoub.com/uploads/monthly_2024_09/04_php-result.png.30abddc3018ab2eb5d1e1e0ce03678ff.png"> </a>
</p>

<p>
	<strong>ملاحظة</strong>: لن يعمل المثال السابق إن حملته ضمن المتصفح مباشرة، فلا يمكن للمتصفحات تفسير لغة PHP. لهذا عندما ترسل الاستمارة، سيعرض المتصفح تحميل ملف PHP فقط. كي يعمل هذا الملف، لابد من تنفيذه على خادم يدعم PHP. ومن الخيارات المتاحة لاختبار PHP محليًا نقترح <a href="https://www.mamp.info/en/downloads/" rel="external nofollow">MAMP</a> ( على نظام التشغيل ويندوز وماك أو إس) و <a href="https://ampps.com/downloads/" rel="external nofollow">AMPPS</a> (على نظام ويندوز وماك و لينكس).<br>
	وفي حال استخدمت MAMP ولم تكن قد ثبّته مسبقًا (أو كانت نسخة تجريبية انتهت صلاحيتها) فقد تواجهك بعض المشكلات عند تشغيل الملف. ولكي يعمل مجددًا، حمل التطبيق واختر <code>NAMP&gt;Preferences&gt;PHP</code> ثم اضبط قيمة <code>Standard Version</code> على<code>*.7.2</code> وستختلف قيمة <code>*</code> وفقًا للنسخة التي ثبّتها.
</p>

<h3 id="-4">
	مثال على إرسال البيانات للخادم باستخدام لغة بايثون
</h3>

<p>
	سنعرض في هذه الفقرة نفس المثال السابق لكن باستخدام لغة <a href="https://wiki.hsoub.com/Python" rel="external">بايثون</a> وإطار العمل فلاسك <a href="https://academy.hsoub.com/programming/python/flask/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%81%D9%84%D8%A7%D8%B3%D9%83-flask-r2201/" rel="">Flask</a> لتصيير القوالب والتعامل مع بيانات الاستمارة. بإمكانك الاطلاع على <a href="https://github.com/mdn/learning-area/blob/main/html/forms/sending-form-data/python-example.py" rel="external nofollow">ملف المثال</a> على جيت-هاب:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2723_28" style=""><span class="kwd">from</span><span class="pln"> flask </span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Flask</span><span class="pun">,</span><span class="pln"> render_template</span><span class="pun">,</span><span class="pln"> request

app </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Flask</span><span class="pun">(</span><span class="pln">__name__</span><span class="pun">)</span><span class="pln">

</span><span class="lit">@app</span><span class="pun">.</span><span class="pln">route</span><span class="pun">(</span><span class="str">'/'</span><span class="pun">,</span><span class="pln"> methods</span><span class="pun">=[</span><span class="str">'GET'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">])</span><span class="pln">
</span><span class="kwd">def</span><span class="pln"> form</span><span class="pun">():</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> render_template</span><span class="pun">(</span><span class="str">'form.html'</span><span class="pun">)</span><span class="pln">

</span><span class="lit">@app</span><span class="pun">.</span><span class="pln">route</span><span class="pun">(</span><span class="str">'/hello'</span><span class="pun">,</span><span class="pln"> methods</span><span class="pun">=[</span><span class="str">'GET'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">])</span><span class="pln">
</span><span class="kwd">def</span><span class="pln"> hello</span><span class="pun">():</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> render_template</span><span class="pun">(</span><span class="str">'greeting.html'</span><span class="pun">,</span><span class="pln"> say</span><span class="pun">=</span><span class="pln">request</span><span class="pun">.</span><span class="pln">form</span><span class="pun">[</span><span class="str">'say'</span><span class="pun">],</span><span class="pln"> to</span><span class="pun">=</span><span class="pln">request</span><span class="pun">.</span><span class="pln">form</span><span class="pun">[</span><span class="str">'to'</span><span class="pun">])</span><span class="pln">

</span><span class="kwd">if</span><span class="pln"> __name__ </span><span class="pun">==</span><span class="pln"> </span><span class="str">"__main__"</span><span class="pun">:</span><span class="pln">
    app</span><span class="pun">.</span><span class="pln">run</span><span class="pun">()</span></pre>

<p>
	يتعامل المثال السابق مع قالبين ينبغي أن يكونا ضمن مجلد فرعي باسم<code>templates</code> في نفس المجلد الذي يضم الملف <code>python-example.py</code> إن أردت تشغيل المثال بنفسك:
</p>

<ul>
	<li>
		القالب <code>form.html</code>: وهو ملف يضم نفس الاستمارة التي تعاملنا معها في المثال السابق، لكن ضبطت قيمة الخاصية <code>action</code> لتكون <code>{{('helo')url_for}}</code>. يُدعى هذا القالب قالب <a href="https://jinja.palletsprojects.com" rel="external nofollow">Jinja</a> وهو أساسًا ملف HTML لكن يمكن أن يضم استدعاءات لشيفرة بايثون التي تعمل على الخادم بوضعها ضمن أقواس معقوصة. وتعني الشيفرة <code>url_for('hello')</code> : انتقل إلى الوجهة <code>hello/</code> عند إرسال الاستمارة.
	</li>
	<li>
		القالب <code>greeting.html</code>: ويضم فقط شيفرة لتصيير البيانات التي نمررها إليه عندما تكون جاهزة. وينفذ اﻷمر باستخدام الدالة <code>()hello</code> التي تُنفذ عند الانتقال إلى الوجهة <code>hello/</code>.
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: لن يعمل المثال السابق إن حملته ضمن المتصفح مباشرة، إذ تعمل لغة بايثون بشكل مختلف قليلًا عن PHP. لتنفيذ هذا المثال على متصفحك عليك <a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%84%D9%84%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D9%85%D8%B9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1751/" rel="">تثبيت وإعداد Python</a><span style="display: none;"> </span>، ثم <a href="https://academy.hsoub.com/programming/python/flask/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-%D9%81%D9%84%D8%A7%D8%B3%D9%83-flask-%D9%85%D9%86-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1622/" rel="">تثبيت Flask</a> باستخدام اﻷمر <code>pip3 install flask</code>. وبعد الانتهاء من المفترض أن تتمكن من تشغيل المثال باستخدام <code>python3 python-example.py</code>. بعد ذلك اكتب العنوان التالي في متصفحك <code>localhost:5042</code>.
</p>

<h3 id="-5">
	لغات وإطارات عمل أخرى
</h3>

<p>
	ستجد العديد من التقنيات المتوفرة للتعامل مع الاستمارات في طرف الخادم بما في ذلك بيرل Perl و<a href="https://academy.hsoub.com/programming/java/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-java-r2275/" rel="">جافا Java</a> وروبي Ruby ودوت نت Net وغيرها. ومن الجدير بالذكر أن استخدام هذه التقنيات مباشرة ليس شائعًا لأن التعامل معها قد يكون مربكًا. لهذا من اﻷفضل العمل مع إطارات عمل عالية الجودة تُسهّل التعامل مع الاستمارات مثل:
</p>

<ul>
	<li>
		<p>
			إطارات عمل <a href="https://wiki.hsoub.com/Python" rel="external">بايثون</a>:
		</p>

		<ul>
			<li>
				<p>
					جانغو Django
				</p>
			</li>
			<li>
				<p>
					فلاسك Flask
				</p>
			</li>
			<li>
				<p>
					web2py (سهلة للمبتدئين).
				</p>
			</li>
			<li>
				<p>
					py4web (طوّرت من قبل نفس مطوري wep2py لكنها تشبه دجانغو قليلًا من ناحية اﻹعداد).
				</p>
			</li>
		</ul>
	</li>
	<li>
		<p>
			إطارت عمل <a href="https://wiki.hsoub.com/Node.js" rel="external">Node.js</a>
		</p>

		<ul>
			<li>
				<p>
					EXpress
				</p>
			</li>
			<li>
				<p>
					<a href="https://wiki.hsoub.com/Next.js" rel="external">Next.js</a> (لتطبيقات <a href="https://wiki.hsoub.com/React" rel="external">رياكت</a>).
				</p>
			</li>
			<li>
				<p>
					Nuxt (لتطبيقات Vue).
				</p>
			</li>
			<li>
				<p>
					Remix
				</p>
			</li>
		</ul>
	</li>
	<li>
		<p>
			إطارات <a href="https://wiki.hsoub.com/PHP" rel="external">PHP</a>
		</p>

		<ul>
			<li>
				<p>
					لارافيل Laravel
				</p>
			</li>
			<li>
				<p>
					سمفوني Symfony
				</p>
			</li>
		</ul>
	</li>
	<li>
		<p>
			إطارات <a href="https://wiki.hsoub.com/Ruby" rel="external">Ruby</a>
		</p>

		<ul>
			<li>
				<p>
					<a href="https://wiki.hsoub.com/Rails" rel="external">Ruby On Rails</a>
				</p>
			</li>
		</ul>
	</li>
	<li>
		<p>
			إطارات جافا
		</p>

		<ul>
			<li>
				<p>
					Spring Boot
				</p>
			</li>
		</ul>
	</li>
</ul>

<p>
	وتجدر اﻹشارة إلى أن العمل مع الاستمارات لن يكون سهلًا بالضرورة عند استخدام أطر العمل، لكنه أسهل بكثير مقارنة مع كتابة جميع الوظائف الضرورية من الصفر، وتوفر عليك الوقت والجهد بالتأكيد.
</p>

<p>
	لن تتعلم بالطبع من أمثلة هذا المقال كل ما تحتاجه للعمل مع لغات البرمجة من طرف الخادم فهو غير مخصص لهذا اﻷمر، لكنك ستجد الكثير من المقالات المفيدة التي تساعدك على الانطلاق ضمن <a href="https://academy.hsoub.com/" rel="">أكاديمية حسوب</a> و<a href="https://wiki.hsoub.com/" rel="external">موسوعة حسوب</a>، إضافة إلى <a href="https://academy.hsoub.com/#courses" rel="">دورات البرمجة</a> التي تبدأ بك من الصفر حتى الاحتراف وإعداد معرض اﻷعمال.
</p>

<h2 id="-6">
	حالة خاصة: إرسال ملف
</h2>

<p>
	تُمثَّل البيانات في الملف على شكل بيانات ثنائية -أو تُعد كذلك- بينما تكون بقية أنواع البيانات المرسلة نصية. وطالما أن HTTP هو بروتوكول نصي، هناك متطلبات خاصة للتعامل مع البيانات الثنائية.
</p>

<h3 id="enctype">
	الخاصية <code>enctype</code>
</h3>

<p>
	تسمح لك هذه الخاصية بتحدبد قيمة الترويسة <code>Content_Type</code> في طلب HTTP الذي ينتج عن تسليم الاستمارة. وهذه الترويسة مهمة جدًا لأنها تخبر الخادم عن نوع البيانات التي أرسلت. تأخذ الخاصية افتراضيًا القيمة التالية <code>application/x-www-form-urlencoded</code> والتي<br>
	تعني: "هذه بيانات استمارة شُفرت ضمن عنوان URL". لكن إن أردت إرسال ملف، عليك تنفيذ الخطوات اﻹضافية التالية:
</p>

<ul>
	<li>
		اضبط قيمة الخاصية <code>method</code> على <code>POST</code> لأن محتوى الملف لا يمكن وضعه ضمن عنوان URL.
	</li>
	<li>
		اضبط قيمة الخاصية <code>enctype</code> على <code>multipart/form-data</code> لأن البيانات ستُجزّأ إلى أقسام متعددة : قسم لكل ملف وقسم للبيانات النصية التي يضمها جسم الطلب (في حال وجدت بيانات نصية أخرى في الاستمارة).
	</li>
	<li>
		استخدام عنصر <code>&lt;'input type='file&gt;</code> أو أكثر لتسمح للمستخدم باختيار الملف أو الملفات التي يريد رفعها.
	</li>
</ul>

<p>
	إليك مثالًا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2723_35" style=""><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">method</span><span class="pun">=</span><span class="atv">"post"</span><span class="pln"> </span><span class="atn">action</span><span class="pun">=</span><span class="atv">"https://www.foo.com"</span><span class="pln"> </span><span class="atn">enctype</span><span class="pun">=</span><span class="atv">"multipart/form-data"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"file"</span><span class="tag">&gt;</span><span class="pln">Choose a file</span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"file"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"file"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"myFile"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;button&gt;</span><span class="pln">Send the file</span><span class="tag">&lt;/button&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	<strong>ملاحظة</strong>: قد تُهيّأ الخوادم لتقبل حجمًا محدودًا للملفات أو طلبات HTTP لمنع الاستخدام غير الصحيح لمواردها.
</p>

<h2 id="-7">
	اعتبارات أمنية
</h2>

<p>
	عليك الانتباه إلى أية اعتبارات أمنية قبل إرسال البيانات إلى الخادم، لأن استمارات HTML حتى اللحظة هي أكثر النقاط التي تهاجم الخوادم منها. وليست المشكلة في الاستمارة نفسها، بل بكيفية تعامل الخادم مع بيانات الاستمارة.
</p>

<p>
	ننصحك بالاطلاع على مقال "<a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D9%85%D8%A7%D9%86-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r2020/" rel="">تعرف على أمان مواقع الويب</a>" الذي يناقش أكثر الهجمات شيوعًا والطرق الممكنة لصدها، لكي تكوّن تصورًا واضحًا عما يمكن أن يحدث.
</p>

<h3 id="-8">
	كن مرتابًا ولا تثق أبدًا بالمستخدم
</h3>

<p>
	إن القاعدة الأهم والتي عليك تذكرها دائمًا هي: لا تثق أبدًا بالمستخدمين بمن فيهم أنت، فقد يتعرض أي مستخدم حتى الموثوقون إلى هجمات سرقة البيانات. لهذا يجب أن تتحقق دائمًا من كل البيانات التي تصل إلى الخادم وتنقيها دون أي استثناء.
</p>

<ul>
	<li>
		<strong>تجاوز escape أي محرف قد يشكل خطرًا:</strong> تختلف المحارف التي يجب الانتباه إليها حسب سياق البيانات المدخلة ومنصة عمل الخادم. وتمتلك جميع لغات البرمجة التي تعمل من طرف الخادم دوال للتعامل مع هذه اﻷمور. أما اﻷشياء التي عليك مراقبتها فهي أي تسلسل لمحارف تبدو وكأنها شيفرة تنفيذية (مثل جافا سكريبت أو أوامر <a href="https://wiki.hsoub.com/SQL" rel="external">SQL</a>).
	</li>
	<li>
		<strong>حدد كميات البيانات التي يمكن استقبالها:</strong> حتى تسمح بإرسال البيانات الضرورية فقط.
	</li>
	<li>
		<strong>احتجز الملفات المرفوعة إلى الخادم</strong>: خزّن هذه الملفات على خادم مختلف واسمح بالوصول إليها عبر نطاقات فرعية أو اﻷفضل عبر نطاقات مختلفة تمامًا عن نطاق موقعك.
	</li>
</ul>

<p>
	ستتجنب الكثير من المشاكل المتعلقة باﻷمان وحتى معظمها إن اتبعت الخطوات السابقة. مع ذلك، من اﻷفضل أن تنفذ اختبارًا امنيًا بمساعدة طرف خارجي، فلا تفترض أبدًا أنك حددت جميع المشاكل المحتملة.
</p>

<h2 id="-9">
	الخلاصة
</h2>

<p>
	إن إرسال البيانات للخادم أمر سهل نسبيًا، لكن تأمين التطبيق من خطر هذه البيانات أمر مربك، وتذكر دائمًا أن <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A3%D9%85%D8%A7%D9%85%D9%8A%D8%A9-frontend-web-development/" rel="">مطور واجهة المستخدم</a> ليس الشخص المسؤول عن وضع نموذج لتقييم سلامة البيانات. وعلى الرغم من إمكانية إجراء تقييم لصلاحية البيانات في طرف العميل، لكن لا يمكن للخادم الوثوق بهذا التقييم، لأنه لا يعرف إطلاقًا ما يحدث حقيقية في طرف العميل.
</p>

<p>
	وهكذا نكون قد وصلنا إلى نهاية هذه السلسلة التي تحدثنا فيها عن استمارات الويب وطرق إنشائها وتنسيقها والتحقق من صلاحية بياناتها ثم إرسال البيانات إلى الخادم.
</p>

<p>
	ترجمة -وبتصرف- للمقال: <a href="https://developer.mozilla.org/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data" rel="external nofollow">Sending form data</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%AD%D9%82%D9%82-%D9%85%D9%86-%D8%B5%D8%AD%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A9-%D9%88%D9%8A%D8%A8-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r2406/" rel="">التحقق من صحة بيانات استمارة ويب في طرف العميل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/cpp/%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%AE%D8%A7%D8%AF%D9%85-%D8%B9%D9%85%D9%8A%D9%84-client-server-%D8%A8%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D9%84%D8%BA%D8%A9-cpp-r1184/" rel="">أمثلة على التعامل مع خادم عميل (Client server) باستعمال لغة Cpp</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A5%D8%B1%D8%B3%D8%A7%D9%84-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A7%D8%AA-form-submit-%D9%88%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1196/" rel="">إرسال الاستمارات (form submit) ومعالجتها في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B9%D9%84%D9%89-%D8%AA%D9%81%D8%A7%D8%B9%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-%D9%85%D8%B9-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%81%D9%8A-%D9%85%D9%88%D9%82%D8%B9-%D9%88%D9%8A%D8%A8-%D8%AF%D9%8A%D9%86%D8%A7%D9%85%D9%8A%D9%83%D9%8A-r782/" rel="">نظرة على تفاعلات الخادم مع العميل في موقع ويب ديناميكي</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/flask/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-%D9%81%D9%84%D8%A7%D8%B3%D9%83-flask-%D9%85%D9%86-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1622/" rel="">إنشاء تطبيق ويب باستخدام إطار عمل فلاسك Flask من لغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2407</guid><pubDate>Mon, 30 Sep 2024 15:09:01 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x623;&#x62F;&#x627;&#x629; TestGrid &#x648;&#x623;&#x647;&#x645;&#x64A;&#x62A;&#x647;&#x627; &#x641;&#x64A; &#x623;&#x62A;&#x645;&#x62A;&#x629; &#x627;&#x644;&#x627;&#x62E;&#x62A;&#x628;&#x627;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629;</title><link>https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%AF%D8%A7%D8%A9-testgrid-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%A3%D8%AA%D9%85%D8%AA%D8%A9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r2390/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_08/-------TestGrid.png.b5888da755cfb410a96d82040d04eb92.png" /></p>
<p>
	تعد الاختبارات جزءًا أساسيًا من عملية تطوير البرمجيات فهي تضمن تقديم منتج برمجي عالي الجودة ويلبي توقعات المستخدمين، ومن المتعارف عليه أن يضع المبرمجون آلية معينة لأتمتة اختبار البرمجيات الخاصة بهم باستخدام أدوات وأكواد برمجية مختلفة.
</p>

<p>
	ولكن من الملاحظ أن الأدوات المستخدمة لاختبار البرمجيات لم تتطور بنفس الوتيرة التي تطورت بها البرمجيات نفسها، وتدل هذه الفجوة على مقدار الحاجة لتطوير وتحديث طرق أتمتة الاختبارات (test automation methods) لتواكب سرعة تطور البرمجيات نفسها.
</p>

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

<h2 id="">
	استخدام الطرق التقليدية لأتمتة الاختبارات
</h2>

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

<p>
	أحد العيوب الرئيسية لهذه الطريقة هو صعوبة صيانة سكريبتات الاختبارات عندما يزداد حجم هذه التطبيقات وتضاف لها وظائف إضافية، مما يؤدي إلى ارتفاع تكاليف الصيانة، كما أنه قد يجعل هذه السكريبتات غير مستقرة وقد تعطي نتائج خاطئة.
</p>

<p>
	في الفقرات القادمة سنلقي نظرة على طريقتين معتمدتين بشكل واسع في أتمتة عملية الاختبار باستخدام <a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">إطار عمل</a> أتمتة الاختبارات سيلينيوم Selenium وإطار أبيوم Appium.
</p>

<h3 id="selenium">
	استخدام الأداة سيلينيوم Selenium لأتمتة اختبار تطبيقات الويب
</h3>

<p>
	تعتبر أداة سيلينيوم <a href="https://www.selenium.dev/" rel="external nofollow">Selenium</a> الأداة الأكثر شيوعًا لأتمتة عمليات الاختبار التي يستخدمها المطورون والمبرمجون لكتابة اختبارات واجهة المستخدم المؤتمتة لتطبيقات الويب.
</p>

<p>
	يمكن تنفيذ هذه الاختبارات في جميع متصفحات الويب ويمكن كتابة هذه الاختبارات بإحدى <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> الكثيرة التي تدعمها الأداة سيلينيوم Selenium.
</p>

<p>
	على سبيل المثال سنكتب اختبار مؤتمت باستخدام الأداة سيلينيوم حيث سنستخدم <a href="https://demoqa.com/" rel="external nofollow">موقع ويب تجريبي</a> لتنفيذ الخطوات على النحو التالي:
</p>

<ol>
	<li>
		تشغيل متصفح الويب (أي متصفح من اختيارك).
	</li>
	<li>
		الانتقال إلى <a href="https://demoqa.com/login" rel="external nofollow">صفحة تسجيل الدخول</a> للموقع المراد اختباره.
	</li>
	<li>
		إدخال اسم المستخدم وكلمة السر.
	</li>
	<li>
		الضغط على زر تسجيل الدخول Login.
	</li>
	<li>
		التأكد من تسجيل دخول المستخدم عن طريق ظهور اسم المستخدم على الشاشة.
	</li>
</ol>

<p>
	<strong>ملاحظة1</strong>: سنستخدم الموقع <a href="https://demoqa.com/" rel="external nofollow">demoqa.com</a> وهو موقع تجريبي مفيد جداً للمطورين والمهتمين بتعلم أتمتة الاختبارات ويوفر مجموعة متنوعة من الأدوات والصفحات الوهمية التي يمكن استخدامها لأتمتة الاختبارات باستخدام أدوات مثل Selenium و Appium ويمكن للمستخدمين اختبار سيناريوهات مختلفة مثل تسجيل الدخول وتعبئة النماذج والتفاعل مع عناصر الواجهة وغيرها من العمليات الشائعة في مواقع الويب.
</p>

<p>
	<strong>ملاحظة2</strong>: يمكنك إنشاء مستخدم جديد بالضغط على زر مستخدم جديد New User بحيث يكون لدينا بيانات اعتماد صحيحة لسيناريو أتمتة عملية الاختبار.
</p>

<p>
	سنستخدم كود Selenium التالي لتنفيذ خطوات أتمتة اختبار تسجيل الدخول إلى موقع ويب:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8473_12" style=""><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Login</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="lit">@Test</span><span class="pln">
  </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> loginTest</span><span class="pun">()</span><span class="pln"> throws </span><span class="typ">InterruptedException</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="typ">WebDriver</span><span class="pln"> driver </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ChromeDriver</span><span class="pun">();</span><span class="pln"> </span><span class="com">// Instantiating the webdriver instance</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="str">"https://demoqa.com/login"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Launching the website</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">manage</span><span class="pun">().</span><span class="pln">window</span><span class="pun">().</span><span class="pln">maximize</span><span class="pun">();</span><span class="pln"> </span><span class="com">// Maximizing browser window</span><span class="pln">

    </span><span class="typ">String</span><span class="pln"> user </span><span class="pun">=</span><span class="pln"> </span><span class="str">"your user name"</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">String</span><span class="pln"> pass </span><span class="pun">=</span><span class="pln"> </span><span class="str">"your password"</span><span class="pun">;</span><span class="pln">

    </span><span class="com">// Locating web elements for username, password, and the Login button</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">By</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"userName"</span><span class="pun">)).</span><span class="pln">sendKeys</span><span class="pun">(</span><span class="pln">user</span><span class="pun">);</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">By</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"password"</span><span class="pun">)).</span><span class="pln">sendKeys</span><span class="pun">(</span><span class="pln">pass</span><span class="pun">);</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">By</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"login"</span><span class="pun">)).</span><span class="pln">click</span><span class="pun">();</span><span class="pln">

    </span><span class="com">// Using sleep to load web elements on page</span><span class="pln">
    </span><span class="typ">Thread</span><span class="pun">.</span><span class="pln">sleep</span><span class="pun">(</span><span class="lit">5000</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// Capturing the displayed username after login</span><span class="pln">
    </span><span class="typ">String</span><span class="pln"> uName </span><span class="pun">=</span><span class="pln"> driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">By</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"userName-value"</span><span class="pun">)).</span><span class="pln">getText</span><span class="pun">();</span><span class="pln">

    </span><span class="com">// Asserting that the logged-in username matches the expected username</span><span class="pln">
    </span><span class="typ">Assert</span><span class="pun">.</span><span class="pln">assertEquals</span><span class="pun">(</span><span class="pln">uName</span><span class="pun">,</span><span class="pln"> user</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// Locating the web element for the Logout button and closing the webdriver instance</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">By</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"submit"</span><span class="pun">)).</span><span class="pln">click</span><span class="pun">();</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">quit</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يفتح الكود السابق صفحة تسجيل الدخول ويدخل اسم المستخدم وكلمة المرور وويحاول تسجيل الدخول والتحقق من نجاح عملية التسجيل، ثم يسجل الخروج ويغلق المتصفح. فكما توضح التعليقات الموجودة في الكود سيُفتَح موقعنا الإلكتروني وتُحدد عناصر الويب ثم تنفذ الإجراءات المطلوبة عليها، وبعد تنفيذ الكود سترى سجلات التنفيذ الناجحة.
</p>

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

<h3 id="seleniumtestgrid">
	استخدام الأداة Selenium مع الأداة TestGrid لأتمتة اختبار تطبيقات الويب
</h3>

<p>
	لنحاول تشغيل كود الاختبار السابق الذي كتبناه باستخدام Selenium على أجهزة حقيقية متوفرة عبر سحابة TestGrid، حيث تسمح لنا الأداة TestGrid بإعادة استخدام سكريبتات الاختبار الموجودة في الأداة Selenium وتوسيع نطاق تنفيذ الاختبار المؤتمت.
</p>

<p>
	<strong>ملاحظة</strong>: TestGrid هي خدمة تقدم سحابة من الأجهزة الحقيقية التي يمكنك استخدامها لتشغيل اختبارات البرمجيات، فبدلاً من استخدام أجهزتك المحلية يمكنك استخدام هذه السحابة لاختبار التطبيقات على مجموعة متنوعة من المتصفحات والأنظمة الأساسية.
</p>

<p>
	بعد تسجيل الدخول إلى الموقع <a href="https://public.testgrid.io" rel="external nofollow">TestGrid</a> وتنفيذ الخطوات الموجودة على <a href="https://testgrid.io/docs/document/executing-your-local-selenium-code-for-web-browsers/" rel="external nofollow">الرابط التالي</a> الذي يشرح كيفية تشغيل اختبارات Selenium المكتوبة محليًا على بيئة متصفح موجهة عبر TestGrid ستحصل على الكود التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8473_18" style=""><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">TestGridDemoQaLogin</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="lit">@Test</span><span class="pln">
  </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> loginTest</span><span class="pun">()</span><span class="pln"> throws </span><span class="typ">InterruptedException</span><span class="pun">,</span><span class="pln"> </span><span class="typ">MalformedURLException</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="com">// Setting the desired capabilities to use the TestGrid platform</span><span class="pln">
    </span><span class="typ">DesiredCapabilities</span><span class="pln"> dc </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">DesiredCapabilities</span><span class="pun">();</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"browserName"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"firefox"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"platformName"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"linux"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"tg:userToken"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Your Token"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"tg:udid"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"201"</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// Instantiating the remote webdriver instance and passing desired capabilities to it</span><span class="pln">
    </span><span class="typ">WebDriver</span><span class="pln"> driver </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RemoteWebDriver</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> URL</span><span class="pun">(</span><span class="str">"Your Browser Run URL(Public)"</span><span class="pun">),</span><span class="pln">dc</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// Remaining steps are the same as in the previous Selenium code</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">get</span><span class="pun">(</span><span class="str">"https://demoqa.com/login"</span><span class="pun">);</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">manage</span><span class="pun">().</span><span class="pln">window</span><span class="pun">().</span><span class="pln">maximize</span><span class="pun">();</span><span class="pln">

    </span><span class="typ">String</span><span class="pln"> user </span><span class="pun">=</span><span class="pln"> </span><span class="str">"your user name"</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">String</span><span class="pln"> pass </span><span class="pun">=</span><span class="pln"> </span><span class="str">"your password"</span><span class="pun">;</span><span class="pln">

    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">By</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"userName"</span><span class="pun">)).</span><span class="pln">sendKeys</span><span class="pun">(</span><span class="pln">user</span><span class="pun">);</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">By</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"password"</span><span class="pun">)).</span><span class="pln">sendKeys</span><span class="pun">(</span><span class="pln">pass</span><span class="pun">);</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">By</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"login"</span><span class="pun">)).</span><span class="pln">click</span><span class="pun">();</span><span class="pln">

    </span><span class="typ">Thread</span><span class="pun">.</span><span class="pln">sleep</span><span class="pun">(</span><span class="lit">5000</span><span class="pun">);</span><span class="pln">

    </span><span class="typ">String</span><span class="pln"> uName </span><span class="pun">=</span><span class="pln"> driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">By</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"userName-value"</span><span class="pun">)).</span><span class="pln">getText</span><span class="pun">();</span><span class="pln">

    </span><span class="typ">Assert</span><span class="pun">.</span><span class="pln">assertEquals</span><span class="pun">(</span><span class="pln">uName</span><span class="pun">,</span><span class="pln"> user</span><span class="pun">);</span><span class="pln">

    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">By</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"submit"</span><span class="pun">)).</span><span class="pln">click</span><span class="pun">();</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">quit</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عندما تنفذ هذا الكود على جهازك المحلي، ستلاحظ أن نفس الكود سيُنفَّذ على الجهاز الافتراضي للأداة TestGrid عبر الإنترنت لرؤية كيفية تنفيذ الاختبارات على السحابة.
</p>

<p>
	وبهذه الطريقة يمكنك مراقبة عملية تنفيذ الكود عن بعد من خلال البوابة الإلكترونية للأداة وتشخيص أي مشكلات تتعلق بتنفيذه، كما يمكنك أيضًا مشاهدة حالة التنفيذ (Execution Status) والتسجيلات (Recording) وتشغيل الملخص (Run Summary) الموجود في التبويب <strong>Automation Sessions</strong> ببساطة عن طريق اختيار الجهاز الذي ستنفذ عليه عملية الاختبار مما يوفر لك إمكانية تتبع الأداء والتحقق من صحة نتائج الاختبارات عن بُعد.
</p>

<h3 id="appium">
	استخدام الأداة أبيوم Appium لاختبار تطبيقات الهاتف المحمولة بشكل مؤتمت
</h3>

<p>
	تعرف الأداة أبيوم Appium على أنها إطار عمل مفتوح المصدر وشائع الاستخدام يصلح لأتمتة اختبار <a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D9%84-r1801/" rel="">تطبيقات الجوال</a> الأصيلة (Native Apps) والتطبيقات الهجينة (Hybrid Apps) وتطبيقات الويب على الجوال (Mobile Web Apps).
</p>

<p>
	وتتطلب كتابة السكريبتات الخاصة بالأداة Appium معرفة جيدة بأنظمة تشغيل الهواتف المحمولة وفهم عميق للغات البرمجة مثل لغة <a href="https://academy.hsoub.com/programming/java/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-java-r2275/" rel="">جافا</a> ولغة <a href="https://academy.hsoub.com/programming/cpp/" rel="">++C</a> ولغة <a href="https://academy.hsoub.com/python/" rel="">بايثون</a>.
</p>

<p>
	قد يكون اختبار تطبيقات الجوال بشكل آلي وصيانة سكربتات الاختبارات تحديًا بسبب التغير السريع في البرمجيات وأطر العمل وتحديثات الأجهزة وأنظمة التشغيل.
</p>

<p>
	على سبيل المثال سنستخدم تطبيق الآلة الحاسبة الأساسي، ونقوم بأتمتة عملية الاختبار للحالة التالية باستخدام أبيوم Appium.
</p>

<ol>
	<li>
		نشغل تطبيق الآلة الحاسبة.
	</li>
	<li>
		ننفذ عملية الضرب بين عددين.
	</li>
	<li>
		نسجل نتيجة العملية الحسابية.
	</li>
	<li>
		التأكد من النتيجة.
	</li>
</ol>

<p>
	في هذه الحالة سوف نستخدم الأداة Appium مع لغة البرمجة Java، حيث نشغل الاختبار على الجهاز الافتراضي (المحاكي) وبإمكاننا استخدام جهاز حقيقي لتنفيذ عملية الاختبار، وهذا هو الكود المستخدم
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8473_24" style=""><span class="pln">package appium</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">net</span><span class="pun">.</span><span class="typ">MalformedURLException</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">net</span><span class="pun">.</span><span class="pln">URL</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">openqa</span><span class="pun">.</span><span class="pln">selenium</span><span class="pun">.</span><span class="pln">remote</span><span class="pun">.</span><span class="typ">DesiredCapabilities</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">testng</span><span class="pun">.</span><span class="typ">Assert</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">testng</span><span class="pun">.</span><span class="pln">annotations</span><span class="pun">.</span><span class="typ">Test</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> io</span><span class="pun">.</span><span class="pln">appium</span><span class="pun">.</span><span class="pln">java_client</span><span class="pun">.</span><span class="typ">AppiumBy</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> io</span><span class="pun">.</span><span class="pln">appium</span><span class="pun">.</span><span class="pln">java_client</span><span class="pun">.</span><span class="pln">android</span><span class="pun">.</span><span class="typ">AndroidDriver</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Calculator</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="lit">@Test</span><span class="pln">
  </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> calculator</span><span class="pun">()</span><span class="pln"> throws </span><span class="typ">MalformedURLException</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="typ">DesiredCapabilities</span><span class="pln"> dc </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">DesiredCapabilities</span><span class="pun">();</span><span class="pln">

    </span><span class="com">// Setting the desired capabilities for the android device</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"platformName"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"android"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"platformVersion"</span><span class="pun">,</span><span class="str">"14"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"deviceName"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Pixel6_TestGrid"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"automationName"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"UiAutomator2"</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// Setting capability for the application we want to test</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"app"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"/Users/macair/Downloads/calculator.apk"</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// Instantiating Android Driver and using Appium server host and port</span><span class="pln">
    </span><span class="typ">AndroidDriver</span><span class="pln"> driver </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AndroidDriver</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> URL</span><span class="pun">(</span><span class="str">"http://127.0.0.1:4723/wd/hub"</span><span class="pun">),</span><span class="pln"> dc</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// Locating numbers, mathematical operators, and the equals button in the Calculator app</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">AppiumBy</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"com.google.android.calculator:id/digit_4"</span><span class="pun">)).</span><span class="pln">click</span><span class="pun">();</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">AppiumBy</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"com.google.android.calculator:id/op_mul"</span><span class="pun">)).</span><span class="pln">click</span><span class="pun">();</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">AppiumBy</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"com.google.android.calculator:id/digit_9"</span><span class="pun">)).</span><span class="pln">click</span><span class="pun">();</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">AppiumBy</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"com.google.android.calculator:id/eq"</span><span class="pun">)).</span><span class="pln">click</span><span class="pun">();</span><span class="pln">

    </span><span class="com">// Storing the result in a string variable</span><span class="pln">
    </span><span class="typ">String</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="pln">
      </span><span class="typ">AppiumBy</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"com.google.android.calculator:id/result_final"</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">).</span><span class="pln">getText</span><span class="pun">();</span><span class="pln">
    </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"The result is - "</span><span class="pun">+</span><span class="pln">result</span><span class="pun">);</span><span class="pln">

    </span><span class="typ">Assert</span><span class="pun">.</span><span class="pln">assertEquals</span><span class="pun">(</span><span class="pln">result</span><span class="pun">,</span><span class="pln"> </span><span class="str">"36"</span><span class="pun">);</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">quit</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<h3 id="appiumtestgrid">
	استخدام Appium مع TestGrid لأتمتة اختبار تطبيقات الهواتف المحمولة
</h3>

<p>
	سنرى في هذه الفقرة طريقة تنفيذ سكريبت اختبار الأداة Appium على السحابة الحقيقية للأجهزة
</p>

<p>
	إن طريقة استخدام اختبارات الأداة Appium المحلية الخاصة بتطبيقات الهاتف المحمول تشبه تمامًا ما تعرفنا عليه سابقًا في حالة تطبيقات الويب. يمكنك اتباع التعليمات الموجودة على <a href="https://testgrid.io/docs/document/test-your-local-execution-mobile-appium-code-java-python/" rel="external nofollow">هذا الرابط</a> لتنفيذ كود Appium المحلي على سحابة الأجهزة.
</p>

<p>
	سنلقي نظرة على الكود بعد تحديثه بحيث يتضمن الصنف <code>DesiredCapabilities</code> وهو صنف برمجي يُستخدم لتحديد معاملات معينة عند إعداد بيئة الاختبار، وتستخدم هذه المعاملات لضبط خصائص جهاز TestGrid الذي سيجري عليه الاختبار، على سبيل المثال حددنا هنا المعاملات <code>udid</code> و<code>smartPort</code> و<code>userToken</code> لتحديد الجهاز الذي نريد التواصل معه:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_8473_27" style=""><span class="pln">package appium</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">net</span><span class="pun">.</span><span class="typ">MalformedURLException</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">net</span><span class="pun">.</span><span class="pln">URL</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">openqa</span><span class="pun">.</span><span class="pln">selenium</span><span class="pun">.</span><span class="pln">remote</span><span class="pun">.</span><span class="typ">DesiredCapabilities</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">testng</span><span class="pun">.</span><span class="typ">Assert</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> org</span><span class="pun">.</span><span class="pln">testng</span><span class="pun">.</span><span class="pln">annotations</span><span class="pun">.</span><span class="typ">Test</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> io</span><span class="pun">.</span><span class="pln">appium</span><span class="pun">.</span><span class="pln">java_client</span><span class="pun">.</span><span class="typ">AppiumBy</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> io</span><span class="pun">.</span><span class="pln">appium</span><span class="pun">.</span><span class="pln">java_client</span><span class="pun">.</span><span class="pln">android</span><span class="pun">.</span><span class="typ">AndroidDriver</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CalculatorTestGrid</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="lit">@Test</span><span class="pln">
  </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> calculator</span><span class="pun">()</span><span class="pln"> throws </span><span class="typ">MalformedURLException</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="typ">DesiredCapabilities</span><span class="pln"> dc </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">DesiredCapabilities</span><span class="pun">();</span><span class="pln">

    </span><span class="com">// Setting the desired capabilities for the android device</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"platformName"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"android"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"platformVersion"</span><span class="pun">,</span><span class="str">"12"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"deviceName"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Samsung Galaxy S21"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"automationName"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"UiAutomator2"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"udid"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"R5CRC28KGKB"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"tg:systemPort"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"4005"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"tg:userToken"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Your user token"</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// Since the application is installed on the device, we directly use</span><span class="pln">
    </span><span class="com">// the package name and main activity</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"appPackage"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"com.google.android.calculator"</span><span class="pun">);</span><span class="pln">
    dc</span><span class="pun">.</span><span class="pln">setCapability</span><span class="pun">(</span><span class="str">"appActivity"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"com.android.calculator2.Calculator"</span><span class="pun">);</span><span class="pln">

    </span><span class="typ">AndroidDriver</span><span class="pln"> driver </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AndroidDriver</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> URL</span><span class="pun">(</span><span class="str">"Your TestGrid Appium URL"</span><span class="pun">),</span><span class="pln"> dc</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// Remaining steps are the same as in the previous Appium code</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">AppiumBy</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"com.google.android.calculator:id/digit_4"</span><span class="pun">)).</span><span class="pln">click</span><span class="pun">();</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">AppiumBy</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"com.google.android.calculator:id/op_mul"</span><span class="pun">)).</span><span class="pln">click</span><span class="pun">();</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">AppiumBy</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"com.google.android.calculator:id/digit_9"</span><span class="pun">)).</span><span class="pln">click</span><span class="pun">();</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="typ">AppiumBy</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"com.google.android.calculator:id/eq"</span><span class="pun">)).</span><span class="pln">click</span><span class="pun">();</span><span class="pln">

    </span><span class="typ">String</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> driver</span><span class="pun">.</span><span class="pln">findElement</span><span class="pun">(</span><span class="pln">
      </span><span class="typ">AppiumBy</span><span class="pun">.</span><span class="pln">id</span><span class="pun">(</span><span class="str">"com.google.android.calculator:id/result_final"</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">).</span><span class="pln">getText</span><span class="pun">();</span><span class="pln">
    </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="str">"The result is - "</span><span class="pun">+</span><span class="pln">result</span><span class="pun">);</span><span class="pln">

    </span><span class="typ">Assert</span><span class="pun">.</span><span class="pln">assertEquals</span><span class="pun">(</span><span class="pln">result</span><span class="pun">,</span><span class="pln"> </span><span class="str">"36"</span><span class="pun">);</span><span class="pln">
    driver</span><span class="pun">.</span><span class="pln">quit</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يقوم الكود أعلاه بتشغيل تطبيق الآلة الحاسبة على جهاز Android عبر TestGrid حيث ينفذ عملية ضرب عددين، ثم يتحقق من صحة النتيجة ويعرض النتائج في وحدة التحكم. يجب أن تكون قادرًا على رؤية سجلات التنفيذ في بيئة التطوير المتكاملة الخاصة بك IDE بعد تنفيذ هذا الكود المحدث، كما يمكنك أيضًا رؤية نتائج التنفيذ من خلال الذهاب إلى علامة التبويب <strong>Automaton Sessions</strong> في لوحة التحكم اليسرى واختيار نوع الجهاز الذي تريد تنفيذ الكود عليه.
</p>

<p>
	ستكون قادرًا أيضًا بعد تنفيذ هذا الكود على رؤية حالة كل مرحلة من مراحل التنفيذ مع لقطات الشاشة الخاصة بتلك المرحلة، كما ستكون قادرًا على الاطلاع على السجلات ونتائج الاختبار المرئية لهذه الاختبارات.
</p>

<h2 id="testgrid-1">
	الاستفادة من حلول اختبار الأتمتة الحديثة للأداة TestGrid
</h2>

<p>
	تسمح <a href="https://testgrid.io/codeless-testing" rel="external nofollow">منصة أتمتة الاختبار بدون كود من الأداة TestGrid</a> للمستخدمين الذين لا يملكون خبرات برمجية أو خبراتهم الفنية ضعيفة نوعًا ما بالبدء بأتمتة الاختبارات.
</p>

<p>
	يمكنك باستخدام الأتمتة بدون كود تسجيل تفاعلات المستخدم بشكل مرئي دون الحاجة لكتابة أي كود برمجي، وبعد تسجيل التفاعلات يقوم النظام بتوليد سكربتات اختبار تلقائيًا بناءً على التسجيلات. وبالتالي لا داعي لكتابة كود برمجي يدويًا، تساعد هذه الميزة الرائعة على تقليل تكاليف صيانة السكريبت البرمجي بشكل كبير وأتمتة اختباراتك بشكل أسرع وأسهل، دون الحاجة لمعرفة تفصيلية بالبرمجة.
</p>

<h3 id="testgrid-2">
	استخدام مولد سكريبت الاختبار للأداة TestGrid لاختبار تطبيقات الويب
</h3>

<p>
	لنرى كيف يمكننا أتمتة نفس حالة <a href="https://demoqa.com/login" rel="external nofollow">تسجيل الدخول للموقع التجريبي demoqa</a> الذي اختبرناه في الفقرة السابقة ولكن هذه المرة بدون كود باستخدام منصة اختبار الويب بدون كود التي توفرها أداة TestGrid. اتبع الخطوات كما هو موضح عند إضافة حالة اختبار باستخدام مولد حالات الاختبار. وأدخل عنوان URL واختر متصفحًا وصفحة الويب، ثم أضف الإجراءات أو التفاعلات التي يمكن القيام بها على العناصر المختلفة للصفحة حسب المطلوب.
</p>

<p>
	كما هو موضح في الصورة أدناه، عند النقر على عنصر الويب المطلوب التفاعل معه يُكتشَف موقع هذا العنصر تلقائيًا، ستلاحظ أيضًا تحديد مواقع عناصر مختلفة ويمكنك اختيار ما تريده. كما أن لديك خيار كتابة موقع العنصر يدويًا، في حال لم يتمكن النظام من الكشف عن موقع العنصر المطلوب.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="156413" href="https://academy.hsoub.com/uploads/monthly_2024_08/1.png.707d8893196dde54115b585892e1759a.png" rel=""><img alt="1" class="ipsImage ipsImage_thumbnailed" data-fileid="156413" data-unique="jblmz53ft" src="https://academy.hsoub.com/uploads/monthly_2024_08/1.png.707d8893196dde54115b585892e1759a.png"> </a>
</p>

<p>
	يمكنك بعد الحصول على بيانات عناصر الاختبار المطلوبة إضافة الإجراءات المتعلقة بعملية تسجيل الدخول مع التفاصيل الضرورية لتوليد دالة الاختبار لحالة الاختبار الخاصة بك، تستطيع بعد ذلك تشغيل حالة الاختبار.
</p>

<p>
	يمكنك مع تقدم التنفيذ البدء بمراقبة السجلات وحالة الاختبار التي تم توليدها.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="156414" href="https://academy.hsoub.com/uploads/monthly_2024_08/2.png.70347055c37d740265121275d2c65898.png" rel=""><img alt="2" class="ipsImage ipsImage_thumbnailed" data-fileid="156414" data-unique="pk2dk58wc" src="https://academy.hsoub.com/uploads/monthly_2024_08/2.png.70347055c37d740265121275d2c65898.png"> </a>
</p>

<p>
	اضغط على الأمر <strong>Build Summary</strong> في لوحة التحكم اليسرى لعرض التفاصيل مثل عدد الخطوات أو المراحل الناجحة والفاشلة ولقطات شاشة لخطوات التنفيذ وزمن البداية والنهاية وحالات الاختبار
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="156415" href="https://academy.hsoub.com/uploads/monthly_2024_08/3.png.7462103591b87825f80701c34ce60842.png" rel=""><img alt="3" class="ipsImage ipsImage_thumbnailed" data-fileid="156415" data-unique="5jffv3ukb" src="https://academy.hsoub.com/uploads/monthly_2024_08/3.thumb.png.10df9628eabc6dd9b3d757823f8f27e2.png"> </a>
</p>

<p>
	يمكنك بعد انتهاء عملية التنفيذ الوصول تقارير التحليل المختلفة والموجودة على التبويب <strong>Transaction Analysis</strong> في لوحة التحكم اليسرى.
</p>

<p>
	توجد ميزة أخرى يقدمها لنا مولد الاختبار بدون كود للأداة TestGrid وهي ميزة التسجيل والتشغيل، التي تسمح بتحويل إجراءات التطبيق أو المتصفح إلى اختبارات مؤتمتة وقوية.
</p>

<p>
	يمكنك بعد إنشاء المشروع والوحدة النقر على الأمر <strong>Add Test Case with Scriptless</strong> لاستخدام هذه الميزة، ستلاحظ وجود الزر <strong>Start Recording</strong> في لوحة التحكم اليمنى الذي يقوم بعد النقر عليه بتسجيل الإجراءات كما يمكن الضغط على الزر <strong>Stop</strong> عند الانتهاء.
</p>

<p>
	نلاحظ قفل الأمرين <strong>Live View</strong> و<strong>Element Picker</strong> خلال عملية التنفيذ
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="156416" href="https://academy.hsoub.com/uploads/monthly_2024_08/4.png.29beab77955173d9299959a7dedf01b7.png" rel=""><img alt="4" class="ipsImage ipsImage_thumbnailed" data-fileid="156416" data-unique="td961v3ha" src="https://academy.hsoub.com/uploads/monthly_2024_08/4.thumb.png.ac870dbcadec0ce35bfe3db527cb8570.png"> </a>
</p>

<p>
	يمكننا بعد إيقاف التسجيل تعديل المعاملات في العمود <strong>Action</strong> أو على الأمر <strong>Element Picker</strong> لإجراء المزيد من التعديلات على الخطوات المضافة باستخدام التسجيل.
</p>

<p>
	يمكننا بعد ذلك الانتقال مرة أخرى إلى الأمر <strong>Test Cases</strong> وتنفيذ الاختبار كما نفذناه المرة السابقة تمامًا، سنكون قادرين على مشاهدة التنفيذ والنتائج في تقارير التحليل المختلفة، نستطيع باستخدام الأمر <strong>Network Log</strong> عرض الطلبات requests المرسلة إلى النطاق domain والوقت الذي يستغرقه كل منها
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="156417" href="https://academy.hsoub.com/uploads/monthly_2024_08/5.png.94d8e1f398b38a41a9db69901cd62c78.png" rel=""><img alt="5" class="ipsImage ipsImage_thumbnailed" data-fileid="156417" data-unique="dczl67mvu" src="https://academy.hsoub.com/uploads/monthly_2024_08/5.thumb.png.6e17bfd17d8bd80e96b48e5f53ef434f.png"> </a>
</p>

<p>
	سكريبتات مخصصة عند الحاجة أو الطلب.
</p>

<p>
	وضحنا في هذا القسم كيفية كتابة اختبارات أتمتة بدون كود بسهولة لحالات الاستخدام الخاصة بك، وشرحنا طريقة إضافة سكربتات مخصصة عند الطلب. إذ يمكن لمولد سكربتات الاختبار في منصة TestGrid أن يساعدك في زيادة سرعة وموثوقية اختبارات الأتمتة لتطبيقات الويب الحديثة.
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="351" id="ips_uid_5323_6" referrerpolicy="strict-origin-when-cross-origin" src="https://academy.hsoub.com/applications/core/interface/index.html" title="ما هي لغات البرمجة وأطر العمل المستخدمة في تطوير تطبيقات الويب؟" width="633" data-embed-src="https://www.youtube.com/embed/E5jntujlfG4"></iframe>
</p>

<h3 id="testgrid-3">
	استخدام مولد سكريبت الاختبار في الأداة TestGrid لاختبار تطبيقات الهاتف المحمول
</h3>

<p>
	تعرفنا في الفقرة السابقة على طريقة اختبار تطبيقات الويب ويمكننا بطريقة مشابهة اختبار تطبيقات الهاتف المحمول وذلك باستخدام ميزة <a href="https://testgrid.io/docs/scriptless-mobile-app-test-case-writing/" rel="external nofollow">كتابة حالات اختبار تطبيقات الجوال دون كود</a> التي توفرها في الأداة TestGrid، حيث يمكنك باستخدام هذه الميزة أتمتة تطبيقات الهاتف المحمول بسرعة دون الحاجة إلى إعداد الخادم والعملاء والأجهزة والإعدادات الأخرى يدويًا المطلوبة لتشغيل الاختبارات التلقائية.
</p>

<p>
	سنشاهد عند الضغط على الزر <strong>Add Test Function with TestCase Generator</strong> نافذة منبثقة تطلب منك إدخال معرف APK/IPA/BUNDLE ID التطبيق الذي تريد اختباره، حمل التطبيق ثم اختر الجهاز الذي سينفذ عليه الاختبار ثم اضغط على الزر <strong>Run</strong> لتشغيله.
</p>

<p>
	الخطوات التالية مشابهة تمامًا للخطوات التي طبقناها في حالة تطبيقات الويب
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="156418" href="https://academy.hsoub.com/uploads/monthly_2024_08/6.png.b2dc90ebf967385f8b8e5c2be4cc02c2.png" rel=""><img alt="6.png" class="ipsImage ipsImage_thumbnailed" data-fileid="156418" data-ratio="86.58" data-unique="4oqbu2f4m" width="693" src="https://academy.hsoub.com/uploads/monthly_2024_08/6.thumb.png.114629814b9c6002c40130a6e50ffc60.png"></a>
</p>

<p>
	بعد حفظ خطوات تطبيق عملية الضرب على رقمين والتحقق من النتيجة تضاف دالة الاختبار.
</p>

<p>
	لاحظ أنه يمكننا كتابة حالة الاختبار مباشرة دون الحاجة إلى كتابة دالة الاختبار كما فعلنا سابقًا، كما يمكننا إنشاء أو كتابة دوال اختبار للخطوات التي تتكرر في حالات الاختبار، والتي يمكن إعادة استخدامها كدوال مشتركة
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="156419" href="https://academy.hsoub.com/uploads/monthly_2024_08/7.png.e81e1676ce05e3482005e97ca33ab973.png" rel=""><img alt="7.png" class="ipsImage ipsImage_thumbnailed" data-fileid="156419" data-ratio="60.44" data-unique="uxeqhz8kq" width="900" src="https://academy.hsoub.com/uploads/monthly_2024_08/7.thumb.png.5544a5608e4b996c1ae2d6051c2c6c2b.png"></a>
</p>

<p>
	بعد الضغط على الزر <strong>Run</strong> لتشغيل حالات الاختبار، نختار الإصدار والجهاز الذي نريد تنفيذ الاختبارات عليه، يمكننا بعد الدمج والتجميع رؤية عملية التنفيذ في الوقت الحقيقي بالإضافة إلى السجلات المولدة.
</p>

<p>
	نستطيع من خلال الأمر <strong>Build Summary</strong> معرفة كافة تفاصيل عملية التنفيذ.
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="156420" href="https://academy.hsoub.com/uploads/monthly_2024_08/8.png.d21b7d5ccc9d5dfe88bcb3e25b193ad9.png" rel=""><img alt="8.png" class="ipsImage ipsImage_thumbnailed" data-fileid="156420" data-ratio="56.22" data-unique="l3q56kmka" width="900" src="https://academy.hsoub.com/uploads/monthly_2024_08/8.thumb.png.bf63e62e6b9ffa0b5357e951255a16d0.png"></a>
</p>

<p>
	يمكننا استخدام ميزة التسجيل والتشغيل في تطبيقات الهاتف المحمول بطريقة مشابهة تمامًا لتطبيقات الويب.
</p>

<p>
	كما يمكننا الاستفادة بقوة من السجلات والتقارير المتعددة التي توفرها لنا الأداة TestGrid، والتي تجعل تحليل أداء عمليات التنفيذ عملية سهلة وبسيطة للغاية.
</p>

<p>
	تُظهر الميزة Insights رسوم بيانية لمعاملات الأداء المختلفة المتوافقة مع عملية التنفيذ مثل التحميل على وحدة المعالجة المركزية (CPU) خلال الاختبارات. ومقدار استخدام الذاكرة خلال تشغيل التطبيق وحجم بيانات الاستجابة والطلب وغيرها الكثير.
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="156421" href="https://academy.hsoub.com/uploads/monthly_2024_08/9.png.0b5e7c1e04638ad36f8a9061195747be.png" rel=""><img alt="9.png" class="ipsImage ipsImage_thumbnailed" data-fileid="156421" data-ratio="54.11" data-unique="7xovj8tgo" width="900" src="https://academy.hsoub.com/uploads/monthly_2024_08/9.thumb.png.47fabed4f225437639b34244ee6b5bf5.png"></a>
</p>

<p>
	بالإضافة إلى ذلك يوفر الأمر <strong>Transaction Analysis</strong> العديد من التقارير القابلة للتحميل، والتي تتعلق بمعايير الأداء بما في ذلك معلومات الجهاز.
</p>

<p>
	وضحنا في هذا القسم طريقة الاستفادة القصوى من التحليلات والتقارير التفصيلية وسهولة التنفيذ لزيادة نطاق الأتمتة الخاصة بك، حيث يمكننا باستخدام سحابة الأجهزة الحقيقية للأداة TestGrid توسيع نطاق الاختبارات لتشمل عددًا كبيرًا من الأجهزة.
</p>

<p>
	وسواء كنت تمتلك أطر اختبار موجودة مسبقًا مثل مثل Selenium أو Appium دمجتها مع TestGrid واستخدمتها على سحابة الأجهزة الحقيقية أو بدأت من الصفر، فإن TestGrid يمكّنك من متابعة عملك من آخر نقطة وصلت إليها.
</p>

<h3 id="testgrid-4">
	استخدام ميزة الاختبار المرئي للأداة TestGrid
</h3>

<p>
	يتحقق الاختبار المرئي Visual testing من كون واجهات المستخدم في التطبيقات تظهر كما هو متوقع حيث يتجاوز هذا النوع من الاختبارات التحقق الوظيفي (أي التحقق من أن العناصر تعمل بشكل صحيح) ويركز على التأكد من أن التصميم المرئي والمظهر العام للتطبيق يتطابق مع التصميم المتوقع والمخطط له ويسمح لنا باكتشاف التغييرات غير المرغوب فيها في التصميم أو المشكلات البصرية التي قد تحدث أثناء عملية تطوير التطبيق بمنهجية التطوير السريعة أجايل <a href="https://academy.hsoub.com/entrepreneurship/business/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-%D9%84%D9%85%D9%86%D9%87%D8%AC%D9%8A%D8%A9-%D8%A3%D8%AC%D8%A7%D9%8A%D9%84-agile-r1047/" rel="">agile</a>.
</p>

<p>
	تساعد الأداة TestGrid في جعل <a href="https://testgrid.io/visual-testing" rel="external nofollow">الاختبار المرئي</a> قابلًا للقياس من خلال الأتمتة الذكية للاختبار، التي تسمح للمبرمجين بالتقاط لقطات للشاشة بشكل تلقائي عبر جميع الأطر أو النماذج وهي تستخدم أيضًا المقارنة بين الصور معتمدة بذلك على الذكاء الاصطناعي للعثور على الاختلافات بين هذه الصور.
</p>

<p>
	تساعد الخرائط الحرارية العميقة بالإضافة إلى لوحة تحكم التحليلات فرق الاختبار على أتمتة عمليات التحقق المرئي في تطبيقات الويب والهاتف المحمول، وتساعد هذه الميزة فرق الاختبار بالوصول إلى تجربة رائعة للمستخدم عن طريق التصميم المنظم والمنسق.
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="156422" href="https://academy.hsoub.com/uploads/monthly_2024_08/10.png.a3cd1fea44bc83f9336ce7ffb21704c4.png" rel=""><img alt="10.png" class="ipsImage ipsImage_thumbnailed" data-fileid="156422" data-ratio="66.44" data-unique="r0iophz9o" width="900" src="https://academy.hsoub.com/uploads/monthly_2024_08/10.thumb.png.c58e7990b88fa2de576623284bcc864e.png"></a>
</p>

<p>
	يمكننا بعد اتباع <a href="https://testgrid.io/docs/document/visual-testing/" rel="external nofollow">خطوات إعداد الاختبار المرئي</a> في كود الأداة Selenium أو الأداة Appium مشاهدة نتائج الاختبار المرئي عن طريق الانتقال إلى التبويب <strong>Visual Testing</strong> الموجود ضمن النافذة <strong>Automation Sessions</strong> للجهاز المختار.
</p>

<p>
	يلعب الاختبار المرئي دورًا أساسيًا في ضمان جودة التطبيقات بشكل عام سواء من حيث الوظائف أو من حيث الشكل والمظهر فعندما تختبر واجهة المستخدم بدقة وتهتم بالتفاصيل، فهذا يحسن من رضا المستخدمين ويزيد من احتمالية احتفاظهم بالتطبيق وتُسهّل عليك منصة TestGrid دمج الاختبارات المرئية في الاختبارات الحالية بشكل سهل وبسيط. وتساعدك في توسيع نطاق التحقق من جودة التطبيق الذي تختبره مما يضمن لك أن التطبيق يعمل كما هو متوقع في جميع جوانبه.
</p>

<h2 id="-1">
	الخلاصة
</h2>

<p>
	يتوجب علينا في ظل تسارع عملية تطوير البرامج أن نتبع طرق أكثر حداثة لاختبار تلك البرامج حيث تتطلب التطبيقات الحديثة معايير عالية الجودة للاختبار وهو ما تفتقر إليه طرق أتمتة الاختبار التقليدية.
</p>

<p>
	تعد كتابة سكريبتات الاختبار في الأداتين Selenium و Appium أمرًا معقدًا ومكلفًا ويستغرق الكثير من الوقت مما يجعل التعديلات المتكررة لهذه السكريبتات أمرًا في غاية الصعوبة وغير موثوق دومًا.
</p>

<p>
	لذا يمكن الاعتماد على الأدوات المؤتمتة مثل TestGrid التي تحسن الطرق التقليدية للاختبار وتوفر ميزات حديثة مثل الأتمتة بدون كود برمجي والتقارير القوية والتحليلات المتكاملة، فيما تساعد سكريبتات الاختبار باستخدام تقنيات الذكاء الاصطناعي على الوصول إلى نطاق أوسع من الأجهزة بالإضافة إلى تحقيق الثبات والاستقرار.
</p>

<p>
	وبهذه الطريقة يستطيع المطورون ومهندسو ضمان الجودة (QA Engineers) تحقيق اختبارات نموذجية عن طريق الأجهزة والمتصفحات وأطر العمل وتصور مشكلات <a href="https://academy.hsoub.com/design/user-interface/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-ui-%D8%A7%D9%84%D8%AC%D9%8A%D8%AF%D8%A9-r804/" rel="">واجهة المستخدم UI </a>قبل حدوثها.
</p>

<p>
	ترجمة وبتصرف للمقال <a href="https://developer.mozilla.org/en-US/blog/modernizing-test-automation-with-test-grid/" rel="external nofollow">Modernizing conventional test automation with TestGrid</a> لكاتبه TestGrid.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%84%D9%84%D8%AA%D9%88%D8%A7%D9%81%D9%82-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1953/" rel="">مدخل إلى اختبار مشاريع الويب للتوافق مع المتصفحات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/deployment/ansible/%D8%A3%D8%AA%D9%85%D8%AA%D8%A9-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D8%AF%D8%A7%D8%A9-ansible-r688/" rel="">أتمتة إعداد خادم باستخدام أداة Ansible</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A9-%D9%84%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A2%D9%84%D9%8A%D8%A9-%D9%81%D9%8A-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%84%D9%84%D8%AA%D9%88%D8%A7%D9%81%D9%82-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1990/" rel="">إعداد البيئة للاختبارات الآلية في مشاريع الويب للتوافق مع المتصفحات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%B9%D8%A7%D9%84%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D9%85%D8%B9%D8%A7%D9%8A%D9%8A%D8%B1%D9%87-r1670/" rel="">عالم الويب ومعاييره</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2390</guid><pubDate>Thu, 22 Aug 2024 15:04:00 +0000</pubDate></item><item><title>&#x62A;&#x631;&#x645;&#x64A;&#x632; Big O &#x648;&#x62D;&#x633;&#x627;&#x628; &#x645;&#x631;&#x627;&#x62A;&#x628; &#x62A;&#x639;&#x642;&#x64A;&#x62F; &#x627;&#x644;&#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x627;&#x62A;</title><link>https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-big-o-%D9%88%D8%AD%D8%B3%D8%A7%D8%A8-%D9%85%D8%B1%D8%A7%D8%AA%D8%A8-%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r2128/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_09/-Big-O-----.png.73ffb0dc8925bf551d5ff29cc3396e10.png" /></p>
<p>
	بعد أن تعلمنا كيفية قياس سرعة البرامج في المقال السابق <a href="https://academy.hsoub.com/programming/python/%D9%82%D9%8A%D8%A7%D8%B3-%D8%A3%D8%AF%D8%A7%D8%A1-%D9%88%D8%B3%D8%B1%D8%B9%D8%A9-%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2127/" rel="">قياس أداء وسرعة تنفيذ شيفرة بايثون</a>، سنتعلم كيفية قياس الزيادات النظرية theoretical increases في وقت التنفيذ runtime مع نمو حجم البيانات الخاصة بالبرنامج، ويُطلق على ذلك في علوم الحاسوب <a href="https://academy.hsoub.com/programming/advanced/%D8%AF%D9%84%D9%8A%D9%84-%D8%B4%D8%A7%D9%85%D9%84-%D8%B9%D9%86-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A9-r1247/" rel="">ترميز O الكبير big O notation</a>.
</p>

<p>
	ربما يشعر مطورو البرامج الذين ليس لديهم خلفية في <a href="https://academy.hsoub.com/programming/general/%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/" rel="">علوم الحاسوب</a> التقليدية بوجود نقص في معارفهم، على الرغم من كون المعرفة في علوم الحاسوب مفيدة، لكنه ليس مرتبط مباشرةً مع <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA/" rel="">تطوير البرمجيات</a>.
</p>

<p>
	تُعدّ <a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-big-o-%D9%81%D9%8A-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1290/" rel="">Big O</a> نوعًا من خوارزميات التحليل التي تصف تعقيد الشيفرة مع زيادة عدد العناصر التي تعمل عليها. تصنف الشيفرة في مرتبة تصف عمومًا الوقت الذي تستغرقه الشيفرة لتُنفّذ، وكلما زادت تزيد كمية العمل الواجب إنجازه. يصف مطور <a href="https://academy.hsoub.com/python/" rel="">لغة بايثون Python</a> نيد باتشلدر Ned Batchelder خوارزمية big O بكونها تحليلًا لكيفية "تباطؤ الشيفرة كلما زادت البيانات" وهو عنوان حديثه في معرض PyCon 2018.
</p>

<p>
	لننظر إلى المثال التالي: لنفترض أنه لديك كميةٌ معينةٌ من العمل الذي يستغرق ساعةً ليكتمل، فإذا تضاعفت كمية العمل، كم سيستغرق من الوقت؟ ربما تعتقد أنه سيستغرق ضعف المدة ولكن الجواب الصحيح هو: يعتمد الأمر على نوع العمل المُنجز.
</p>

<p>
	إذا استغرقت ساعةً لقراءة كتاب قصير، فستستغرق أكثر أو أقل من ساعتين لقراءة كتابين قصيرين، وإذا كان بإمكانك ترتيب 500 كتاب أبجديًا، سيستغرق ترتيب 1000 كتاب أكثر من ساعتين لأنك ستبحث عن المكان المناسب لكل كتاب في مجموعة أكبر من الكتب. من ناحية أخرى، إذا أردت التأكد أن رف الكتب فارغ أو لا، لا يهم إذا كان هناك 0 أو 10 أو 1000 كتاب على الرف، فنظرةٌ واحدةٌ كافية لتعرف الجواب، إذ سيبقى وقت التنفيذ على حاله بغض النظر عن عدد الكتب الموجودة. يمكن أن يكون بعض الناس أسرع أو أبطأ في قراءة أو ترتيب الكتب أبجديًا ولكن تبقى هذه التوجهات العامة نفسها.
</p>

<p>
	تصف خوارزمية big O هذه التوجهات، إذ يمكن أن تُنفذ الخوارزمية على حاسوب سريع أو بطيء ولكننا نستطيع استخدام big O لوصف كيفية عمل الخوارزمية عمومًا، بغض النظر عن العتاد الذي ينفذ هذه الخوارزمية. لا تستخدم big O وحدات معينة مثل الثواني أو دورات المعالج لوصف وقت تنفيذ الخوارزمية لأنها تختلف بين أجهزة الحاسوب ولغات البرمجة.
</p>

<h2>
	مراتب Big O
</h2>

<p>
	يُعرّف ترميز Big O عادةً المراتب التالية، التي تتراوح من المنخفضة -التي تصف الشيفرة التي لا تتباطأ كثيرًا كلما زادت البيانات- إلى المراتب العليا -التي تصف الشيفرة الأكثر تباطؤًا:
</p>

<ol>
	<li>
		O(1)‎ وقت ثابت (أدنى مرتبة)
	</li>
	<li>
		O(log n)‎ وقت لوغاريتمي
	</li>
	<li>
		O(n)‎ وقت خطي
	</li>
	<li>
		O(n log n)‎ وقت N-Log-N
	</li>
	<li>
		O(n<sup>2</sup>)‎ وقت متعدد الحدود
	</li>
	<li>
		O(2<sup>n</sup>)‎ وقت أسي
	</li>
	<li>
		O(n!)‎ وقت عاملي (أعلى مرتبة)
	</li>
</ol>

<p>
	لاحظ أن big O تستخدم حرف O كبير متبوعًا بقوسين يحتويان وصف المرتبة، إذ يمثّل حرف O الكبير المرتبة وتمثل n حجم دخل البيانات التي تعمل عليها الشيفرة. نلفظها "big oh of n" أو "big oh n".
</p>

<p>
	لا تحتاج لفهم المعنى الدقيق لكلمات مثل لوغاريتمي أو حدودي لاستخدام صيغة big O، إذ سنصِف كل نوع بالتفصيل في الفقرة التالية ولكن هذا تبسيط لهم:
</p>

<ul>
	<li>
		خوارزميات O(1)‎ و O(log n)‎ سريعة
	</li>
	<li>
		خوارزميات O(n)‎ و O(n log n)‎ ليست سيئة
	</li>
	<li>
		خوارزميات O(n<sup>2</sup>)‎ و O(2<sup>n</sup>)‎ بطيئة يمكن طبعًا مناقشة العكس في بعض الحالات ولكن هذه التوصيفات هي قواعد جيدة عمومًا، فهناك مراتب أكثر من big O المذكورة هنا ولكن هذه هي الأعم. دعنا نتحدث عن أنواع المهام التي يصفها كل نوع من هذه المهام.
	</li>
</ul>

<h2>
	اصطلاح رف الكتب Bookshelf لمراتب Big O
</h2>

<p>
	سنستمر في المثال التالي لمراتب big O باستخدام مصطلح رف الكتب، إذ تمثّل n عدد الكتب في الرف ويصف ترميز Big O كيف أن المهام المختلفة تستغرق وقتًا أطول كلما زاد عدد الكتب.
</p>

<h3>
	تعقيد O(1)‎: وقت ثابت
</h3>

<p>
	معرفة "هل رف الكتب فارغ؟" هي عملية ذات وقت ثابت، إذ لا يهم عدد الكتب في الرف، فنظرةٌ واحدةٌ ستخبرنا ما إذا كان رف الكتب فارغ أم لا. يمكن أن يختلف عدد الكتب ولكن وقت التنفيذ يبقى ثابتًا، لأنه طالما رأينا كتابًا واحدًا على الرف يمكننا إيقاف البحث. القيمة n غير مهمة لسرعة تنفيذ المهمة لذا لا يوجد n في O(1)‎. يمكنك أيضًا رؤية الوقت الثابت مكتوبًا (O(c أحيانًا ‎.
</p>

<h3>
	تعقيد O(log n)‎: لوغاريتمي
</h3>

<p>
	اللوغاريتم هو عكس الأس، الأس 2<sup>4</sup> أو 2×2×2×2 يساوي 16 ولكن اللوغاريتم log<sub>2</sub>(16)‎ (تلفظ لوغاريتم أساس 2 للعدد 16) يساوي 4. نفترض في البرمجة قيمة الأساس 2 ولذلك نكتب O(log n)‎ بدلًا من O(log<sub>2</sub> n)‎.
</p>

<p>
	البحث عن كتاب في رف كتب مرتب أبجديًا هي عملية لوغاريتمية الوقت؛ فمن أجل إيجاد كتاب واحد، يمكن التحقق من الكتاب في منتصف الرف، وإذا كان هو الكتاب المطلوب تكون قد انتهيت، وإذا لم يكن كذلك، يمكن تحديد إذا كان الكتاب قبل أو بعد الكتاب الذي في المنتصف. يقلل ذلك مجال البحث إلى النصف، ويمكنك تكرار هذه العملية مجددًا من خلال فحص الكتاب الذي في منتصف النصف الذي تتوقع فيه إيجاد الكتاب. نسمي ذلك خوارزمية بحث ثنائية binary search وهناك مثال على ذلك في "أمثلة عن ترميز Big O" لاحقًا.
</p>

<p>
	عدد المرات التي تستطيع قسم مجموعة n كتاب للنصف هي log<sub>2</sub> n، في رف فيه 16 كتاب ستحتاج إلى 4 خطوات على الأكثر لإيجاد الكتاب الصحيح، لأن كل خطوة تقلل عدد الكتب التي يجب البحث فيها إلى النصف. يحتاج رف الكتب الذي فيه ضعف عدد الكتب فقط إلى خطوة إضافية واحدة للبحث عنه. إذا كان هناك 4.2 مليار كتاب في رف كتب مرتبة أبجديًا ستحتاج فقط إلى 32 خطوة لإيجاد كتاب معين.
</p>

<p>
	تتضمن خوارزميات Log n عادةً خطوة فرّق تسد divide and conquer وتنطوي على اختيار نصف دخل n للعمل عليه وبعدها نصف آخر من ذلك النصف وهكذا دواليك. تتدرج عمليات log n جيدًا، إذ يمكن أن يزداد العمل n إلى الضعف ولكن سيزداد وقت التنفيذ خطوةً واحدةً فقط.
</p>

<h3>
	تعقيد O(n)‎: وقت خطي
</h3>

<p>
	تستغرق عملية قراءة كل الكتب على الرف وقتًا خطيًا؛ فإذا كانت الكتب بنفس الطول تقريبًا وضاعفت عدد الكتب على الرف، فسيستغرق ضعف الوقت تقريبًا لقراءة كل الكتب، ويزداد وقت التنفيذ بالتناسب مع عدد الكتب n.
</p>

<h3>
	تعقيد O(n log n)‎: وقت N-Log-N
</h3>

<p>
	ترتيب الكتب أبجديًا هي عملية تستغرق وقت n-log-n. هذه المرتبة هي ناتج ضرب وقتي التنفيذ O(n)‎ و O(log n)‎ ببعضهما. يمكن القول أن مهمة O(n log n)‎ هي مهمة O(log n)‎ مع تنفيذها n مرة. فيما يلي تفسير بسيط حول ذلك.
</p>

<p>
	ابدأ بمجموعة كتب يجب ترتيبها أبجديًا ورف كتب فارغ، واتبع الخطوات في خوارزمية البحث الثنائي كما موضح في الفقرة السابقة "تعقيد O(log n)‎: زمن لوغاريتمي" لمعرفة مكان كل كتاب على الرف. هذه عملية O(log n)‎، ومن أجل ترتيب n كتاب أبجديًا وكان كل كتاب بحاجة إلى log n خطوة لترتيبه أبجديًا، ستحتاج n×log n أو n log n خطوة لترتيب كل مجموعة الكتب أبجديًا. إذا تضاعف عدد الكتب سيستغرق أكثر من ضعف الوقت لترتيبهم أبجديًا، لذا تتدرج خوارزميات n log n جيدًا.
</p>

<p>
	خوارزميات الترتيب ذات الكفاءة، هي: O(n log n)‎، مثل ترتيب الدمج merge sort والترتيب السريع quicksort وترتيب الكومة heapsort وترتيب تيم Timsort (من اختراع تيم بيترز Tim Peters وهي الخوارزمية التي يستخدمها تابع <code>sort()‎</code> الخاص ببايثون).
</p>

<h3>
	تعقيد O(n<sup>2</sup>)‎: وقت متعدد الحدود
</h3>

<p>
	التحقق من الكتب المكررة في رف كتب غير مرتب هي عملية تستغرق وقت حدودي polynomial time operation؛ فإذا كان هناك 100 كتاب يمكنك أن تبدأ بالكتاب الأول وتقارنه مع التسعة وتسعون الكتاب الباقين لمعرفة التشابه، ثم نأخذ ثاني كتاب ونتحقق بنفس الطريقة مثل باقي بقية الكتب التسعة وتسعون. خطوات التحقق من التكرار لكتاب واحد هي 99 (سنقرّب هذا الرقم إلى 100 الذي هو n في هذا المثال). يجب علينا فعل ذلك 100 مرة، مرةً لكل كتاب، لذا عدد خطوات التحقق لكل كتاب على الرف هو تقريبًا n×n أو n<sup>2</sup>. (يبقى هذا التقريب n<sup>2</sup> صالحًا حتى لو كنا أذكياء ولم نكرر المقارنات).
</p>

<p>
	يزداد وقت التنفيذ بازدياد عدد الكتب تربيعيًا. سيأخذ التحقق من التكرار لمئة كتاب 100×100 أو 10,000 خطوة، ولكن التحقق من ضعف هذه القيمة أي 200 كتاب سيكون 200×200 أو 40,000 خطوة، أي أربع مرات عمل أكثر.
</p>

<p>
	وجد بعض الخبراء في كتابة الشيفرات الواقعية للعالم الحقيقي أن معظم استخدامات تحليل big O هي لتفادي كتابة خوارزمية O(n<sup>2</sup>)‎ عن طريق الخطأ، عند وجود خوارزمية O(n log n)‎ أو O(n)‎.
</p>

<p>
	مرتبة O(n<sup>2</sup>)‎ هي عندما تبدأ الخوارزميات بالتباطؤ كثيرًا، لذا معرفة أن الشيفرة الخاصة بك في مرتبة O(n<sup>2</sup>)‎ أو أعلى يجب أن يجعلك تتوقف. ربما توجد خوارزمية مختلفة يمكنها حل المشكلة بصورةٍ أسرع، ويمكن في هذه الحالة أن يكون الإطلاع على قسم <a href="https://wiki.hsoub.com/Algorithms" rel="external">هيكلة البيانات والخوارزميات Data Structure and Algorithms</a> -أو اختصارًا DSA- إما على أكاديمية حسوب مفيدًا.
</p>

<p>
	نسمي أيضًا O(n<sup>2</sup>)‎ وقتًا تربيعيًا، ويمكن أن يكون لخوارزميات O(n<sup>3</sup>) وقتًا تكعيبيًا وهو أبطأ من O(n<sup>2</sup>)‎ أو وقتًا رباعيًا O(n<sup>4</sup>)‎ الذي هو أبطأ من O(n<sup>3</sup>)‎ أو غيره من الأوقات الزمنية متعددة الحدود.
</p>

<h3>
	تعقيد O(2<sup>n</sup>)<span><span><span>:</span> </span></span>وقت أسي
</h3>

<p>
	أخذ صور لرف الكتب مع كل مجموعة ممكنة من الكتب هو عملية تستغرق وقتًا أُسّيًا. انظر لهذا الأمر بهذه الطريقة، كل كتاب على الرف يمكن أن يكون في الصورة أو لا يكون. يبين الشكل 1 كل مجموعة ممكنة، إذ تكون n هي 1 أو 2 أو 3. إذا كانت n هي 1 هناك طريقين للتجميع، إذا كانت n هي 2 هناك أربعة صور ممكنة، الكتابان على الرف، أو الكتابان ليسا على الرف، أو الكتاب الأول موجود والثاني ليس موجودًا، أو الكتاب الأول ليس موجودًا والثاني موجود. إذا أضفنا كتابًا ثالثًا، نكون قد ضاعفنا مرةً ثانية العمل المُراد فعله، لذا يجب عليك النظر إلى كل مجموعة فرعية لكتابين التي تضم الكتاب الثالث (أربعة صور) وكل مجموعة فرعية لكتابين دون الكتاب الثالث (أربعة صور أُخرى أي 2<sup>3</sup> أو 8 صور). يضاعف كل كتاب إضافي كمية العمل، فمن أجل n كتاب سيكون عدد الصور التي يجب أخذها (أي العمل الواجب فعله) هو 2<sup>n</sup>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="135551" href="https://academy.hsoub.com/uploads/monthly_2023_09/book-shelf-combinations.png.772e68bae1a4cfdce612b54b0639c866.png" rel=""><img alt="book-shelf-combinations.png" class="ipsImage ipsImage_thumbnailed" data-fileid="135551" data-unique="xp9ek4hp3" src="https://academy.hsoub.com/uploads/monthly_2023_09/book-shelf-combinations.png.772e68bae1a4cfdce612b54b0639c866.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 1: مجموعة الكتب الممكنة على رف كتب من أجل كتاب واحد أو اثنين أو ثلاث كتب]
</p>

<p>
	يزداد وقت التنفيذ للمهام الأُسية بسرعة كبيرة. تحتاج ستة كتب إلى 2<sup>6</sup> أو 32 صورة، ولكن 32 كتاب يحتاج 2<sup>32</sup> أو أكثر من 4.2 مليار صورة. مرتبة O(2<sup>2</sup>) أو O(2<sup>3</sup>)‎ أو O(2<sup>4</sup>) وما بعدها هي مراتب مختلفة ولكنها كلها تحتوي تعقيدات وقت أُسّي.
</p>

<h3>
	تعقيد O(n!)‎: وقت عاملي
</h3>

<p>
	أخذ صورةٍ لكل ترتيب معين هي عملية تستغرق وقتًا عاملي. نطلق على كل ترتيب ممكن اسم التبديل permutation من أجل n كتاب. النتيجة هي ترتيب n!‎ أو n عاملي، فمثلًا 3‎!‎‎ هي 3×2×1 أو 6. يبين الشكل 2 كل التبديلات الممكنة لثلاثة كتب.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="135550" href="https://academy.hsoub.com/uploads/monthly_2023_09/bookshelf-permutations.png.ccf5fd543dd82e88eda611c5122e6757.png" rel=""><img alt="bookshelf-permutations.png" class="ipsImage ipsImage_thumbnailed" data-fileid="135550" data-unique="r12ieogkk" src="https://academy.hsoub.com/uploads/monthly_2023_09/bookshelf-permutations.png.ccf5fd543dd82e88eda611c5122e6757.png"> </a>
</p>

<p style="text-align: center;">
	[الشكل 2: كل تبديلات !3 (أي 6) لثلاثة كتب على رف كتب]
</p>

<p>
	لحساب ذلك بنفسك، فكر بكل التبديلات الممكنة بالنسبة إلى n كتاب. لديك n خيار ممكن للكتاب الأول وبعدها n-1 خيار ممكن للكتاب الثاني (أي كل كتاب ما عدا المكان الذي اخترته للكتاب الأول) وبعدها n-2 خيار ممكن للكتاب الثالث وهكذا دواليك. مع 6 كتب تكون نتيجة !6 هي 6×5×4×3×2×1 أو 720 صورة. إضافة كتاب واحد آخر يجعل عدد الصور المطلوبة !7 أو 5.040. حتى من أجل قيم n صغيرة، تصبح خوارزميات الوقت العاملي مستحيلة الإنجاز في وقت منطقي، فإذا كان لديك 20 كتاب ويمكنك ترتيبهم وأخذ صورة كل ثانية، فستحتاج إلى وقت أكثر من عمر الكون للانتهاء من كل تبديل.
</p>

<p>
	واحدة من مشاكل O(n!)‎ المعروفة هي معضلة مندوب المبيعات المسافر، إذ يجب على مندوب المبيعات أن يزور n مدينة ويريد حساب المسافة المقطوعة لكل مراتب n!‎ الممكنة والتي يمكنه زيارتها، إذ يستطيع من تلك الحالات إيجاد أقصر طريق، ومن أجل منطقة بعدد كبير من المدن تصبح هذه المهمة مستحيلة الإنجاز في وقت منطقي. لحسن الحظ هناك خوارزميات مُحسنة لإيجاد طريق قصير (ولكن ليس من المضمون أن يكون الأقصر) بطريقة أسرع من O(n!)‎.
</p>

<h2>
	يحسب ترميز Big O الحالات الأسوأ
</h2>

<p>
	يحسب Big O أسوأ حالة ممكنة لأي مهمة، إذ يحتاج إيجاد كتاب معين في رف كتب غير مُنظم مثلًا أن تبدأ من أحد الأطراف وتتحقق من الكتب حتى تجد الكتاب المطلوب. يمكن أن تكون محظوظًا ويكون الكتاب المطلوب هو أول كتاب تتحقق منه، ولكن ربما تكون سيء الحظ ويكون الكتاب الذي تريده هو آخر كتاب تتحقق منه، أو قد لا يكون موجودًا على الرف إطلاقًا. لذلك، في أفضل الحالات لا يهم إذا كان هناك مليارات الكتب التي يجب البحث فيها لأنك ستجد الكتاب الذي تريده مباشرةً، لكن هذا التفاؤل ليس مفيدًا في خوارزميات التحليل. تصف Big O ماذا يحصل في الحالات الأسوأ حظًا، أي إذا كان لديك n كتاب يجب عليك البحث في كل الكتب، ففي هذا المثال يزداد وقت التنفيذ بنفس معدل ازدياد عدد الكتب.
</p>

<p>
	يستخدم بعض المبرمجين ترميز big Omega لوصف الحالة الأفضل للخوارزمية، فمثلًا تعمل <a href="https://wiki.hsoub.com/Algorithms/Asymptotic_Analysis#.D9.85.D8.B5.D8.A7.D8.AF.D8.B1" rel="external">خوارزمية ‎Ω(n)‎</a> بكفاءة خطية في أفضل حالاتها وفي الحالة الأسوأ ربما تستغرق وقتًا أطول. تواجه بعض الخوارزميات حالات محظوظة جدًا، يحيث لا تعمل أي شيء، مثل إيجاد مسار الطريق لمكان أنت أصلًا فيه.
</p>

<p>
	يصف ترميز Big Theta الخوارزميات التي لها الترتيب نفسه في أسوأ وأفضل الحالات، فمثلًا تصف ‎Θ(n)‎ خوارزميةً لديها كفاءة خطية في أحسن وأسوأ الحالات، أي أنها خوارزمية O(n)‎ وكذلك ‎Ω(n)‎. لا يُستخدم هذين الترميزين كثيرًا مثل استخدام big O ولكن تجدر معرفتهما.
</p>

<p>
	يُعد سماع الناس يتحدثون عن "big O الحالة الوسطية" عندما يعنون big Theta أو "big O الحالة الفُضلى" عندما يعنون big Omega أمرًا شائعًا رغم أنه متناقض؛ إذ تصف big O الحالة الأسوأ لوقت تنفيذ الخوارزمية تحديدًا، ولكن حتى لو كانت كلماتهم خاطئة لا تزال تفهم المعنى بغض النظر.
</p>

<h2>
	العمليات الرياضية الكافية للتعامل مع Big O
</h2>

<p>
	إذا كان علم الجبر لديك ضعيفًا فمعرفة العمليات الرياضية التالية أكثر من كافي عند التعامل مع big O:
</p>

<ul>
	<li>
		الضرب: تكرار الإضافة أي 2×4=8 هو مثل 2+2+2+2=8، وفي حال المتغيرات يكون n+n+n هو 3‎×n.
	</li>
	<li>
		ترميز الضرب: يهمل ترميز الجبر عادةً إشارة ×، لذا 2‎×n تُكتب 2n ومع الأرقام 3×2 تُكتب (3)2 أو ببساطة 6.
	</li>
	<li>
		خاصية الضرب بالعدد 1: ضرب أي عدد بالرقم 1 يُنتج الرقم نفسه أي 5=x1‏5 و42 =x1‏42 أو عمومًا n×1=n
	</li>
	<li>
		توزيع الضرب على الجمع: (3×2) + (3×2) = (4+3)2x كل طرف من المعادلة يساوي 14 أي عمومًا a(b+c) = ab+ac
	</li>
	<li>
		الأس: تكرار الضرب 16= 2<sup>4</sup> (تُلفظ "2 مرفوعة للقوة الرابعة تساوي 16") مثل 2×2×2×2= 16 هنا تكون 2 هي الأساس و 4 هي الأس. باستخدام المتغيرات n×n×n×n هي n<sup>4</sup>. يُستخدم في بايثون المعامل <code>**</code> على سبيل المثال <code>2**4</code> تساوي 16.
	</li>
	<li>
		الأس الأول يساوي الأساس: 2= 2<sup>1</sup> و 9999=9999<sup>1</sup> وبصورةٍ عامة n<sup>1</sup>=n
	</li>
	<li>
		الأس 0 يساوي 1: 1= 2<sup>0</sup> و 1=9999<sup>0</sup> وبصورةٍ عامة n<sup>0</sup>=1
	</li>
	<li>
		المعاملات: عوامل الضرب في 3n<sup>2</sup>+4n+5 المعاملات هي 3 و4 و5. يمكنك معرفة أن 5 هي معامل لأن 5 يمكن أن يُعاد كتابتها بالشكل (1)5 وأيضًا يمكن إعادة كتابتها 5n<sup>0</sup>.
	</li>
	<li>
		اللوغاريتمات: عكس الأس. لأن 16=2<sup>4</sup> نعرف أن log<sub>2</sub>(16)=4. نفول لوغاريتم الأساس 2 للعدد 16 هو 4. نستخدم في بايثون دالة <code>math.log()‎</code> إذ <code>math.log(16, 2)‎</code> تساوي إلى 4.0.
	</li>
</ul>

<p>
	يتطلب حساب big O تبسيط العمليات عن طريق جمع الحدود المتشابهة؛ والحد هو مجموعة من الأرقام والمتغيرات مضروبة مع بعضها، ففي 3n<sup>2</sup>+4n+5 تكون الحدود هي 3n<sup>2</sup> و4n و5، إذ أن الحدود المتشابهة لديها نفس المتغير مرفوعًا لنفس القوة. في التعبير 3n<sup>2</sup>+4n+6n+5 الحدان 4n و6n هما متشابهان بإمكاننا التبسيط وإعادة الكتابة كالتالي 3n<sup>2</sup>+10n+5.
</p>

<p>
	خذ بالحسبان أنه يمكن كتابة 3n<sup>2</sup>+5n+4 على النحو التالي 3n<sup>2</sup>+5n+4(1)‎، إذ تطابق الحدود في هذا التعبير مرتبات big O التالية O(n<sup>2</sup>)‎ و O(n)‎ و O(1)‎. سيفيد هذا لاحقًا عندما نُسقط المعاملات في حسابات big O.
</p>

<p>
	ستفيد هذه التذكرة عندما تحاول معرفة big O لقطعة من الشيفرة، ولكن لن تحتاجها بعد أن تنتهي من "تحليل Big O بنظرة سريعة" في المقال التالي. مفهوم big O بسيط ويمكن أن يفيد حتى لو لم تتبع القواعد الرياضية بصرامة.
</p>

<h2>
	الخلاصة
</h2>

<p>
	يُعد ترميز Big O من أكثر المفاهيم انتشارًا في علم الحواسيب للمبرمجين، وهو بحاجة لبعض المفاهيم في الرياضيات لفهمه، ولكن يمكن للمفهوم الأساسي، ألا وهو معرفة أن الشيفرة ستبطأ كلما زادت البيانات، أن يصف الخوارزميات دون الحاجة إلى أرقام كبيرة لحسابها.
</p>

<p>
	هناك سبع مراتب لترميز big O، وهي: O(1)‎ أو الوقت الثابت، الذي يصف الشيفرة التي لا تتغير مع زيادة البيانات؛ و O(log n)‎ أو الوقت اللوغاريتمي الذي يصف الشيفرة التي تزداد بخطوة كلما تضاعف عدد البيانات بمقدار n؛ و O(n)‎ أو الوقت الخطي، الذي يصف الشيفرة التي تتباطأ يتناسب مع زيادة حجم البيانات بمقدار n؛ و O(n log n)‎ أو وقت n-log-n، الذي يصف الشيفرة التي هي أبطأ من O(n)‎ والعديد من خوارزميات الترتيب لديها هذه المرتبة.
</p>

<p>
	المراتب الأعلى هي أبطأ لأن وقت تنفيذها يزداد بصورةٍ أسرع من زيادة حجم دخل البيانات. يصف الوقت الحدودي O(n<sup>2</sup>)‎ الشيفرة التي يزداد وقت تنفيذها بتربيع الدخل n. ليست المراتب O(2<sup>n</sup>)‎ أو الوقت الأسّي، و O(n!)‎ أو الوقت العاملي شائعة جدًا، ولكنها تأتي مع المجموعات والتبديلات على الترتيب.
</p>

<p>
	ترجمة -وبتصرف- لقسم من الفصل <a href="http://inventwithpython.com/beyond/chapter13.html" rel="external nofollow">Measuring Performance And Big O Algorithm Analysis</a> من كتاب <a href="https://inventwithpython.com/beyond//" rel="external nofollow">Beyond the Basic Stuff with Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D9%82%D9%8A%D8%A7%D8%B3-%D8%A3%D8%AF%D8%A7%D8%A1-%D9%88%D8%B3%D8%B1%D8%B9%D8%A9-%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r2127/" rel="">قياس أداء وسرعة تنفيذ شيفرة بايثون</a> -<a href="https://wiki.hsoub.com/Algorithms/Asymptotic_Analysis" rel="external">تحليل الخوارزميات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-algorithms-complexity-r1284/" rel="">تعقيد الخوارزميات Algorithms Complexity</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%AF%D9%84%D9%8A%D9%84-%D8%B4%D8%A7%D9%85%D9%84-%D8%B9%D9%86-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A9-r1247/" rel="">دليل شامل عن تحليل تعقيد الخوارزمية</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2128</guid><pubDate>Wed, 13 Sep 2023 13:00:00 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x623;&#x645;&#x627;&#x646; &#x645;&#x648;&#x627;&#x642;&#x639; &#x627;&#x644;&#x648;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D9%85%D8%A7%D9%86-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r2020/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_07/----.png.bf5de21c4c01c4a6d558592d4dbcc808.png" /></p>
<p>
	يتطلب أمان مواقع الويب اليقظة في جميع جوانب تصميم الموقع واستخدامه. لن يجعلك هذا المقال التمهيدي خبيرًا في أمان مواقع الويب، ولكنه سيساعدك على فهم مصدر الهجمات وما يمكنك فعله لتقوية تطبيق الويب ضد الهجمات الأكثر شيوعًا.
</p>

<ul>
	<li>
		<strong>المتطلبات الأساسية</strong>: المهارات الحاسوبية الأساسية.
	</li>
	<li>
		<strong>الهدف</strong>: فهم الهجمات الأكثر شيوعًا التي تهدّد أمان تطبيقات الويب وما يمكنك فعله لتقليل مخاطر اختراق موقعك.
	</li>
</ul>

<h2>
	ما هو أمان موقع الويب؟
</h2>

<p>
	تُعَد <a href="https://academy.hsoub.com/devops/networking/%D8%A2%D9%84%D9%8A%D8%A9-%D8%B9%D9%85%D9%84-%D8%B4%D8%A8%D9%83%D8%A9-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-r571/" rel="">شبكة الإنترنت</a> مكانًا خطيرًا، إذ نسمع بانتظام عن عدم توفّر مواقع الويب بسبب هجمات حجب الخدمة أو عرض معلومات مُعدَّلة وضارة غالبًا على صفحاتها الرئيسية، وكانت قد سُرِّبت الملايين من كلمات المرور وعناوين البريد الإلكتروني وتفاصيل بطاقة الائتمان إلى النطاق العام في حالات أخرى عالية المستوى، مما عرّض مستخدمي مواقع الويب للإحراج الشخصي والمخاطر المالية، فالغرض من أمان موقع الويب هو منع هذه الهجمات أو أيّ نوع آخر منها. التعريف الرسمي لأمان موقع الويب هو فعل أو ممارسة تهدف إلى حماية مواقع الويب من الوصول، أو الاستخدام، أو التعديل، أو التدمير، أو التعطيل غير المُصرَّح به.
</p>

<p>
	يتطلب أمان موقع الويب الفعّال جهدًا في التصميم على كامل موقع الويب بما في ذلك تطبيق الويب وإعداد خادم الويب وسياساتك لإنشاء كلمات المرور وتجديدها والشيفرة البرمجية من طرف العميل. يمكن أن يبدو ذلك صعبًا، إلا أن الخبر السار هو أنه إذا استخدمتَ <a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">إطار عمل</a> ويب من طرف الخادم، فسيفعِّل افتراضيًا آليات دفاع قوية ومدروسة جيدًا ضد عدد من الهجمات الأكثر شيوعًا، ويمكن تخفيف الهجمات الأخرى عبر إعداد خادم الويب من خلال تفعيل بروتوكول HTTPS مثلًا. أخيرًا، هناك أدوات فحص للثغرات الأمنية عامة يمكنها مساعدتك في معرفة ما إذا كنت مرتكبًا لأي أخطاء واضحة.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة علوم الحاسوب
		</p>

		<p class="banner-subtitle">
			دورة تدريبية متكاملة تضعك على بوابة الاحتراف في تعلم أساسيات البرمجة وعلوم الحاسوب
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/computer-science/" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة علوم الحاسوب" src="https://academy.hsoub.com/learn/assets/images/courses/computer-science.png">
	</div>
</div>

<p>
	سيمنحك هذا المقال مزيدًا من التفاصيل حول بعض الهجمات الشائعة وبعض الخطوات البسيطة الممكن اتخاذها لحماية موقعك.
</p>

<p>
	<strong>ملاحظة</strong>: يُعَد هذا المقال موضوعًا تمهيديًا، وهو مُصمَّم لمساعدتك على البدء في التفكير في أمان موقع الويب، ولكنه ليس شاملًا.
</p>
<iframe allowfullscreen="" data-controller="core.front.core.autosizeiframe" data-embedauthorid="3889" data-embedcontent="" src="https://academy.hsoub.com/files/20-%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%A3%D9%85%D8%A7%D9%86-%D8%A7%D9%84%D8%B1%D9%82%D9%85%D9%8A/?do=embed" style="margin: auto;"></iframe>

<h2>
	هجمات أمان مواقع الويب
</h2>

<p>
	سنوضّح عددًا من هجمات مواقع الويب الأكثر شيوعًا وكيفية التخفيف منها. لاحظ كيف تكون الهجمات أنجح عندما يثق تطبيق الويب بالبيانات القادمة من المتصفح، أو عندما لا يكون متشككًا بها بالقدر الكافي.
</p>

<h3>
	هجمات السكربتات العابرة للمواقع Cross-Site Scripting أو XSS
</h3>

<p>
	هجمات السكربتات العابرة للمواقع هو مصطلح يُستخدَم لوصف فئة من الهجمات التي تسمح للمهاجمين بحقن سكربتات من طرف العميل من خلال موقع الويب في متصفحات المستخدمين الآخرين. تأتي الشيفرة البرمجية المحقونة إلى المتصفح من الموقع، لذلك تُعَد هذه الشيفرة البرمجية موثوقة ويمكنها تطبيق أمور، مثل إرسال ملف تعريف ارتباط تصريح الموقع الخاص بالمستخدم إلى المهاجم، وبالتالي يمكنه تسجيل الدخول إلى الموقع كما لو أنه المستخدم ويفعل أي شيء يمكن للمستخدم فعله مثل الوصول إلى تفاصيل بطاقته الائتمانية، أو الاطلاع على تفاصيل جهات الاتصال، أو تغيير كلمات المرور.
</p>

<p>
	<strong>ملاحظة</strong>: كانت ثغرات XSS سابقًا أكثر شيوعًا من أي نوع آخر من الهجمات الأمنية.
</p>

<p>
	تنقسم ثغرات XSS إلى ثغرات منعكسة Reflected وأخرى دائمة Persistent بناءً على كيفية إعادة الموقع للسكربتات البرمجية المحقونة في المتصفح كما يلي:
</p>

<ul>
	<li>
		تحدث ثغرة XSS المنعكسة عند إعادة محتوى المستخدم المُمرَّر إلى الخادم مباشرةً مع عدم تعديله لعرضه في المتصفح، إذ ستُشغّل أيّ سكربتات برمجية في محتوى المستخدم الأصلي عند تحميل الصفحة الجديدة. ضع في بالك مثلًا وظيفة البحث في الموقع إذ تُرمَّز مصطلحات البحث بوصفها معاملات في <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87-r1435/" rel="">عنوان URL</a>، وتُعرَض هذه المصطلحات مع النتائج. يمكن للمهاجم إنشاء رابط بحث يحتوي على سكربت ضار بوصفه معاملًا مثل الرابط الآتي ويرسله بالبريد الإلكتروني إلى مستخدم آخر. إذا نقر المستخدم المستهدف على هذا "الرابط المهم"، فسيُنفَّذ السكربت عند عرض نتائج البحث، مما يمنح المهاجم جميع المعلومات التي يحتاجها للدخول إلى الموقع بصفته المستخدم المستهدف، ويُحتمَل أن ينفّذ عمليات شراء بصفته المستخدم، أو يشارك معلومات جهات اتصاله:
	</li>
</ul>

<pre class="ipsCode">http://developer.mozilla.org?q=beer&lt;script%20src="http://example.com/tricky.js"&gt;&lt;/script&gt;‎
</pre>

<ul>
	<li>
		تحدث ثغرة XSS الدائمة عند تخزين السكربت الضار على موقع الويب ثم يعاد عرضه لاحقًا دون تعديل لينفّذه المستخدمون الآخرون دون علمهم. يمكن أن تخزّن مثلًا لوحة المناقشة التي تقبل تعليقات تحتوي على شيفرة HTML غير معدلة سكربتًا ضارًا من المهاجم، ويُنفَّذ السكربت عند عرض التعليقات ويمكن أن يرسل إلى المهاجم المعلومات المطلوبة للوصول إلى حساب المستخدم. يُعَد هذا النوع من الهجمات شائعًا وقويًا جدًا، لأن المهاجم يمكن ألّا يكون له أي ارتباطٍ مباشر مع الضحايا.
	</li>
</ul>

<p>
	تُعَد البيانات الواردة من طلبات من النوع "POST" أو "GET" المصدر الأكثر شيوعًا لثغرات XSS، ولكن يُحتمَل أن تكون أيّ بيانات من المتصفح عرضةً للخطر، مثل بيانات ملفات تعريف الارتباط التي يعرضها المتصفح، أو ملفات المستخدم المُحمَّلة والمعروضة. أفضل دفاع ضد ثغرات XSS هو إزالة أو تعطيل أي شيفرة HTML يمكن أن تحتوي على تعليمات لتشغيل الشيفرة البرمجية، ويتضمن ذلك في لغة HTML عناصرًا، مثل العناصر <a href="https://wiki.hsoub.com/HTML/script" rel="external"><code>&lt;script&gt;</code></a> و <a href="https://wiki.hsoub.com/HTML/object" rel="external"><code>&lt;object&gt;</code></a> و <a href="https://wiki.hsoub.com/HTML/embed" rel="external"><code>&lt;embed&gt;</code></a> و <a href="https://wiki.hsoub.com/HTML/link" rel="external"><code>&lt;link&gt;</code></a>.
</p>

<p>
	تُعرَف عملية تعديل بيانات المستخدم بحيث لا يمكن استخدامها لتشغيل السكربتات أو التأثير بطريقة أخرى على تنفيذ شيفرة الخادم البرمجية باسم تعقيم الإدخال Input Sanitization، إذ تطبّق العديد من أطر عمل الويب تلقائيًا عملية تعقيم مدخلات المستخدم من <a href="https://wiki.hsoub.com/HTML/form" rel="external">نماذج HTML</a> افتراضيًا.
</p>

<h3>
	حقن استعلامات SQL
</h3>

<p>
	تمكّن الثغرات الأمنية الخاصة بحقن <a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85-%D8%B9%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-sql-r588/" rel="">استعلامات SQL</a> المستخدمين الضارين من تنفيذ شيفرة SQL عشوائية على قاعدة بيانات، مما يسمح بالوصول إلى البيانات أو تعديلها أو حذفها بغض النظر عن أذونات المستخدم. يمكن أن يؤدي هجوم الحقن الناجح إلى انتحال الهويات، أو إنشاء هويات جديدة لديها حقوق إدارة، أو الوصول إلى جميع البيانات الموجودة على الخادم، أو تخريب، أو تعديل البيانات لجعلها غير قابلة للاستخدام. تتضمن أنواع حقن استعلامات SQL حقن استعلامات SQL المستندة إلى الأخطاء وحقن استعلامات SQL المستندة إلى الأخطاء المنطقية وحقن استعلامات SQL المستندة إلى الوقت.
</p>

<p>
	تظهر هذه الثغرة الأمنية إذا كان من الممكن أن يغيّر مُدخل المستخدم المُمرَّر إلى تعليمة SQL الأساسية معنى التعليمة، فالغرض من الشيفرة التالية مثلًا هو سرد كافة المستخدمين الذين يملكون اسمًا معينًا <code>userName</code> يوفّره نموذج HTML:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6264_12" style=""><span class="pln">statement </span><span class="pun">=</span><span class="pln"> </span><span class="str">"SELECT * FROM users WHERE name = '"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> userName </span><span class="pun">+</span><span class="pln"> </span><span class="str">"';"</span></pre>

<p>
	إذا حدّد المستخدم اسمًا حقيقيًا، فستعمل التعليمة السابقة كما هو متوقع، ولكن يمكن لمستخدم ضار تغيير سلوك تعليمة SQL هذه بالكامل إلى التعليمة الجديدة في المثال الآتي من خلال تحديد التعليمة <code>a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't</code> للحقل <code>userName</code>.
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6264_8" style=""><span class="pln">SELECT </span><span class="pun">*</span><span class="pln"> FROM users WHERE name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">;</span><span class="pln">DROP TABLE users</span><span class="pun">;</span><span class="pln"> SELECT </span><span class="pun">*</span><span class="pln"> FROM userinfo WHERE </span><span class="str">'t'</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">'t'</span><span class="pun">;</span></pre>

<p>
	تنشِئ التعليمة المُعدَّلة تعليمة SQL صالحة تحذف جدول المستخدمين <code>users</code> وتحدّد جميع البيانات من جدول معلومات المستخدم <code>userinfo</code> الذي يكشف عن معلومات كل مستخدم. تعمل هذه التعليمة لأن الجزء الأول من النص المحقون (<code>a';‎</code>) يكمل التعليمة الأصلية.
</p>

<p>
	يمكنك تجنب هذا النوع من الهجوم من خلال التأكد من أن بيانات المستخدم المُمرَّرة إلى استعلام SQL لا يمكنها تغيير طبيعته، ويمكن تحقيق ذلك باستخدام <a href="https://en.wikipedia.org/wiki/Escape_character" rel="external nofollow">محرف الهروب Escape Character</a> للهروب من جميع المحارف في مدخلات المستخدم التي لها معنًى خاص في <a href="https://academy.hsoub.com/programming/sql/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D9%91%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-sql-r585/" rel="">لغة SQL</a>.
</p>
<iframe allowfullscreen="" data-controller="core.front.core.autosizeiframe" data-embedauthorid="3889" data-embedcontent="" src="https://academy.hsoub.com/files/16-%D9%85%D9%84%D8%A7%D8%AD%D8%B8%D8%A7%D8%AA-%D9%84%D9%84%D8%B9%D8%A7%D9%85%D9%84%D9%8A%D9%86-%D8%A8%D9%84%D8%BA%D8%A9-sql/?do=embed" style="margin: auto;"></iframe>

<p>
	<strong>ملاحظة</strong>: تعالج تعليمة SQL المحرف (') بوصفه بداية ونهاية سلسلة محرفية، وإذا وضعت شرطة مائلة عكسية أمام هذا المحرف (')، فسنهرّب الرمز ونطلب من لغة SQL التعامل معه بوصفه محرفًا، أي مجرد جزء من السلسلة النصية.
</p>

<p>
	سنهرّب في التعليمة التالية المحرف (')، إذ ستفسّر لغة SQL الآن الاسم <code>name</code> على أنه سلسلة نصية كاملة بالخط العريض bold (حسنًا إنه اسم غريب جدًا، ولكنه ليس ضارًا):
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_6264_10" style=""><span class="pln">SELECT </span><span class="pun">*</span><span class="pln"> FROM users WHERE name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'a\';DROP TABLE users; SELECT * FROM userinfo WHERE \'t\' = \'t'</span><span class="pun">;</span></pre>

<p>
	تهتم أطر عمل الويب بمحرف الهروب نيابةً عنك، إذ يضمن إطار عمل <a href="https://academy.hsoub.com/programming/python/django/" rel="">جانغو Django</a> مثلًا هروب أيّ بيانات مستخدم مُمرَّرة إلى مجموعات الاستعلام (استعلامات النموذج).
</p>

<p>
	<strong>ملاحظة</strong>: يمكنك الاطلاع على مقال <a href="https://academy.hsoub.com/devops/security/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%A3%D9%85%D9%8A%D9%86-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D8%B3%D8%AD%D8%A7%D8%A8%D9%8A%D8%A9-%D8%B6%D8%AF-%D9%87%D8%AC%D9%85%D8%A7%D8%AA-%D8%AD%D9%82%D9%86-sql-r98/" rel="">كيفية تأمين الخوادم السحابية ضد هجمات حقن SQL</a> لمعلومات أكثر.
</p>

<h3>
	تزوير الطلبات عبر المواقع Cross-Site Request Forgery أو CSRF
</h3>

<p>
	تسمح هجمات CSRF لمستخدمٍ ضار بتنفيذ إجراءات باستخدام ثبوتيات مستخدم آخر دون عِلم هذا المستخدم أو موافقته. لنفترض مثلًا أن سامي مستخدم ضار يعرف أن موقعًا معينًا يسمح للمستخدمين الذين سجّلوا الدخول بإرسال أموال إلى حسابٍ محدد باستخدام طلب HTTP من النوع "POST"، بحيث يتضمن هذا الطلب اسم الحساب ومبلغًا من المال. ينشئ سامي نموذجًا يتضمن التفاصيل المصرفية الخاصة به ومبلغًا من المال بوصفها حقولًا مخفية، ويرسله بالبريد الإلكتروني إلى مستخدمي الموقع الآخرين مع زر الإرسال Submit المخفي بوصفه رابطًا إلى موقع "الثراء السريع get rich quick".
</p>

<p>
	إذا نقر المستخدم على زر الإرسال، فسيُرسَل <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B7%D9%84%D8%A8%D8%A7%D8%AA-http-%D9%81%D9%8A-nodejs-r1868/" rel="">طلب HTTP</a> من النوع "POST" إلى الخادم الذي يحتوي على تفاصيل المعاملة وأيّ ملفات تعريف ارتباط من طرف العميل يرتبط من خلالها المتصفح بالموقع، إذ تُعَد إضافة ملفات تعريف الارتباط المرتبطة بالموقع إلى الطلبات سلوك المتصفح العادي. سيتحقق الخادم من ملفات تعريف الارتباط، ويستخدمها لتحديد ما إذا كان المستخدم قد سجّل الدخول ولديه إذن لإجراء المعاملة أم لا. النتيجة هي أن أيّ مستخدم ينقر على زر الإرسال أثناء تسجيل الدخول إلى موقع التعاملات المالية سيجري المعاملة، وسيصبح سامي ثريًا.
</p>

<p>
	<strong>ملاحظة</strong>: الحيلة هنا هي أن سامي لا يحتاج إلى الوصول إلى ملفات تعريف الارتباط الخاصة بالمستخدم أو الوصول إلى ثبوتياته، إذ يخزّن متصفح المستخدم هذه المعلومات ويضمّنها تلقائيًا في جميع الطلبات إلى الخادم المرتبط به.
</p>

<p>
	تتمثل إحدى طرق منع هذا النوع من الهجوم في أن يطلب الخادم أن تتضمن طلبات "POST" سرًا خاصًا بالمستخدم يولّده الموقع، إذ سيوفّر الخادم هذا السر عند إرسال نموذج الويب المُستخدَم لإجراء التحويلات. يمنع هذا الأسلوب سامي من إنشاء نموذجه الخاص، لأنه سيتعين عليه معرفة السر الذي يوفره الخادم للمستخدم، وحتى إذا اكتشف السر وأنشأ نموذجًا لمستخدم معين، فلن يكون قادرًا على استخدام النموذج نفسه لمهاجمة جميع المستخدمين.
</p>

<p>
	تتضمن أطر عمل الويب آليات منع هجمات CSRF في أغلب الأحيان.
</p>

<h3>
	هجمات أخرى
</h3>

<p>
	تشمل الهجمات أو الثغرات الشائعة الأخرى ما يلي:
</p>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%87%D8%AC%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%B7%D8%A7%D9%81-%D8%A8%D8%A7%D9%84%D9%86%D9%82%D8%B1-clickjacking-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1254/" rel="">هجمات الاختطاف بالنقر Clickjacking</a>: يختطف مستخدم ضار في هذا الهجوم نقراتٍ مُوجَّهة إلى موقع مرئي من المستوى الأعلى ويوجّهها إلى صفحة مخفية. يمكن استخدام هذه التقنية مثلًا لعرض موقع مصرفي شرعي ولكنه يلتقط ثبوتيات تسجيل الدخول ضمن عنصر <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe" rel="external nofollow"><code>&lt;iframe&gt;</code></a> غير مرئي يتحكم فيه المهاجم. يمكن استخدام هجمات الاختطاف بالنقر لجعل المستخدم ينقر على زر في موقع مرئي، ولكنه بذلك ينقر على زر مختلف تمامًا من غير عِلمه. يمكن تجنب ذلك من خلال أن يمنع موقعك نفسه من أن يُدمَج في عنصر <code>&lt;iframe&gt;</code> في موقع آخر عبر ضبط ترويسات HTTP المناسبة.
	</li>
	<li>
		هجمات حجب الخدمة Denial of Service -أو اختصارًا DoS: تتحقق هجمات DoS من خلال إغراق الموقع المستهدف بطلبات مزيفة بحيث يتعطل الوصول إلى الموقع للمستخدمين الشرعيين. يمكن أن تكون هذه الطلبات كثيرةً، أو يستهلك كل طلب منها كميات كبيرة من الموارد، مثل عمليات القراءة البطيئة، أو تحميل ملفات كبيرة. تعمل دفاعات DoS من خلال تحديد وحظر حركة المرور السيئة مع السماح بمرور الرسائل المشروعة، وتوجد هذه الدفاعات عادةً قبل خادم الويب أو فيه، فهي ليست جزءًا من تطبيق الويب نفسه.
	</li>
	<li>
		هجوم اجتياز شجرة الملفات Directory Traversal (الملف ومعلوماته): يحاول مستخدم ضار في هذا الهجوم الوصول إلى أجزاء من نظام ملفات خادم الويب التي لا ينبغي أن يتمكن من الوصول إليها. تحدث هذه الثغرة عندما يكون المستخدم قادرًا على تمرير أسماء الملفات التي تتضمن محارف التنقل في نظام الملفات، مثل <code>/../..</code>، والحل هو تعقيم مدخلات المستخدم قبل استخدامها.
	</li>
	<li>
		تضمين الملفات File Inclusion: يكون المستخدم في هذا الهجوم قادرًا على تحديد ملف عشوائي لعرض أو تنفيذ البيانات المُمرَّرة إلى الخادم. يمكن عند التحميل تنفيذ هذا الملف على خادم الويب أو من طرف العميل، مما يؤدي إلى هجوم XSS، والحل هو تعقيم مدخلات المستخدم قبل استخدامها.
	</li>
	<li>
		حقن الأوامر Command Injection: تسمح هجمات حقن الأوامر للمستخدم الضار بتنفيذ أوامر نظام عشوائية على نظام التشغيل المضيف. الحل هو تعقيم مدخلات المستخدم قبل استخدامها في استدعاءات النظام.
	</li>
</ul>

<p>
	اطّلع أيضًا على <a href="https://academy.hsoub.com/devops/networking/%D8%A7%D9%84%D9%87%D8%AC%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%85%D9%86%D9%8A%D8%A9-security-attacks-%D9%81%D9%8A-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8%D9%8A%D8%A9-r540/" rel="">الهجمات الأمنية Security Attacks في الشبكات الحاسوبية</a>.
</p>

<h2>
	بعض النصائح الأساسية
</h2>

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

<p>
	<strong>تحذير</strong>: الدرس الوحيد الأكثر أهمية الذي يمكنك تعلّمه حول أمان موقع الويب هو عدم الوثوق أبدًا في البيانات الواردة من المتصفح مثل البيانات الموجودة في معاملات عنوان URL لطلبات "GET" وطلبات "POST" وترويسات وملفات تعريف ارتباط HTTP والملفات التي يرفعها المستخدم. تحقق دائمًا من جميع البيانات الواردة وعقّمها، وافترض الأسوأ دائمًا.
</p>

<p>
	إليك عددًا من الخطوات الأخرى التي يمكنك اتخاذها:
</p>

<ul>
	<li>
		استخدام إدارة أكثر فاعلية لكلمات المرور: شجّع على استخدام كلمات المرور القوية، واستخدم الاستيثاق الثنائي two-factor authentication في موقعك، بحيث يجب على المستخدم إدخال رمز استيثاق آخر بالإضافة إلى كلمة المرور، إذ يمكن تسليم رمز الاستيثاق باستخدام العتاد الحقيقي الذي يمتلكه المستخدم فقط مثل رمز في رسالة SMS مرسَلة إلى هاتف المستخدم.
	</li>
	<li>
		إعداد خادمك الويب لاستخدام بروتوكولات HTTPS وأمن HTTP للنقل الصارم HTTP Strict Transport Security -أو اختصارًا HSTS: يشفّر بروتوكول HTTPS البيانات المرسَلة بين العميل والخادم، مما يضمن أن تكون ثبوتيات تسجيل الدخول وملفات تعريف الارتباط وبيانات طلبات "POST" ومعلومات الترويسة غير متاحة بسهولة للمهاجمين.
	</li>
	<li>
		يجب تتبّع الهجمات الأكثر شيوعًا (<a href="https://owasp.org/www-project-top-ten/" rel="external nofollow">قائمة OWASP الحالية</a>) ومعالجة الثغرات الأكثر شيوعًا أولًا.
	</li>
	<li>
		استخدام <a href="https://owasp.org/www-community/Vulnerability_Scanning_Tools" rel="external nofollow">أدوات فحص الثغرات</a> لإجراء اختبار أمان آلي على موقعك. يمكن أن يجد موقع الويب الناجح الخاص بك لاحقًا أخطاءً من خلال تقديم مكافآت على اكتشاف الأخطاء كما يفعل <a href="https://www.mozilla.org/en-US/security/bug-bounty/faq-webapp/" rel="external nofollow">موقع موزيلا</a>.
	</li>
	<li>
		تخزين وعرض البيانات التي تحتاجها فقط، فمثلًا إذا كان يجب على المستخدمين تخزين معلومات حساسة مثل تفاصيل البطاقة الائتمانية، فاعرض فقط أرقامًا كافية من رقم البطاقة التي يمكن للمستخدم تحديدها ولا يكفي أن ينسخها المهاجم ويستخدمها على موقع آخر، والنمط الأكثر شيوعًا عندها هو عرض آخر 4 أرقام فقط من رقم البطاقة الائتمانية.
	</li>
</ul>

<p>
	يمكن أن تساعد أطر عمل الويب في التخفيف من العديد من الثغرات الأكثر شيوعًا.
</p>

<h2>
	الخلاصة
</h2>

<p>
	أوضحنا في هذا المقال مفهوم أمان الويب وبعض الهجمات الأكثر شيوعًا التي يجب أن يحاول موقع الويب الحماية منها، ويجب أن تفهم أن تطبيق الويب لا يمكنه الوثوق بأيّ بيانات من متصفح الويب، لذا يجب تعقيم جميع بيانات المستخدم قبل عرضها أو استخدامها في استعلامات SQL واستدعاءات نظام الملفات.
</p>

<p>
	وصلنا مع هذا المقال إلى نهاية هذه السلسلة من المقالات والتي تغطي خطواتك الأولى في برمجة مواقع الويب من طرف الخادم، وبذلك تعلّمتَ المفاهيم الأساسية، وأصبحتَ جاهزًا لتحديد إطار عمل الويب وبدء البرمجة.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps/Website_security" rel="external nofollow">Website security</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/devops/servers/%D8%A3%D8%B7%D8%B1-%D8%B9%D9%85%D9%84-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r784/" rel="">أطر عمل الويب من طرف الخادم</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/apps/general/%D8%AA%D8%A3%D9%85%D9%8A%D9%86-%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%81%D9%8A-%D8%A7%D9%84%D8%B9%D8%A7%D9%84%D9%85-%D8%A7%D9%84%D8%B1%D9%82%D9%85%D9%8A-r382/" rel="">تأمين متصفحات الويب في العالم الرقمي</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/%D9%85%D9%85%D8%A7%D8%B1%D8%B3%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%85%D9%86-%D9%88%D8%A7%D9%84%D8%AD%D9%85%D8%A7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-php-r1205/" rel="">ممارسات الأمن والحماية في تطبيقات PHP</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/django/%D8%B1%D9%81%D8%B9-%D9%85%D8%B3%D8%AA%D9%88%D9%89-%D8%A3%D9%85%D8%A7%D9%86-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%AC%D8%A7%D9%86%D8%BA%D9%88-%D9%81%D9%8A-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%A7%D8%AC-r1717/" rel="">رفع مستوى أمان تطبيقات جانغو في بيئة الإنتاج</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2020</guid><pubDate>Wed, 26 Jul 2023 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x631;&#x62C;&#x639; &#x627;&#x644;&#x634;&#x627;&#x645;&#x644; &#x625;&#x644;&#x649; &#x62A;&#x639;&#x644;&#x645; &#x627;&#x644;&#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x627;&#x62A; &#x644;&#x644;&#x645;&#x628;&#x62A;&#x62F;&#x626;&#x64A;&#x646;</title><link>https://academy.hsoub.com/programming/advanced/%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_06/426443801_.png.acbf8f400d80ccbcee63b9934b029393.png" /></p>
<p>
	لا شك<strong> تعلم الخوارزميات</strong> أحد النصائح المهمة التي وجهت لك إذا قررت تعلم البرمجة فكلمة <strong>خوارزمية</strong> تتردد كثيرًا في مجال البرمجة وغيرها من المجالات مثل الرياضيات والعلوم والمنطق وكل ما يتعلق بالحاسوب خصوصًا، فهي من أهم المفاهيم التي لا يمكن أن يستقيم لك تعلم البرمجة والرياضيات وعلوم الحاسب بدونها.
</p>

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

<h2>
	ما هي الخوارزمية؟
</h2>

<p>
	الخوارزمية algorithm هي مجموعة من التعليمات المرتبة لحل مشكلة ما في الرياضيات أو أي مشكلة تواجهك في الحياة اليومية خلال زمن محدد وعدد خطوات محدود.
</p>

<p>
	ينبغي أن تكون خطوات وتعليمات الخوارزمية واضحة ومُرتّبة بحيث تنتهي بحل المشكلة. فكّر في الخوارزمية كما لو كانت وصفة طبخ، إذ تقدّم الوصفة طريقة تحضير وجبة ما خطوة بخطوة، ابتداءً بالمقادير الضرورية للوجبة، وحتى آخر خطوة من تحضير الوجبة وتقديمها.
</p>

<p>
	ورغم أن مفهوم الخوارزميات يمكن أن يطبق في كل مجالات الحياة لكن تبرز أهمية الخوارزميات في <a href="https://academy.hsoub.com/programming/general/%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/" rel="">علوم الحاسب</a> بشكل خاص فهي تنظم عمل المبرمج وتعزز تفكيره المنطقي والرياضي وتمكنه من فهم المشكلات وتصورها بشكل أفضل قبل حلها وتطبيقها فعليًا على برامج حاسوبية مكتوبة بإحدى لغات البرمجة.
</p>

<h2>
	تاريخ الخوارزميات
</h2>

<p>
	تُعزى أقدم الخوارزميات المعروفة إلى البابليين، إذ عُثِر على أقدم لوح يحتوي تعليمات خوارزمية لإجراء عملية القسمة، ويعود تاريخه لسنة 2500 قبل الميلاد. وقد عثِر كذلك على خوارزميات حسابية تعود إلى المصريين القدامى تعود إلى سنة 1550 قبل الميلاد.
</p>

<p>
	ازداد استخدام الخوارزميات في حقبة اليونان، حيث ظهرت الكثير من الخوارزميات الرياضية التي ما تزال تُستخدم حتى يومنا هذا، مثل خوارزمية قسمة إقليدس التي تحسب خارج وباقي عملية القسمة.
</p>

<p>
	تطور مفهوم الخوارزميات في عصر الحضارة الإسلامية، إذ استخدم المسلمون الخوارزميات لحل المعادلات والمسائل الرياضية. ولعل أشهر هذه الخوارزميات هي خوارزمية حل المعادلات من الدرجة الثانية التي ذُكِرت في كتاب "حساب الجبر والمقابلة" لعالم الرياضيات المسلم <a href="https://ar.wikipedia.org/wiki/%D9%85%D8%AD%D9%85%D8%AF_%D8%A8%D9%86_%D9%85%D9%88%D8%B3%D9%89_%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A" rel="external nofollow">محمد بن موسى الخوارزمي</a> مؤسس علم الجبر، والذي تُنسب إليه كلمة خوارزمية في اللغة العربية، وكذلك الكلمة المقابلة لها في اللغات اللاتينية <strong>algorithm</strong> المُشتقة من الكلمة al-Khwārizmī، وهو الاسم الرومي للخوارزمي -وأيضًا كلمة الجبر algebra.
</p>

<p>
	استخدم الأوروبيون كلمة algorithm للدلالة على القواعد والتقنيات التي استخدمها الخوارزمي لحل المعادلات الجبرية، ثمّ عُمِّم هذا المصطلح ليشمل أيّ مجموعة من القواعد والتقنيات الساعية لحل مشكلة ما.
</p>

<p>
	استمر مفهوم الخوارزميات في التطور بعد الحقبة الإسلامية إبّان عصر النهضة، خصوصًا مع تطوّر أسس علم الحوسبة في القرن التاسع عشر وإنتاج أول خوارزمية يمكن تنفيذها على الحاسوب سنة 1840 على يد آدا لوفانس Ada Lovelace. ثمّ الصياغة النهائية لمفهوم الخوارزمية على يد آلان تورنغ Alan Turing عبر آلته الشهيرة <a href="https://ar.wikipedia.org/wiki/%D8%A2%D9%84%D8%A9_%D8%AA%D9%88%D8%B1%D9%86%D8%BA" rel="external nofollow">آلة تورنغ</a> (Turing machine).
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة علوم الحاسوب
		</p>

		<p class="banner-subtitle">
			دورة تدريبية متكاملة تضعك على بوابة الاحتراف في تعلم أساسيات البرمجة وعلوم الحاسوب
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/computer-science/" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة علوم الحاسوب" src="https://academy.hsoub.com/learn/assets/images/courses/computer-science.png">
	</div>
</div>

<h2>
	أركان الخوارزمية
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="128500" href="https://academy.hsoub.com/uploads/monthly_2023_06/629631136_.png.4f3170c99c5b1ac4e43f111171298ad0.png" rel=""><img alt="أركان الخوارزميات" class="ipsImage ipsImage_thumbnailed" data-fileid="128500" data-ratio="52.33" data-unique="ausl0o1em" style="width: 900px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2023_06/.thumb.png.d592008738049c962b558e4db8d76d6d.png"> </a>
</p>

<p>
	تملك أي خوارزمية ثلاثة أركان رئيسية وهي:
</p>

<ul>
	<li>
		<p>
			<strong>الدخل أو المدخلات</strong>: تمثل البيانات أو الأشياء الضرورية والمطلوبة التي تعمل عليها الخوارزمية وإن كان الدخل مؤلفًا من عدة عناصر، فإنّ تعداد عناصره يسمى <strong>حجم الدخل</strong>، مثلًا إن كان الدخل عبارة عن مصفوفة أو سلسلة نصية مؤلفة من n عنصر، فإنّ حجم الدخل سيساوي n. لو عدنا إلى مثال الطبخ فإنّ دخل خوارزمية طهي وجبة معينة ستكون هي المقادير المُستخدمة لإعداد الوجبة.
		</p>
	</li>
	<li>
		<p>
			<strong>متن الخوارزمية</strong>: تأخذ الخوارزمية الدخل، وتطبّق عليه سلسلة من خطوات المعالجة المتتابعة والمُحدّدة سلفًا. مثلًا، يشمل متن خوارزمية الطبخ كل عمليات تجهيز المقادير وإعدَادها وخطوات طهيها.
		</p>
	</li>
	<li>
		<p>
			<strong>الخرج أو المُخرجات</strong>: بعد أن تنتهي الخوارزمية من تنفيذ كافة الخطوات، تُنتج لنا خرجًا يمثل حل المشكلة. مثلًا خرج خوارزمية طهي وجبة سيكون هو الوجبة نفسها جاهزة ومطهيّة.
		</p>
	</li>
</ul>

<p>
	على سبيل المثال إذا أردنا كتابة خوارزمية جداء عددين صحيحين ستكون أركان هذه الخوارزمية كالتالي:
</p>

<ul>
	<li>
		<p>
			<strong>الدخل</strong>: هو العددان الصحيحان المطلوب حساب ناتج جدائهما x, y
		</p>
	</li>
	<li>
		<p>
			<strong>الخرج</strong>: هو ناتج الجداء z
		</p>
	</li>
	<li>
		<p>
			<strong>متن الخوارزمية</strong>:
		</p>

		<ul>
			<li>
				<p>
					الخطوة 1: ابدأ
				</p>
			</li>
			<li>
				<p>
					الخطوة 2: قم بالتصريح عن ثلاثة أعداد صحيحة x و y و z
				</p>
			</li>
			<li>
				<p>
					الخطوة 3: أدخل قيم المدخلات x و y
				</p>
			</li>
			<li>
				<p>
					الخطوة 4: اضرب قيم x بـ y
				</p>
			</li>
			<li>
				<p>
					الخطوة 5: خزّن ناتج الضرب في z
				</p>
			</li>
			<li>
				<p>
					الخطوة 6: اعرض قيمة z
				</p>
			</li>
			<li>
				<p>
					الخطوة 7: توقف
				</p>
			</li>
		</ul>
	</li>
</ul>

<h2>
	خطوات حل الخوارزميات
</h2>

<p>
	عندما تواجهك أي مشكلة وتريد كتابة خوارزمية لحلها عليك أن تفكر في العثور على إجابات للأسئلة التالية:
</p>

<ul>
	<li>
		ما هو دخل الخوارزمية أي ما هي المعلومات التي أحتاج إلى الحصول عليها من المستخدم؟
	</li>
	<li>
		ما هو خرج الخوارزمية أي ما هي المعلومات التي أحتاج لعرضها على المستخدم؟
	</li>
	<li>
		ما هي الخطوات الرئيسية المطلوبة لحل هذه المشكلة؟
	</li>
	<li>
		ما هو ترتيب تنفيذ هذه الخطوات؟
	</li>
	<li>
		ما هي القرارات أو الشروط التي أحتاج مراعاتها عند معالجة المعلومات؟
	</li>
	<li>
		هل هناك تعليمات بحاجة لأن أكررها عدة مرات؟
	</li>
</ul>

<h2>
	طرق تمثيل الخوارزميات
</h2>

<p>
	يمكنك ثمثيل الخوارزمية أو التعبير عنها بعدة طرق:
</p>

<ul>
	<li>
		الطريقة الأولى من خلال <strong>الكلام المبعثر</strong> الذي يصف حل المشكلة بلغتك المحكية دون اتباع أي قواعد في الوصف.
	</li>
	<li>
		الطريقة الثانية من خلال ما يسمى <strong>الشيفرة الوهمية أو الزائفة pseudocode</strong> وهي مجموعة من التعليمات التي تحاكي في طريقة كتابتها لغات البرمجة لكنها لا تلتزم <a href="https://academy.hsoub.com/programming/general/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D8%A8%D8%B3%D8%A7%D8%B7%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-r1870/" rel="">بقواعد البرمجة</a> التي يجب الالتزام بها عندما تكتب شيفرات برمجية فعلية.
	</li>
	<li>
		الطريقة الثالثة تسمى <strong>المخططات الانسيابية Flowcharts</strong>، والمخطط الانسيابي هو تمثيل رسومي للخوارزمية يرسم باستخدام أنواع مختلفة من الرموز لكل رمز غرض معين وهو في تقسيم المشكلة الكبيرة إلى مشاكل صغيرة سهلة الفهم ويعد طريقة مناسبة للتواصل بين الأشخاص غير التقنيين.
	</li>
</ul>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="128496" href="https://academy.hsoub.com/uploads/monthly_2023_06/1765347479_.png.455dc58d2cacb271b07a5e586fe40220.png" rel=""><img alt="طرق تمثيل الخوارزميات" class="ipsImage ipsImage_thumbnailed" data-fileid="128496" data-ratio="101.13" data-unique="w2qrrrnra" style="width: 530px; height: auto;" width="530" src="https://academy.hsoub.com/uploads/monthly_2023_06/1765347479_.png.455dc58d2cacb271b07a5e586fe40220.png"> </a>
</p>

<p>
	تتوافر عدة برامج مساعدة تساعدك على رسم المخططات الانسيابية، <a href="https://academy.hsoub.com/apps/productivity/office/microsoft-powerpoint/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%B1%D8%B3%D9%85-%D9%85%D8%AE%D8%B7%D8%B7-%D8%A7%D9%86%D8%B3%D9%8A%D8%A7%D8%A8%D9%8A-flowchart-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-powerpoint-r210/" rel="">وللمزيد يمكنك مطالعة مقال كيفية رسم مخطط انسيابي Flowchart باستخدام PowerPoin</a>.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة الذكاء الاصطناعي
		</p>

		<p class="banner-subtitle">
			احترف برمجة الذكاء الاصطناعي AI وتحليل البيانات وتعلم كافة المعلومات التي تحتاجها لبناء نماذج ذكاء اصطناعي متخصصة.
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/artificial-intelligence" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<a href="https://academy.hsoub.com/learn/artificial-intelligence" rel=""><img alt="دورة الذكاء الاصطناعي AI" src="https://academy.hsoub.com/learn/assets/images/courses/artificial-intelligence.png"></a>
	</div>
</div>

<h2>
	مثال على استخدام الخوارزمية في حياتنا اليومية
</h2>

<p>
	على سبيل المثال إذا طلب منك كتابة خوارزمية توضح طريقة التعامل مع آلة صنع القهوة والشاي ستكون الخطوات التي عليك اتباعها كالتالي:
</p>

<ul>
	<li>
		معرفة إن كان الزبون يريد شرب الشاي أم القهوة
	</li>
	<li>
		إضافة الشاي أو القهوة إلى الكوب
	</li>
	<li>
		معرفة إن كان المستخدم يريد إضافة السكر للكوب
	</li>
	<li>
		إذا كان الجواب نعم أضف السكر إلى الكوب
	</li>
	<li>
		سكب الماء الساخن في الكوب
	</li>
</ul>

<p>
	للتعبير عن هذه الخوارزمية باستخدام الشيفرة الزائفة سنكتب التالي:
</p>

<pre class="ipsCode">START;
/Would you like Tea or Coffee?/;
if tea {
  Add Tea in cup;
} else {
  Add Coffee in cup;
}
/Would you like Sugar?/;
if Sugar {
  Add Sugar in cup;
}
Pour boiling water in cup
END;
</pre>

<p>
	وللتعبير عنها باستخدام المخطط الانسيابي سنرسم المخطط التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="128495" href="https://academy.hsoub.com/uploads/monthly_2023_06/2135123883_.png.f2df3d47461824102e861cd426653ba9.png" rel=""><img alt="المخطط الانسيابي لخوارزمية شاي" class="ipsImage ipsImage_thumbnailed" data-fileid="128495" data-ratio="213.52" data-unique="pjpjp1bi1" style="width: 281px; height: auto;" width="281" src="https://academy.hsoub.com/uploads/monthly_2023_06/.thumb.png.755dad9bd513d5bbaaa93170efb7d2e9.png"></a>
</p>

<h2>
	أمثلة على الخوارزميات
</h2>

<p>
	لنفهم بشكل أفضل ما هي الخوارزميات دعنا نستعرض بعض الأمثلة الإضافية على خوارزميات لحل مسائل رياضية بسيطة ونتعرف على أركانها المختلفة وخطوات حلها ونعبر عنها بالطرق المختلفة.
</p>

<h3>
	خوارزمية لحساب قيمة مضروب عدد
</h3>

<p>
	في الرياضيات المضروب factorial أو عاملي العدد الصحيح n الذي يعبر عنه بالشكل التالي n! هو جداء كل الأعداد الطبيعية المساوية أو الأصغر من n ما عدا الصفر ولكتابة خوارزمية تحل هذه المسألة الرياضية علينا اتباع الخطوات التالية:
</p>

<ul>
	<li>
		<strong>الدخل:</strong> هو العدد الصحيح المطلوب حساب مضروبه n.
	</li>
	<li>
		<strong>الخرج</strong>: هو ناتج المضروب factorial
	</li>
	<li>
		<strong>المتن</strong>:
		<ul>
			<li>
				الخطوة 1: إدخال العدد n المراد حساب مضروبه
			</li>
			<li>
				الخطوة 2: تعريف متغير مساعد وليكن i وهو عبارة عن عدد صحيح يأخذ قيمة متغيرة بين الواحد والعدد نفسه ويساعدنا على حساب القيمة المطلوبة.
			</li>
			<li>
				الخطوة 3: تهيئة المتغير factorial الذي يمثل القيمة المؤقتة للمضروب، والمتغير i الذي يمثل المرحلة التي نحن فيها أثناء تنفيذ الخوارزمية بالقيمة واحد أي نجعل factorial = 1 و i = 1
			</li>
			<li>
				الخطوة 4: إعادة تعيين قيم المتغير factorial بالقيمة factorial*i والمتغير بالقيمة i+1
			</li>
			<li>
				الخطوة 5: كرر الخطوة 3 حتى تصبح قيمة المتغير i أكبر تمامًا من n
			</li>
			<li>
				الخطوة 6: قم بإيقاف الخوارزمية وإعادة قيمة factorial التي تمثل مضروب العدد n
			</li>
		</ul>
	</li>
</ul>

<p>
	الصورة التالية توضح طريقة رسم المخطط الانسيابي لحل خوارزمية حساب مضروب عدد:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="128497" href="https://academy.hsoub.com/uploads/monthly_2023_06/1001793508_.png.ebcd955700c50ee73498b7795af4288b.png" rel=""><img alt="المخطط الانسيابي لخوارزمية المضروب" class="ipsImage ipsImage_thumbnailed" data-fileid="128497" data-ratio="90.36" data-unique="2apaaqpqg" style="width: 664px; height: auto;" width="664" src="https://academy.hsoub.com/uploads/monthly_2023_06/.thumb.png.5f3725f25241226dfb18f68599e7cc2d.png"> </a>
</p>

<p>
	هل تعرف طرقًا أو أساليب أخرى لحل هذه المسألة؟ شاركنا إياها في قسم التعليقات أسفل المقال.
</p>

<h3>
	خوارزمية للعثور على أكبر عدد من بين ثلاثة أعداد
</h3>

<p>
	سنكتب حل هذه المسألة بأكثر من خوارزمية أو طريقة:
</p>

<p>
	<strong>طريقة 1</strong>:
</p>

<ul>
	<li>
		<p>
			<strong>الدخل</strong>: 3 أرقام a و b و c
		</p>
	</li>
	<li>
		<p>
			<strong>الخرج</strong>: العدد الأكبر من بينها a أو b أو c
		</p>
	</li>
	<li>
		<p>
			<strong>المتن</strong>:
		</p>

		<ul>
			<li>
				<p>
					الخطوة 1: أدخل الأرقام الثلاثة وخزنها في المتغيرات a و b و c على التوالي.
				</p>
			</li>
			<li>
				<p>
					الخطوة 2: تحقق من كون الرقم الأول a أكبر من الرقم الثاني b و الثالث c عندها اطبع أن a هو العدد الأكبر بين الكل وأنهي التنفيذ
				</p>
			</li>
			<li>
				<p>
					الخطوة 3: تحقق من كون b أكبر من a و c عندها اطبع أن b هو العدد الأكبر بين الكل وأنهي التنفيذ
				</p>
			</li>
			<li>
				<p>
					الخطوة 4: تحقق من كون c أكبر من a و b عندها اطبع أن c هو العدد الأكبر بين الكل وأنهي التنفيذ
				</p>
			</li>
		</ul>
	</li>
</ul>

<p>
	الكود الزائف للتعبير عن خوارزمية أكبر عدد بين 3 أعداد:
</p>

<pre class="ipsCode">Step-1 Start
Step-2 Read three numbers a,b,c
Step-3 If a&gt;b then go to step-5
Step-4 IF b&gt;c THEN
print b is largest
ELSE
print c is largest
ENDIF
GO TO Step-6
Step-5 IF a&gt;c THEN
print a is largest
ELSE
print c is largest
ENDIF
Step-6 Stop
</pre>

<p>
	والمخطط الانسيابي للتعبير عن الخوارزمية هو كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="128494" href="https://academy.hsoub.com/uploads/monthly_2023_06/13.png.eff1425c98f71ed294ae51d35118ebb2.png" rel=""><img alt="مخطط انسيابي flowchart للخوارزميات" class="ipsImage ipsImage_thumbnailed" data-fileid="128494" data-ratio="190.48" data-unique="1j4mj0h52" style="width: 315px; height: auto;" width="315" src="https://academy.hsoub.com/uploads/monthly_2023_06/13.thumb.png.dba0c22da91e75e5a95af441dbe68bc3.png"> </a>
</p>

<p>
	لعلك لاحظت أن الطريقة أعلاه تتطلب اختبار الكثير من الشروط حتى نصل لقرار حول العدد الأكبر من بين الأعداد الثلاثة، دعنا نحاول حلها بطريقة أبسط.
</p>

<p>
	كما تلاحظ فإن خوارزمية مقارنة ثلاثة أعداد حتى نصل لقرار حول العدد الأكبر من بين الأعداد الثلاثة تطلبت إجراء خطوتين والمرور مرتين على كل عدد، ولكن ماذا لو كان لدينا 10 أعداد أو أكثر ستزداد الخطوات ومرات المرور على الأعداد وسيزاد زمن الحل وتعقده لذا من الأفضل أن نفكر بطريقة أفضل تمكننا من المرور مرة واحدة على الأعداد ومعرفة العدد الأكبر من بينها.
</p>

<p>
	يمكن أن نخزن الأعداد في بنية واحدة مثل المصفوفة ثم نقرأها واحدًا تلو الآخر ونعتبر أن أول عدد هو الأكبر من بين كل الأعداد وكلما قرأنا عددًا جديدًا نقارنه بالذي قبله مباشرة فإن كان أكبر منه نبدل بينهما وإن كان أصغر منه أو يساويه لا نقوم بأي إجراء. لنطبق هذه الطريقة لإيجاد العدد الأكبر من بين ثلاثة أعداد
</p>

<p>
	<strong>طريقة 2</strong>:
</p>

<ul>
	<li>
		<p>
			<strong>الدخل</strong>: 3 أرقام a و b و c
		</p>
	</li>
	<li>
		<p>
			<strong>الخرج</strong>: max الذي يمثل العدد الأكبر
		</p>
	</li>
	<li>
		<p>
			<strong>المتن</strong>:
		</p>

		<ul>
			<li>
				<p>
					الخطوة 1: أدخل الأعداد الثلاثة وخزنها في a و b و c على التوالي
				</p>
			</li>
			<li>
				<p>
					الخطوة 3: افترض أن العدد الأول a هو الأكبر max = a
				</p>
			</li>
			<li>
				<p>
					الخطوة 4: إذا كان العدد الثاني b أكبر من max اجعل max = b
				</p>
			</li>
			<li>
				<p>
					الخطوة 5: إذا كان العدد الثالث c أكبر من أكبر من max اجعل max = c
				</p>
			</li>
			<li>
				<p>
					الخطوة 6: اعرض قيمة max
				</p>
			</li>
		</ul>
	</li>
</ul>

<p>
	الكود الزائف للتعبير عن هذه الخوارزمية لإيجاد أكبر عدد من بين 3 أعداد:
</p>

<pre class="ipsCode">Step-1 Start
Step-2 Read three numbers: a,b,c
Step-3 max = a
Step-4 IF b &gt; max THEN
max = b
ENDIF
Step-5 IF c &gt; max THEN
max = c
ENDIF
Step-6 print max
Step-7 Stop
</pre>

<p>
	والمخطط الانسيابي للتعبير عن الخوارزمية هو كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="128493" href="https://academy.hsoub.com/uploads/monthly_2023_06/23.png.34c74ee85f97aab0e990fd319fe9ef67.png" rel=""><img alt="مخطط انسيابي للتعبير عن الخوارزميات" class="ipsImage ipsImage_thumbnailed" data-fileid="128493" data-ratio="52.11" data-unique="kg96w02n2" style="width: 900px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2023_06/23.thumb.png.d3085e365274b20fa061d45218f38bb2.png"> </a>
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		Quote
	</div>

	<p data-gramm="false">
		هذه الطريقة هي ما نتبعه نحن البشر عادةً فإن طلب منك معرفة أكبر رقم من مجموعة أعداد فستمر عليها من البداية وحتى النهاية وتحفظ (تُخزن في ذاكرتك) العدد الأكبر الذي يمر معك لتقوله في النهاية.
	</p>
</blockquote>

<p>
	هل لديك خوارزمية أخرى أفضل لحل هذه المسألة؟ يمكن أن تشاركنا إياها لنتناقش حولها.
</p>

<p>
	كما تلاحظ فقد مررنا مرة واحدة على الأعداد وعرفنا أيها أكبر، وهذا لن يختلف لو كان لدينا قائمة تحوي 100 عدد أو أكثر، هذا المفهوم ببساطته يسمى بكفاءة الخوارزمية أو تعقيد الخوارزمية والذي يشار إليه برمز Big O وستجده دومًا في أي موضوع يتناول الخوارزميات، لأن من شروط كتابة الخوارزميات أن تكون ذات كفاءة وأقل تعقيدًا ممكنًا.
</p>

<p>
	فكل الخوارزميات تعطي في النهاية الخرج الصحيح نفسه ولكن تتميز عن بعضها بكفاءة وكيفية وسرعة الوصول للخرج الصحيح، وهنا تبرز أهمية التفكير المنطقي وضرورة دراسة الخوارزميات و<a href="https://academy.hsoub.com/programming/general/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-data-structures/" rel="">بنى المعطيات</a> لمعرفة الحل الأكثر كفاءة لحل أي مسألة برمجية تواجهها وسنتحدث عن هذا الموضوع بالتفصيل تاليًا في فقرة "تحليل الخوارزميات".
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			هل ترغب في تطوير موقع إلكتروني احترافي؟
		</p>

		<p class="banner-subtitle">
			وظّف مطور ويب خبير لبرمجة موقعك من مستقل
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://mostaql.com/freelancers/web-developer" rel="external">أضف مشروعك الآن</a>
		</div>
	</div>
</div>

<h2>
	تحويل الخوارزمية إلى برنامج حاسوبي
</h2>

<p>
	إن طرق تمثيل الخوارزمية مفهومة وواضحة لنا كبشر لكنها في الواقع غير قابلة للاستخدام والتطبيق على الحاسوب، لأنّ الخوارزميات خطط عامة ومجرّدة لحل المشاكل.
</p>

<p>
	فإن أردنا استخدام الخوارزمية لحل مشكلة ما، فسيكون علينا ترجمتها أو التعبير عنها بإحدى <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> كي يفهمها الحاسوب ويعيد لنا النتائج المطلوبة، هذه العملية تُسمّى تحقيق الخوارزمية أو تنفيذ الخوارزمية implementation وينتج عن تحويل الخوارزمية إلى إحدى لغات البرمجة شيفرة برمجية قابلة للتنفيذ execution على الحاسوب.
</p>

<p>
	لنضرب مثالًا على ذلك سنحول الخوارزمية التي ذكرناها آنفًا والتي تحاول إيجاد أكبر عدد من بين ثلاثة أعداد إلى برنامج مكتوب بلغة بايثون حتى يتسنّى لنا تنفيذها على الحاسوب.
</p>

<h3>
	تنفيذ خوارزمية إيجاد أكبر عدد من بين ثلاثة أعداد في لغة بايثون
</h3>

<p>
	برنامج بايثون لإيجاد أكبر عدد من بين ثلاثة أعداد
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9768_8" style=""><span class="pln">a </span><span class="pun">=</span><span class="pln"> int</span><span class="pun">(</span><span class="pln">input</span><span class="pun">(</span><span class="str">'a= '</span><span class="pun">))</span><span class="pln">
b </span><span class="pun">=</span><span class="pln"> int</span><span class="pun">(</span><span class="pln">input</span><span class="pun">(</span><span class="str">'b= '</span><span class="pun">))</span><span class="pln">
c </span><span class="pun">=</span><span class="pln"> int</span><span class="pun">(</span><span class="pln">input</span><span class="pun">(</span><span class="str">'c= '</span><span class="pun">))</span><span class="pln">
max </span><span class="pun">=</span><span class="pln"> a
</span><span class="kwd">if</span><span class="pln"> b </span><span class="pun">&gt;</span><span class="pln"> max </span><span class="pun">:</span><span class="pln">
    max </span><span class="pun">=</span><span class="pln"> b
</span><span class="kwd">if</span><span class="pln"> c </span><span class="pun">&gt;</span><span class="pln"> max </span><span class="pun">:</span><span class="pln">
    max </span><span class="pun">=</span><span class="pln"> c
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">max</span><span class="pun">,</span><span class="pln"> </span><span class="str">"هو العدد الأكبر"</span><span class="pun">)</span></pre>

<p>
	يمكنك الآن تنفيذ هذه الشيفرة على أيّ حاسوب وستعمل كما هو متوقع. كما يمكنك بالطبع تحقيق الخوارزمية بأيّ لغة برمجة أخرى تريدها مثل C أو C++‎ أو جافا أو جافا سكريبت أو R …إلخ.
</p>

<p>
	تمرين ما رأيك أن تجرب تحويل الخوارزمية السابقة إلى برنامج حاسوبي يطلب من المستخدم إدخال مجموعة أعداد تفصل بينها فراغات مثل 4 9 8 7 10 ليحللها البرنامج بتلك الخوارزمية ويعطي العدد الأكبر من بينها (مساعدة: ستحاول استعمال حلقات التكرار loops)
</p>

<h2>
	مجالات استخدام الخوارزميات
</h2>

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

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

<p>
	وكل البرامج والتطبيقات وأنظمة التشغيل تعتمد على الخوارزميات وتستخدمها من أجل <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">معالجة البيانات</a> وإجراء الحسابات وحلّ مشكلات العمل كما تستخدم الخوارزميات في عدة مجالات أخرى مثل الفيزياء وتحليل اللغات والاقتصاد وغيرها من مناحي الحياة وهي تطبق اليوم في كل شيء نستخدمه من حولنا ومن أبرز الأمثلة على مجالات استخدام الخوارزميات في حياتنا اليومية نذكر:
</p>

<ul>
	<li>
		خوارزميات التواصل الاجتماعي التي تتحكم فيما تراه على مواقع التواصل الاجتماعي خاصتك
	</li>
	<li>
		خوارزميات محركات البحث التي تحسن عمليات البحث وتتنبأ بما سيكتبه المستخدمون.
	</li>
	<li>
		خوارزميات كشف الجرائم باستخدام تقنيات التعرف على الوجه ورسم الخرائط التنبؤية لتوقع الجرائم واتخاذ الإجراءات لمنعها.
	</li>
	<li>
		خوارزميات التشفير التي تقوم بتحويل نص مفهوم إلى نص غير مفهوم وتُستخدم لحماية البيانات المهمة بحيث لا يفهمها إلّا الأشخاص الذين وجهت إليهم.
	</li>
	<li>
		خوارزميات التنبؤ بحالة الطقس ودرجة الحرارة بمساعدة البيانات السابقة وهي تفيد في عدة مجالات أهمها الزراعة والطيران والاتصالات والحروب.
	</li>
	<li>
		خوارزميات تحديد مواعيد وصول الحافلات وجدولتها بأفضل طريقة.
	</li>
	<li>
		خوارزميات تحديد مواقف السيارات المتاحة لمساعدة السائقين على العثور على وجهتم بسرعة وسهولة.
	</li>
	<li>
		خوارزميات توجيه المركبات إلى أفضل طريق.
	</li>
	<li>
		خوارزميات تعديل مسارات الصواريخ.
	</li>
</ul>

<p>
	وغيرها الكثير من مجالات الحياة المختلفة في الاقتصاد والعلوم والإعلام وما شابه التي تلعب فيها الخوارزميات دورًا فعالًا وحيويًا، على سبيل المثال عرضت نيتفليكس جائزة قدرها مليون دولار لمن يطور خوارزميتها المستخدمة في اقتراح الأفلام والمسلسلات على الزوار ويجعلها أفضل بنسبة 10%.
</p>

<h2>
	مواصفات الخوارزمية الجيدة
</h2>

<p>
	قد تتساءل وهل هناك خوارزمية جيدة وأخرى ليست كذلك؟ وما الذي يجعل الخوارزمية جيدة؟ والجواب هو نعم فكما أنّ هناك وصفات طبخة سيئة ولا تنجح، فكذلك هناك خوارزميات سيئة وغير عملية ولا تصلح للتطبيق، فليست كل سلسلة متتابعة من الخطوات والتعليمات خوارزمية، بل لابد أن تتوفر فيها شروط خاصة تجعلها عملية وقابلة للتطبيق مثل:
</p>

<ul>
	<li>
		<p>
			<strong>الوضوح</strong>: ينبغي أن تكون كل خطوة من خطوات الخوارزمية واضحة ومفهومة ولا لبس فيها.
		</p>
	</li>
	<li>
		<p>
			<strong>المحدودية</strong>: يجب أن تكون الخوارزمية محدودة، أي أن تكون خطواتها منتهية، وتُنفّذ في مدة زمنية منتهية. فإن كان عدد الخطوات غير منته، أو كان بإمكان إحدى الخطوات أن تستغرق مدة لا منتهية، فإنّها ليست خوارزمية.
		</p>
	</li>
	<li>
		<p>
			<strong>البساطة والواقعية</strong>: ينبغي أن تكون الخوارزمية قابلة للتطبيق بالموارد والتقنيات المتاحة، ولا ينبغي أن تعتمد على تقنية مستقبلية أو غامضة.
		</p>
	</li>
	<li>
		<p>
			<strong>قابلية التطبيق</strong>: يعني الاستقلالية عن لغات البرمجة أي لا ينبغي أن تكون الخوارزمية مرتبطة بلغة برمجة محددة، ويجب أن تكون مجردة وعامّة بحيث تركز على العمل الأساسي للبرنامج بدلًا من التركيز على خصائص لغة برمجة معينة وبعدها يمكن تطبيقها عبر أي لغة برمجة.
		</p>
	</li>
	<li>
		<p>
			<strong>صحة النتائج</strong>: يجب أن تقوم الخوارزمية بتنفيذ المهمة المطلوبة منها دون أخطاء في التنفيذ أو عدم دقة في النتائج.
		</p>
	</li>
	<li>
		<p>
			<strong>الكفاءة والفعالية</strong>: ينبغي أن تستخدم الخوارزمية أفضل الطرق لحل المشكلة بأسرع وقت تنفيذ أو ما يعرف بالتعقيد الزماني للخوارزمية وأقل مساحة ذاكرة أو ما يعرف بالتعقيد المكاني للخوارزمية.
		</p>
	</li>
</ul>

<h2>
	هل أحتاج إلى معرفة الرياضيات لتعلم الخوارزميات؟
</h2>

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

<p>
	لكن إن كنت تقصد بمعرفة الرياضيات أنّه ينبغي أن تكون لك شهادة جامعية في الرياضيات، أو تكون لك معرفة عميقة أو حتى متوسطة بها، فالجواب سيكون لا لأنّ الخوارزميات لا تتطلب معرفة عميقة بمفاهيم الرياضيات.
</p>

<p>
	تذكر أنّ الخوارزميات هي طرق عامة لحل المشاكل والمسائل الحسابية، لذلك فمقدار الرياضيات التي تحتاجها يتعلق بنوع المشكلة التي تريد حلها، إن كنت تريد حل مشكلة معقدة، مثل التنبؤ بالمناخ، فعلى الأرجح أنّ الخوارزمية ستكون معقدة وتحتاج إلى معرفة متقدمة بالرياضيات لكن لا تدع هذا يخيفك ويصدّك عن تعلم الخوارزميات، إذ أنّ معظم المسائل التي ستُواجهها هي مشاكل لا تحتاج إلا إلى معرفة بسيطة بأساسيات الرياضيات، مثل العمليات الحسابية البسيطة.
</p>

<p>
	خلاصة القول هو أنك لا تحتاج إلى تعلم الرياضيات لكي تتعلم الخوارزميات، فما دمت تفهم كيف تجري العمليات الحسابية البسيطة، مثل الجمع والضرب، وبعض المفاهيم البسيطة، مثل المجموعات والدوال وطرق حل المعادلات فيمكنك أن تتعلم الخوارزميات بسهولة.
</p>

<p>
	لكن النقطة الأهم هي الارتباط بين <a href="https://academy.hsoub.com/programming/general/%D8%A3%D9%87%D9%85%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%81%D9%83%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D9%86%D8%B7%D9%82%D9%8A-%D9%81%D9%8A-%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r2095/" rel="">التفكير المنطقي</a> أو <a href="https://academy.hsoub.com/programming/general/%D8%AD%D9%84-%D8%A7%D9%84%D9%85%D8%B4%D9%83%D9%84%D8%A7%D8%AA-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%A7%D8%AD%D8%AA%D8%B1%D8%A7%D9%81-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r760/" rel="">طرق حل المشكلات</a> وبين الخوارزميات والبرمجة، فالتفكير المنطقي هو بلا شك مهارة ضرورية للبشر عمومًا وللمبرمجين على وجه الخصوص ومن الضروري تعليمه للأطفال من سن مبكرة لمنحهم هذه المهارة المهمة والضرورية جدًا لنجاحهم في مستقبلهم المهني.
</p>

<p style="text-align: center;">
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="506" id="ips_uid_8416_6" src="https://academy.hsoub.com/applications/core/interface/index.html" title="التفكير المنطقي في البرمجة" width="900" data-embed-src="https://www.youtube.com/embed/LC3O7f6MKMI"></iframe>
</p>

<h2 id="algorithms-complexity">
	تحليل الخوارزميات
</h2>

<p>
	الخوارزمية هي في الأصل خطة لحل مشكلة حسابية فإن لم تكن تملك خطة مُسبقة لحل مشكلة معقدة، فستفشل محاولاتك على الأرجح لحل تلك المشكلة وحتى لو كانت المشكلة بسيطة، فقد يكون هناك العديد من الحلول، و<strong>بعض هذه الحلول أسرع وأكفأ من بعض</strong>.
</p>

<p>
	من هنا تأتي أهمية تحليل الخوارزميات الذي يهتم بدراسة كفاءة الخوارزميات من ناحية الوقت والذاكرة التي يحتاجها تنفيذ الخوارزمية واختيار الحل الأبسط والأسرع والأقل استهلاكًا للموارد، أو بلغة الحوسبة، تريد أن تخفض <a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-algorithms-complexity-r1284/" rel="">تعقيد الخوارزمية Algorithms Complexity</a> إلى أقصى حد ممكن.
</p>

<p>
	يشمل تعقيد الخوارزمية كل الموارد الضرورية لحل المشكلة، إن كانت الخوارزمية خوارزميةَ طبخ، فإنّ تعقيدها سيكون الوقت والطاقة اللازمة لإعداد الوجبة. أما إن كانت الخوارزمية تسعى لحل مشكلة في الرياضيات، سيكون تعقيدها هو الوقت اللازم لإجراء خطوات الخوارزمية.
</p>

<p>
	أما إن كانت الخوارزمية حاسوبية، فإنّ تعقيدها سيكون الوقت ومساحة الذاكرة الضروريان لتنفيذ خطوات الخوارزمية. لهذا السبب ابتكر العلماء فرعًا كاملًا في علم الخوارزميات مُخصّصًا لتقدير تعقيد الخوارزميات يُسمّى <strong>نظرية التعقيد</strong>، والذي يصنّف الخوارزميات إلى أصناف بحسب تعقيدها الزماني Time Complexity الذي يصف مقدار الوقت الذي يستغرقه تنفيذ الخوارزمية. وتعقيدها المكاني أو ما يسمى بتعقيد المساحة Space Complexity الذي يمثل عدد خلايا الذاكرة اللازمة لتنفيذ عمليات الخوارزمية مع استثناء المساحة المخصصة لدخل الخوارزمية.
</p>

<p>
	يتم التعبير عن تعقيد الخوارزمية بتدوين خاص يسمى <strong>Big O notation</strong> وهو طريقة لوصف تعقيد الخوارزمية الزمني باستخدام مصطلحات جبرية وأدنى هذه الأصناف هو الصنف O(1)‎، والذي يعني أنّ الخوارزمية تستغرق وقتا ثابتًا لحل المشكلة مهما كان حجم الدخل كما في خوارزمية معرفة كون العدد فردي أم زوجي مثلًا أو خوازرمية طباعة أول رقم من بين قائمة من الأرقام، أما الصنف O(n)‎ فَيعني أنّ مدة تنفيذ الخوارزمية متناسبة مع حجم الدخل n كما في خوارزمية حساب مضروب العدد وخوازمية إيجاد أكبر عدد من بين مجموعة من الأعداد.
</p>

<p>
	وإليك قائمة مختصرة بأشهر أنواع تعقيد الخوارزميات وفق تدوين Big O ودلالة كل منها:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="128498" href="https://academy.hsoub.com/uploads/monthly_2023_06/2018896428_.png.f593dff431d20abc3cf5666e44d33fe9.png" rel=""><img alt="تعقيد الخوارزمية Big O" class="ipsImage ipsImage_thumbnailed" data-fileid="128498" data-ratio="61.56" data-unique="fcdhgeiab" style="width: 900px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2023_06/.thumb.png.318162d7f31c46f736cf234facf641e0.png"> </a>
</p>

<ul>
	<li>
		<p>
			O(1)‎ تعقيد زمني ثابت: أي تستغرق الخوارزمية نفس الزمن مهما كان حجم الدخل.
		</p>
	</li>
	<li>
		<p>
			O(n)‎ تعقيد زمني خطي: أي يتناسب زمن تنفيذ الخوارزمية بشكل خطي مع حجم الدخل، بمعنى آخر إذا  كان حجم الدخل n فإن عدد الخطوات المطلوب لحلها سيكون n على الأكثر.
		</p>
	</li>
	<li>
		<p>
			O(sqrt(n)) تعقيد جذر تربيعي: أي إذا كان حجم دخل الخوازرمية هو n سوف يتناسب زمن تنفيذ الخوارزمية مع الجذر التربيعي لقيمة الدخل.
		</p>
	</li>
	<li>
		<p>
			O(n^c)‎ تعقيد كثير الحدود: يتناسب زمن تنفيذ الخوارزمية مع حجم الدخل مرفوع للأس c وله أنواع فقد يكون تعقيد زمني تربيعي O (n^²)‎ أي يتناسب زمن تنفيذ الخوارزمية مع مكعب حجم الدخل أو تعقيد زمني تكعيبي O(n^3)‎ أي يتناسب زمن تنفيذ الخوارزمية مع مكعب حجم الدخل.
		</p>
	</li>
	<li>
		<p>
			O(log n)‎ تعقيد لوغاريتمي: تناسب زمن تنفيذ الخوارزمية مع لوغاريتم حجم الدخل.
		</p>
	</li>
	<li>
		<p>
			O(n log n)‌‎ تعقيد لوغاريتمي خطي: وهو أبطأ قليلاً من الخطي
		</p>
	</li>
	<li>
		<p>
			O(2^n)‎ تعقيد أسي: وفيه تتضاعف خطوات الخوارزمية بشكل أسي مع زيادة حجم الدخل.
		</p>
	</li>
	<li>
		<p>
			O(n!)‎ تعقيد عاملي: أي يتناسب زمن الخوارزمية مع قيمة عاملي الدخل أي ضرب جميع الأعداد الصحيحة الموجبة الأصغر من قيمة الدخل. 
		</p>
	</li>
</ul>

<p>
	يمكنك أن تجد حلًا للمشاكل بدون الخوارزميات، لكن من النادر أن يكون ذلك الحل الذي وجدته هو الحل الأفضل والأكفأ، وهذه فائدة تحليل الخوارزميات فهو يمكّنك من العثور على الحل الأمثل والأنسب والأقل استهلاكًا للموارد.
</p>

<p>
	للمزيد من المعلومات أنصح بمطالعة مقال <a href="https://academy.hsoub.com/programming/advanced/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1345/" rel="">مدخل إلى تحليل الخوارزميات</a> ومقال <a href="https://academy.hsoub.com/programming/advanced/%D8%AF%D9%84%D9%8A%D9%84-%D8%B4%D8%A7%D9%85%D9%84-%D8%B9%D9%86-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A9-r1247/" rel="">الدليل الشامل عن تعقيد الخوارزميات</a>
</p>

<h2>
	أنواع الخوارزميات البرمجية
</h2>

<p>
	الخوارزميات هي خطط عامة لحل المشاكل وتتنوع أساليب وأنواع الخوارزميات بحسب الأسلوب الذي تتبعه الخوارزمية لحل المشكلة أو بحسب نوع المشكلة التي تحلها، وبناء على ذلك يمكن أن نجد أنواعًا مختلفة من الخوارزميات ومن أبرزها:
</p>

<ul>
	<li>
		<a href="https://wiki.hsoub.com/Algorithms/Brute_force" rel="external">خوارزميات القوة الغاشمة Brute force algorithms </a>: تحاول الخوارزميات من هذا النوع حل المشكلة بطريقة مباشرة وتمر بجميع الخيارات الممكنة حتى تتمكن من العثور على حل لهذه المشكلة.
	</li>
	<li>
		<a href="https://wiki.hsoub.com/Algorithms/Greedy_Algorithms" rel="external">الخوارزميات الجشعة Greedy algorithms</a>: تحاول الخوارزميات الجشعة حل المشكلة خطوة فخطوة، بحيث تقترب رويدًا رويدًا من الحل العام للمشكلة.
	</li>
	<li>
		<a href="https://wiki.hsoub.com/Algorithms/Dynamic_Programming" rel="external">خوارزميات البرمجة الديناميكية Dynamic Programming</a>: تقسّم خوارزميات البرمجة الديناميكية المشكلة إلى مشاكل فرعية أبسط، ثمّ تحل تلك المشاكل الفرعية لاستنتاج الحل النهائي
	</li>
	<li>
		<a href="https://wiki.hsoub.com/Algorithms/Divide_And_Conquer" rel="external">خوارزميات فرق تسد Divide and conquer algorithms</a>: تقسِّم خوارزميات فرِّق تسد المسألة إلى مسائل فرعية تشبه المسألة الأصلية، ثمّ تحلها وتدمج الحلول لتقديم حلٍّ المسألة الأصلية.
	</li>
	<li>
		<a href="https://wiki.hsoub.com/Algorithms/Backtracking" rel="external">خوارزميات التعقب الخلفي Backtracking algorithms</a>: تحاول خوارزميات التعقب الخلفي حل المشكلة تعاوديًا عبر بناء الحل تصاعديًا خطوة فخطوة، مع حذف الحلول التي لا تستجيب للقيود التي تفرضها المسألة المُراد حلها في أيّ وقت أثناء تنفيذ الخوارزمية.
	</li>
	<li>
		<a href="https://wiki.hsoub.com/Algorithms/Sorting_Algorithms" rel="external">خوارزميات الترتيب Sort algorithms</a>: هي خوارزميات ترتب مجموعة من العناصر القائمة في ترتيب معين رقمي أو هجائي. والفرز هو أحد الخطوات الهامة في الخوارزميات الأكثر تعقيدًا، توجد عدة خوارزميات تمكننا من تحقيق عملية الفرز ولكل منها ميزاتها ومحدوديتها.
	</li>
	<li>
		<a href="https://wiki.hsoub.com/Algorithms/Searching_Algorithms" rel="external">خوارزميات البحث Search algorithms</a>: هي خوارزميات تقوم بتحديد موقع بيانات محددة بين مجموعة من البيانات أي أنها تبحث عن البيانات المخزنة ضمن بعض الهياكل أو بنى البيانات وتقوم باستردادها.
	</li>
	<li>
		خوارزميات التعلم الآلي: هي خوارزميات تحاول التعلم بناءً على مجموعة من حالات اتخاذ القرار السابقة كي تتمكن من اتخاذ قرارات معقدة بناءً عليها.
	</li>
	<li>
		خوارزميات التشفير: هي الخوارزميات التي تقوم بتحويل نص مقروء إلى نص غير مقروء يُعرف باسم النص المشفر بحيث يمكن للأطراف المصرح لهم فقط بفهم المعلومات الموجودة في هذا النص وهي خوارزميات هامة جدًا في مجال أمن البيانات الحساسة والحفاظ على الخصوصية.
	</li>
</ul>

<p>
	كانت تلك بعض أنواع وتصنيفات الخوارزميات الأكثر شيوعًا وهناك بالطبع العديد من الخوارزميات الأخرى للعديد من الحالات المختلفة.
</p>

<p>
	هناك أيضًا طرق أخرى يمكن تطبيقها بسهولة في التطبيقات مثل <a href="https://academy.hsoub.com/programming/advance/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A9-%D8%AF%D9%8A%D9%83%D8%B3%D8%AA%D8%B1%D8%A7-dijkstra%E2%80%99s-algorithm-r1336/" rel="">خوارزمية Dijkstra</a> و <a href="https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D8%B3%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%A3%D8%B4%D8%AC%D8%A7%D8%B1-r1373/" rel="">Cycle Detection</a> و Kruskal Minimum Spanning Trees فهي من الخوارزميات الأساسية للمبتدئين للتعلم منها.
</p>

<h2>
	أهمية الخوارزميات في البرمجة
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="128499" href="https://academy.hsoub.com/uploads/monthly_2023_06/1311952358_.png.be60ff3015237a5d1f182c68ce40d43c.png" rel=""><img alt="الخوارزميات والبرمجة" class="ipsImage ipsImage_thumbnailed" data-fileid="128499" data-ratio="62.63" data-unique="30o1wkb4w" style="width: 800px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2023_06/.thumb.png.520dea0b8b99ef96a7b1379320de5067.png"></a>
</p>

<p>
	هناك من قد يقول أنّ تعلم الخوارزميات تَرفٌ، وهو غير ضروري لكتابة البرامج والتطبيقات، وأنّه يمكن للمبرمج أن يكتب برامجه مباشرة دون الحاجة إلى مفاهيم الخوارزميات.
</p>

<p>
	صحيح أنّه ليس عليك أن تكون خبيرًا في الخوارزميات لتَكون مبرمجًا، لكن لا يمكنك أن تكون مبرمجًا بارعًا ومحترفًا دون أن تتعلم فن تصميم الخوارزميات.
</p>

<p>
	يوفر تعلم الخوارزميات للمبرمج العديد من الفوائد أبرزها:
</p>

<ul>
	<li>
		القدرة على حل المشكلات بشكل أفضل
	</li>
	<li>
		الاستخدام الفعال للموارد الحاسوبية
	</li>
	<li>
		يوفر وقت البرمجة
	</li>
	<li>
		يجعل منك مبرمجًا أفضل
	</li>
</ul>

<p>
	لنناقش كل فائدة منها بمزيد من التفصيل ونتعرف كيف يسهم تعلم الخوارزميات في تعزيزها.
</p>

<h3>
	القدرة على حل المشكلات بشكل أفضل
</h3>

<p>
	سيحسن تعلمك لطرق حل الخوارزميات من قدرتك على حل المشكلات بصورة عامة والمشكلات البرمجية على وجه الخصوص، ويكسبك مرونة ذهنية في التفكير في الحلول ويساعدك لاحقًا في كتابة البرامج الحاسوبية المتنوعة أيًا كانت اللغة البرمجية المستخدمة خصوصًا بأنّ الخوارزميات مستقلة عن لغات البرمجة، لذا يمكنك استخدامها مهما كانت لغة البرمجة التي تعمل عليها.
</p>

<h3>
	الاستخدام الفعال للموارد
</h3>

<p>
	يضمن لك اختيار الخوارزمية الصحيحة الاستخدام المناسب للموارد مثل الذاكرة والتخزين والشبكة وغيرها. فلا يكفي أن تكون الفكرة التي بنيت عليها البرنامج أو التطبيق مفيدة ومبتكرة، ينبغي أن تكون عملية بحيث ينفّذها الحاسوب أو الجوال في وقت معقول وموارد محدودة، فما فائدة تطبيق رسم يحتاج دقيقة كاملة ليرسم مربعًا، وما فائدة برنامج يحتاج إلى 10 جيجابايت من الذاكرة ليعمل!
</p>

<h3>
	توفير وقت البرمجة
</h3>

<p>
	يوفر عليك تعلم الخوارزميات الكثير من الوقت، إذ أنّ معظم المشاكل والمسائل التي قد تعترضك أثناء كتابة البرامج لها حلول جاهزة على هيئة خوارزميات مُحسّنة وسريعة، ما يغنيك عن إعادة اختراع العجلة في كل مرة تعترضك مشكلة ما، لأنّ هناك من حلّها قبلك على الأرجح وأعدّها على هيئة خوارزمية وكل ما عليك فعله هو تحويلها إلى لغة البرمجة التي تستعملها ثم تنفّذها وقد لا تضطر حتى إلى تنفيذها فمعظم الخوارزميات الشهيرة والفعالة مثل خوارزميات البحث والفرز تدمج في لغات البرمجة بشكل مكتبات جاهزة يمكنك استخدامها في شيفراتك البرمجية.
</p>

<h3>
	الخوارزميات تجعل منك مبرمجًا أفضل
</h3>

<p>
	تعلم الخوارزميات سيجعلك مبرمجًا أفضل وأكثر احترافية، فسَواء كنت متخصصًا في تطبيقات الجوال أو تطبيقات سطح المكتب، أو في <a href="https://sndian.com/" rel="external">بناء المواقع</a> أو تصميم الألعاب أو غيرها من <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AC%D8%A7%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">مجالات البرمجة</a>، فإنّ تعلم الخوارزميات سيوسع أفق تفكيرك البرمجي ويساعدك على تطوير برامج أجود وأسرع وأكثر موثوقية.
</p>

<h2>
	أهم مصادر تعلم الخوارزميات
</h2>

<p>
	هناك للأسف ضعف في المحتوى العربي التقني، هذا الضعف يظهر أكثر ما يظهر في مجالات الخوارزميات. ولسدّ هذا القصور في المحتوى العربي فقد وفرت أكاديمية حسوب العديد من مصادر التعلم القيمة باللغة العربية.
</p>

<p>
	فنظرًا للأهمية الكبيرة للخوارزميات في كافة مجالات البرمجة، وفي غيرها من المجالات مثل الرياضيات والتنمية وغيرها، فقد وفرت لك أكاديمية حسوب <a href="https://academy.hsoub.com/store/7-%D8%AF%D9%88%D8%B1%D8%A9-%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/" rel="">دورة علوم الحاسب</a> والتي تتضمن مسارات متعددة يحتاجها أي مبتدئ في <a href="https://academy.hsoub.com/learn-programming/" rel="">تعلم البرمجة</a> من أهمها مسار الخوارزميات وبنى المعطيات الذي يعلمك خطوة بخطوة كيفية كتابة الخوارزميات وتحليلها ويعرفك على أهم الخوارزميات التي تفيدك في عملك البرمجي مثل خوارزميات البحث والترتيب والخوارزميات الرياضية وخوارزميات الرسوم البيانية وغيرها الكثير.
</p>

<p style="text-align: center;">
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="443" id="ips_uid_8416_7" src="https://academy.hsoub.com/applications/core/interface/index.html" title="دورة علوم الحاسوب - أكاديمية حسوب" width="786" data-embed-src="https://www.youtube.com/embed/EkvUFDevMlM"></iframe>
</p>

<p>
	كما نشرت الأكاديمية سلسلة <a href="https://academy.hsoub.com/tags/%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA%20%D9%84%D9%84%D9%85%D8%AD%D8%AA%D8%B1%D9%81%D9%8A%D9%86/" rel="">الخوارزميات للمحترفين</a> التي تشرح الخوارزميات بالتفصيل وتغطّي كافة مفاهيمها الأساسية مثل مفهوم التعقيد لتقدير الموارد التي تستهلكها البرامج كالوقت والذاكرة. إضافة إلى مفاهيم البرمجة الديناميكية، وبعض أنماط الخوارزميات العامة.
</p>

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

<p>
	كما أن السلسلة تنفذ الخوارزميات التي تستعرضها من خلال العديد من لغات البرمجة، فمهما كانت لغة البرمجة خاصتك، سواء كانت بايثون أو جافا أو C++/C أو #C‏‏ أو جافا سكريبت، فستجد أمثلة بهذه اللغات وغيرها في هذا الكتاب.
</p>

<p>
	وستجد كذلك توثيقًا شاملًا عن <a href="https://wiki.hsoub.com/Algorithms" rel="external">الخوارزميات في موسوعة حسوب</a> يضم توثيقات لأهم الخوارزميات المُستخدمة في البرمجة.
</p>

<p>
	وننصحك كذلك بأن تشترك في <a href="https://www.youtube.com/@HsoubAcademy" rel="external nofollow">قناة أكاديمية حسوب على يوتيوب</a> وستجد فيها الكثير من الدروس المفيدة والمواضيع الشيقة حول البرمجة والخوارزميات والتفكير المنطقي وغيرها الكثير.
</p>

<p style="text-align: center;">
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="450" id="ips_uid_8416_8" src="https://academy.hsoub.com/applications/core/interface/index.html" title="هل يجب تعلم الخوارزميات أولًا أم لغات البرمجة" width="800" data-embed-src="https://www.youtube.com/embed/GXxpQ31kTA8"></iframe>
</p>

<p>
	وأخيرًا إذا كنت تتساءل هل يجب علي كمبتدئ أن أتعلم الخوارزميات أولًا أم أتعلم إحدى لغات البرمجة أولًا فنصيحتي لك أن تبدأ كخطوة أولى بالتعرف على <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">أساسيات البرمجة</a> وبعد التمكن منها يمكنك البدء بتعلم الخوارزميات وتطوير تفكيرك البرمجي والخوارزمي على التوازي في رحلة التعلم حتى تصل للاحتراف في كليهما.
</p>

<p>
	إذا أتقنت تعلم الأساسيات يمكن أن تنتقل لتعلم الأمور المتقدمة وستجد في قسم البرمجة ضمن وتحديدًا في قسم "مقالات برمجة متقدمة" في أكاديمية حسوب مجموعة متنوعة من المقالات المتقدمة التي تزيد معرفتك وخبرتك في مجال الخوارزميات وفي حال واجهك أي سؤال يمكنك طرحه في <a href="https://academy.hsoub.com/questions/" rel="">قسم الأسئلة والأجوبة</a> في الأكاديمية.
</p>

<h2>
	الخلاصة
</h2>

<p>
	تعرفنا في مقال اليوم ما هي الخوارزمية وأهمية الخوارزميات في علوم الحاسب وهندسة البرمجيات، وتعرفنا على خطوات حل الخوارزمية وطرق التعبير عن الخوارزميات بالشيفرة الوهمية والمخططات الانسيابية ثم تحويلها بعد ذلك إلى برامج حاسوبية من خلال الأمثلة العملية البسيطة التي تناسب المبتدئين.
</p>

<p>
	كما تعرفنا في الختام على أبرز الفوائد التي تجنيها كمبرمج من تعلم الخوارزميات، وأهم المراجع العربية التي تشرح الخوارزميات من الصفر للاحتراف، فإذا كنت تسعى لتكون مبرمجًا محترفًا فتعلم الخوارزميات أحد الأمور المهمة التي ينبغي أن تضعها في الحسبان لتساعدك على تحقيق هذا الهدف.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%86-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1410/" rel="">أمثلة عن أنواع الخوارزميات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%87%D9%86%D8%AF%D8%B3%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA/" rel="">تعرف على تخصص هندسة البرمجيات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D9%87%D9%86%D8%AF%D8%B3-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D9%85%D9%86-%D9%87%D9%88-%D9%88%D9%85%D8%A7-%D9%87%D9%8A-%D9%85%D9%87%D8%A7%D9%85%D9%87-r2264/" rel="">مهندس البرمجيات من هو وما هي مهامه</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%8A-%D9%85%D8%AF%D8%A9-%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9%D8%9F-r2257/" rel="">ما هي مدة تعلم البرمجة؟</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1997</guid><pubDate>Tue, 06 Jun 2023 13:00:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641; &#x62A;&#x646;&#x634;&#x626; &#x645;&#x644;&#x641;&#x627; &#x642;&#x627;&#x628;&#x644;&#x627; &#x644;&#x644;&#x62A;&#x646;&#x641;&#x64A;&#x630; Executable File &#x645;&#x646; &#x634;&#x64A;&#x641;&#x631;&#x629; &#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; &#x645;&#x635;&#x62F;&#x631;&#x64A;&#x629;</title><link>https://academy.hsoub.com/programming/advanced/%D9%83%D9%8A%D9%81-%D8%AA%D9%86%D8%B4%D8%A6-%D9%85%D9%84%D9%81%D8%A7-%D9%82%D8%A7%D8%A8%D9%84%D8%A7-%D9%84%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-executable-file-%D9%85%D9%86-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%85%D8%B5%D8%AF%D8%B1%D9%8A%D8%A9-r1930/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_03/-----.png.36530fc024a225bf6d2e4218aced2a13.png" /></p>
<p>
	ناقشنا حتى الآن في سلسلة <a href="https://academy.hsoub.com/tags/%D9%85%D8%AF%D8%AE%D9%84%20%D9%84%D8%B9%D9%84%D9%85%20%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/" rel="">مدخل لعلم الحاسوب</a> كيفية <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D9%87%D9%85%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D9%82%D9%8A%D9%82%D9%8A%D8%A9-%D9%81%D9%8A-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r1918/" rel="">تحميل البرنامج في الذاكرة الوهمية</a>، وسنبدأ في هذا المقال بالتعرف على عملية يتعقّبها نظام التشغيل ويتفاعل معها باستخدام استدعاءات النظام هي عملية <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%A3%D9%88%D9%84-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-compilation-%D9%81%D9%8A-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r976/" rel="">التصريف Compiling</a>. سنتعرّف في هذا المقال على الخطوات الثلاث لإنشاء ملف قابل للتنفيذ، ولكن سنبدأ أولًا بالتعرّف على الفرق بين البرامج المُصرَّفة Compiled Programs والبرامج المُفسَّرة Interpreted Programs.
</p>

<h2>
	البرامج المُصرَّفة Compiled Programs والبرامج المُفسَّرة Interpreted programs
</h2>

<p>
	يجب أن يكون البرنامج الذي يمكن تحميله مباشرةً في الذاكرة بصيغة ثنائية binary format، حيث تُسمَّى عملية تحويل الشيفرة البرمجية المكتوبة بلغةٍ مثل <a href="https://academy.hsoub.com/programming/c/" rel="">لغة C</a> إلى ملف ثنائي جاهز للتنفيذ بعملية التصريف التي تُطبَّق باستخدام مصرِّف Compiler، والمثال الأكثر انتشارًا هو المصرِّف gcc.
</p>

<p>
	للبرامج المُصرَّفة بعض العيوب في تطوير البرمجيات الحديثة، إذ يجب استدعاء المصرِّف لإعادة إنشاء الملف القابل للتنفيذ في كل مرة يُجري فيها المطور تعديلًا -لو بسطيًا- وفي المقابل يمكن منطقيًا وبناءً على ذلك تصميم برنامجٍ مُصرَّف يمكنه قراءة برنامج آخر وتنفيذ شيفرته البرمجية سطرًا سطرًا، ونسمي هذا النوع من البرامج المُصرَّفة بالبرامج المُفسَّرة Interpreter لأنها تفسّر كل سطر من ملف الدخل وتنفّذه بوصفه شيفرة برمجية، بحيث لا تكون هناك حاجة لتصريف البرنامج وستظهر أي تعديلات جديدة مضافة في المرة التالية التي يشغّل فيها المفسِّر الشيفرة البرمجية.
</p>

<p>
	تعمل البرامج المفسَّرة عادةً بصورة أبطأ من نظيرتها المُصرَّفة، حيث يمكن مصادفة حِمل البرنامج في قراءة وتفسير الشيفرة البرمجية مرةً واحدة فقط في البرامج المُصرَّفة، بينما يصادف البرنامج المفسَّر هذا الحِمل في كل مرة يُشغَّل فيها. لكن تمتلك اللغات المفسَّرة العديد من الجوانب الإيجابية، حيث تعمل العديد من اللغات المفسرة فعليًا في آلة افتراضية virtual machine مُجرَّدة من العتاد الأساسي. تُعَد لغتا <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">بايثون Python</a> و Perl 6 من اللغات التي تستخدم آلةً افتراضية تفسّر الشيفرة البرمجية.
</p>

<h3>
	الآلات الافتراضية Virtual Machines
</h3>

<p>
	يعتمد البرنامج المُصرَّف كليًا على عتاد الآلة التي يُصرَّف من أجلها، إذ يجب أن يكون هذا العتاد قادرًا على نسخ البرنامج في الذاكرة وتنفيذه، حيث تُعَد الآلة الافتراضية Virtual Machine تجريدًا برمجيًا للعتاد.
</p>

<p>
	تستخدم <a href="https://academy.hsoub.com/programming/java/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-java-%D9%85%D8%A7-%D9%87%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9%D8%9F-r371/" rel="">لغة جافا Java</a> مثلًا نهجًا هجينًا يجمع بين التصريف والتفسير، فهي لغة مُصرَّفة جزئيًا ومُفسَّرة جزئيًا. تُصرَّف شيفرة جافا في برنامج يعمل ضمن <a href="https://academy.hsoub.com/programming/java/%D8%A2%D9%84%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A7%D9%84%D8%A7%D9%81%D8%AA%D8%B1%D8%A7%D8%B6%D9%8A%D8%A9-java-virtual-machine-r964/" rel="">آلة جافا الافتراضية Java Virtual Machine</a> أو يشار إليها JVM اختصارًا، وبالتالي يمكن تشغيل البرنامج المُصرَّف على أيّ عتاد يحتوي على آلة JVM خاصة به، أي يمكنك أن تكتب شيفرتك البرمجية مرة واحدة وتشغّلها في أيّ مكان.
</p>

<h2>
	بناء ملف قابل للتنفيذ
</h2>

<p>
	هناك ثلاث خطوات منفصلة تتضمنها عملية إنشاء ملف قابل للتنفيذ عندما نتحدث عن المُصرِّفات وهذه الخطوات هي:
</p>

<ol>
	<li>
		التصريف Compiling
	</li>
	<li>
		التجميع Assembling
	</li>
	<li>
		الربط Linking
	</li>
</ol>

<p>
	تسمَّى جميع المكونات المتضمنة في هذه العملية بسلسلة الأدوات Toolchain، إذ تكون هذه الأدوات على شكل سلسلة بحيث يكون خرج إحداها دخلًا للأخرى حتى الوصول إلى الخرج النهائي. يأخذ كل رابط في السلسلة الشيفرةَ البرمجية تدريجيًا بحيث تكون أقرب إلى كونها شيفرة برمجية ثنائية مناسبةً للتنفيذ.
</p>

<h3>
	التصريف Compiling
</h3>

<p>
	تتمثل الخطوة الأولى لتصريف ملف مصدري إلى ملف قابل للتنفيذ في تحويل الشيفرة البرمجية من لغة عالية المستوى يفهمها الإنسان إلى شيفرة تجميع Assembly Code تعمل مباشرةً مع التعليمات والمسجلات التي يوفرها المعالج.
</p>

<p>
	تُعَد عملية التصريف أكثر الخطوات تعقيدًا لعدة أسباب أولها أنه لا يمكن التنبؤ بتصرفات البشر، فلديهم شيفرتهم البرمجية الخاصة بأشكال مختلفة. يهتم المصرِّف بالشيفرة البرمجية الفعلية فقط، ولكن يحتاج البشر لأشياء إضافية مثل التعليقات والمسافات البيضاء (الفراغات ومسافات الجدولة Tab والمسافات البادئة وما إلى ذلك) لفهم هذه الشيفرة البرمجية. تسمى العملية التي يتخذها المصرِّف لتحويل الشيفرة البرمجية التي يكتبها الإنسان إلى تمثيلها الداخلي بعملية التحليل Parsing.
</p>

<p>
	هناك خطوة قبل تحليل الشيفرة البرمجية في الشيفرة المكتوبة بلغة C، حيث تسمَّى هذه الخطوة بالمعالجة المُسبَقة أو التمهيدية يقوم بها المعالج المسبق Pre-processor، وهو عبارة عن برنامج لاستبدال النصوص، حيث يُستبدَل مثلًا المتغير <code>variable</code> المُصرَّح عنه بالشكل <code>‎#define variable text</code> بالنص <code>text</code>، ثم تُمرَّر هذه الشيفرة البرمجية المعالَجة مسبقًا إلى المصرِّف.
</p>

<h4>
	الصياغة
</h4>

<p>
	لكل <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغة برمجة</a> صياغة معينة تمثّل قواعد اللغة، بحيث يعرف المبرمج والمصرِّف قواعد الصياغة ليفهما بعضهما البعض ويسير كل شيء على ما يرام. ينسى البشر القواعد أو يكسرونها في أغلب الأحيان، مما يجعل المصرِّف غير قادر على فهم ما يقصده المبرمج، فإن لم تضع قوس الإغلاق لشرط <code>if</code> مثلًا، فلن يعرف المصرِّف مكان الشرط فعليًا.
</p>

<p>
	تُوصَف الصياغة في صيغة باكوس نور Backus-Naur Form -أو BNF اختصارًا- في أغلب الأحيان، وهي لغة يمكنك من خلالها وصف اللغات، والشكل الأكثر شيوعًا منها هو صيغة باكور نور الموسَّعة Extended Backus-Naur Form -أو EBNF اختصارًا- التي تسمح ببعض القواعد الإضافية الأكثر ملاءمة للغات الحديثة.
</p>

<h4>
	توليد شيفرة التجميع Assembly Generation
</h4>

<p>
	وظيفة المصرِّف هي ترجمة <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%B3%D8%AA%D9%88%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغة عالية المستوى higher level language</a> إلى شيفرة تجميع مناسبة للهدف من التصريف، فلكل معمارية مجموعة تعليمات مختلفة وأعداد مختلفة من المسجلات وقواعد مختلفة للتشغيل الصحيح.
</p>

<h5>
	المحاذاة Alignment
</h5>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="121362" href="https://academy.hsoub.com/uploads/monthly_2023_03/01_alignment.png.1b2ec6b98476340ea917c4b1d41f876c.png" rel=""><img alt="01_alignment.png" class="ipsImage ipsImage_thumbnailed" data-fileid="121362" data-unique="824f3spu5" src="https://academy.hsoub.com/uploads/monthly_2023_03/01_alignment.png.1b2ec6b98476340ea917c4b1d41f876c.png"> </a>
</p>

<p>
	تُعَد محاذاة المتغيرات في الذاكرة أمرًا مهمًا للمصرِّفات، إذ يحتاج مبرمجو الأنظمة أن يكونوا على دراية بقيود المحاذاة لمساعدة المصرِّف على إنشاء أكثر شيفرة برمجية فعّالة ممكنة.
</p>

<p>
	لا تستطيع <a href="https://academy.hsoub.com/programming/os-embedded-systems/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B1%D9%83%D8%B2%D9%8A%D8%A9-%D9%88%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r1716/" rel="">وحدات المعالجة المركزية CPU</a> تحميل قيمة في المسجل من موقع ذاكرة عشوائي، إذ يتطلب ذلك أن تحاذي المتغيرات حدودًا معينة. يمكننا أن نرى في الشكل السابق كيفية تحميل قيمة 32 بتًا (4 بايتات) في مسجل على آلة تتطلب محاذاة بمقدار 4 بايتات للمتغيرات.
</p>

<p>
	يمكن تحميل المتغير الأول في المسجل مباشرةً، حيث يقع بين حدود 4 بايتات، ولكن يجتاز المتغير الثاني حدود 4 بايتات، مما يعني أنه ستكون هناك حاجة إلى عمليتي تحميل على الأقل للحصول على المتغير في مسجل واحد إحداهما للنصف السفلي أولًا ثم النصف العلوي.
</p>

<p>
	يمكن لبعض المعماريات مثل معمارية x86 التعاملَ مع عمليات التحميل التي تكون دون محاذاة في العتاد مع انخفاض في الأداء، حيث يطبّق العتاد العمل الإضافي للحصول على القيمة في المسجل، بينما لا يمكن أن يكون هناك انتهاك لقواعد المحاذاة في المعماريات الأخرى وسترفع استثناءً يكتشفه نظام التشغيل الذي يتعين عليه بعد ذلك تحميل المسجل يدويًا على أجزاء، مما يتسبب في مزيد من الحِمل.
</p>

<h5>
	حاشية البنية Structure Padding
</h5>

<p>
	يجب أن يأخذ المبرمجون المحاذاة في الحسبان خاصةً عند إنشاء البنى <code>struct</code>، حيث يمكن للمبرمجين في بعض الأحيان أن يتسببوا في سلوك دون المستوى الأمثل، بينما يعرف المصرِّف قواعد المحاذاة للمعماريات التي يبنيها. ينص معيار C99 على أن البنى ستُرتَّب في الذاكرة بالترتيب المُحدَّد في التصريح نفسه، وستكون جميع العناصر بالحجم نفسه في مصفوفة من البنى.
</p>

<p>
	إليك مثال عن حاشية بنية Struct Padding:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7940_8" style=""><span class="pln">$ cat </span><span class="kwd">struct</span><span class="pun">.</span><span class="pln">c
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> a_struct </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">char</span><span class="pln"> char_one</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">char</span><span class="pln"> char_two</span><span class="pun">;</span><span class="pln">
  </span><span class="typ">int</span><span class="pln"> int_one</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">struct</span><span class="pln"> a_struct s</span><span class="pun">;</span><span class="pln">

  printf</span><span class="pun">(</span><span class="str">"%p : s.char_one\n"</span><span class="pln"> \
    </span><span class="str">"%p : s.char_two\n"</span><span class="pln"> \
    </span><span class="str">"%p : s.int_one\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">s</span><span class="pun">.</span><span class="pln">char_one</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">&amp;</span><span class="pln">s</span><span class="pun">.</span><span class="pln">char_two</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">s</span><span class="pun">.</span><span class="pln">int_one</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

</span><span class="pun">}</span><span class="pln">

$ gcc </span><span class="pun">-</span><span class="pln">o </span><span class="kwd">struct</span><span class="pln"> </span><span class="kwd">struct</span><span class="pun">.</span><span class="pln">c

$ gcc </span><span class="pun">-</span><span class="pln">fpack</span><span class="pun">-</span><span class="kwd">struct</span><span class="pln"> </span><span class="pun">-</span><span class="pln">o </span><span class="kwd">struct</span><span class="pun">-</span><span class="pln">packed </span><span class="kwd">struct</span><span class="pun">.</span><span class="pln">c

$ </span><span class="pun">./</span><span class="kwd">struct</span><span class="pln">
</span><span class="lit">0x7fdf6798</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">char_one
</span><span class="lit">0x7fdf6799</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">char_two
</span><span class="lit">0x7fdf679c</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">int_one

$ </span><span class="pun">./</span><span class="kwd">struct</span><span class="pun">-</span><span class="pln">packed
</span><span class="lit">0x7fcd2778</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">char_one
</span><span class="lit">0x7fcd2779</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">char_two
</span><span class="lit">0x7fcd277a</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">int_one</span></pre>

<p>
	أنشأنا في المثال السابق بنية تحتوي على بايتين من النوع <code>char</code> متبوعين بعدد صحيح بحجم 4 بايتات من النوع <code>int</code>. يضيف المصرِّف حاشية للبنية البنية كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="121363" href="https://academy.hsoub.com/uploads/monthly_2023_03/02_padding.png.2380f1ceab9f4a822e89524f99040f93.png" rel=""><img alt="02_padding.png" class="ipsImage ipsImage_thumbnailed" data-fileid="121363" data-unique="9hl9kd25a" src="https://academy.hsoub.com/uploads/monthly_2023_03/02_padding.png.2380f1ceab9f4a822e89524f99040f93.png"> </a>
</p>

<p>
	نوجّه في المثال السابق المصرِّف إلى عدم حشو البنى، وبالتالي يمكننا أن نرى أن العدد الصحيح يبدأ مباشرةً بعد قيمتين من النوع <code>char</code>.
</p>

<h5>
	محاذاة خط الذاكرة المخبئية Cache line alignment
</h5>

<p>
	تحدثنا سابقًا عن <a href="https://academy.hsoub.com/programming/os-embedded-systems/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B9%D9%85%D9%8A%D9%82%D8%A9-%D8%B9%D9%84%D9%89-%D8%AA%D8%B3%D9%84%D8%B3%D9%84-%D8%A7%D9%84%D8%B0%D9%88%D8%A7%D9%83%D8%B1-%D8%A7%D9%84%D9%87%D8%B1%D9%85%D9%8A-%D9%88%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D8%AE%D8%A8%D8%A6%D9%8A%D8%A9-%D9%81%D9%8A-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r1720/" rel="">استخدام الأسماء البديلة في الذاكرة المخبئية</a>، وكيف يمكن ربط عدة عناوين مع سطر الذاكرة المخبئية نفسه. يجب أن يتأكد المبرمجون من أنهم لا يتسببون في ارتداد Bouncing في خطوط الذاكرة المخبئية عندما يكتبون برامجهم.
</p>

<p>
	يحدث هذا الموقف عندما يصل البرنامج باستمرار إلى منطقتين من الذاكرة ترتبطان مع خط الذاكرة المخبئية نفسه، مما يؤدي إلى هدر هذا الخط، حيث يُحمَّل ويُستخدَم لفترة قصيرة ثم يجب إزالته وتحميل خط الذاكرة المخبئية الآخر في المكان نفسه من الذاكرة المخبئية. يؤدي تكرار هذا الموقف إلى تقليل الأداء بصورة كبيرة، ولكن يمكن تخفيفه من خلال تنظيم البيانات المتعارضة بطرق مختلفة لتجنب تعارض خطوط الذاكرة المخبئية.
</p>

<p>
	إحدى الطرق الممكنة لاكتشاف هذا النوع من المواقف هي التشخيص Profiling الذي يمثّل مراقبة الشيفرة البرمجية لتحليل مساراتها التي يمكن استخدامها والمدة المُستغرقَة لتنفيذها. يمكن للمصرِّف باستخدام التحسين المُوجَّه بالتشخيص Profile Guided Optimization -أو PGO اختصارًا- وضعَ بتات إضافية خاصة من الشيفرة البرمجية في أول ثنائية binary يبنيها ويشغّلها ويسجّل الفروع المأخوذة منها وغير ذلك. يمكنك بعد ذلك إعادة تصريفها مع المعلومات الإضافية لإنشاء ثنائية binary مع أداء أفضل، وإلّا فيمكن للمبرمج أن ينظر إلى خرج عملية التشخيص ويكتشف مواقفًا أخرى مثل ارتداد خط الذاكرة المخبئية.
</p>

<h5>
	المقايضة بين المساحة والسرعة
</h5>

<p>
	يمكن المقايضة مع ما فعله المصرِّف سابقًا باستخدام ذاكرة إضافية لتحسين السرعة عند تشغيل شيفرتنا البرمجية. يعرف المصرِّف قواعد المعمارية ويمكنه اتخاذ قرارات بشأن أفضل طريقة لمحاذاة البيانات عن طريق مقايضة كميات صغيرة من الذاكرة المهدورة لزيادة الأداء أو للوصول إلى الأداء الصحيح فقط.
</p>

<p>
	لا يجب أبدًا -بصفتك مبرمجًا- وضع افتراضات حول طريقة ترتيب المصرِّف للمتغيرات والبيانات، لأنها لا تُعَد قابلةً للنقل، إذ يكون للمعماريات المختلفة قواعدٌ مختلفة ويمكن أن يتخذ المُصرِّف قرارات مختلفة بناءً على أوامر أو مستويات تحسين صريحة.
</p>

<h5>
	وضع الافتراضات
</h5>

<p>
	يجب أن تكون -بصفتك مبرمجًا بلغة C- على دراية بما يمكنك افتراضه بشأن ما سيفعله المصرِّف وما يمكن أن يكون متغيرًا. ذُكِر بالتفصيل ما يمكنك أن تفترضه بالضبط وما لا يمكنك افتراضه في معيار C99، حيث إذا كنت مبرمجًا بلغة C، فلا بد أن يكون التعرف على القواعد جديرًا بالعناء لتجنب كتابة شيفرة برمجية غير قابلة للنقل.
</p>

<p>
	إليك مثال عن محاذاة المكدس Stack Alignment:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7940_10" style=""><span class="pln">$ cat </span><span class="typ">stack</span><span class="pun">.</span><span class="pln">c
</span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="kwd">struct</span><span class="pln"> a_struct </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">int</span><span class="pln"> a</span><span class="pun">;</span><span class="pln">
  </span><span class="typ">int</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
  </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">struct</span><span class="pln"> a_struct s</span><span class="pun">;</span><span class="pln">
  printf</span><span class="pun">(</span><span class="str">"%p\n%p\ndiff %ld\n"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">i</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">s</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pun">)&amp;</span><span class="pln">s </span><span class="pun">-</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pun">)&amp;</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
$ gcc</span><span class="pun">-</span><span class="lit">3.3</span><span class="pln"> </span><span class="pun">-</span><span class="typ">Wall</span><span class="pln"> </span><span class="pun">-</span><span class="pln">o </span><span class="typ">stack</span><span class="pun">-</span><span class="lit">3.3</span><span class="pln"> </span><span class="pun">./</span><span class="typ">stack</span><span class="pun">.</span><span class="pln">c
$ gcc</span><span class="pun">-</span><span class="lit">4.0</span><span class="pln"> </span><span class="pun">-</span><span class="pln">o </span><span class="typ">stack</span><span class="pun">-</span><span class="lit">4.0</span><span class="pln"> </span><span class="typ">stack</span><span class="pun">.</span><span class="pln">c

$ </span><span class="pun">./</span><span class="typ">stack</span><span class="pun">-</span><span class="lit">3.3</span><span class="pln">
</span><span class="lit">0x60000fffffc2b510</span><span class="pln">
</span><span class="lit">0x60000fffffc2b520</span><span class="pln">
diff </span><span class="lit">16</span><span class="pln">

$ </span><span class="pun">./</span><span class="typ">stack</span><span class="pun">-</span><span class="lit">4.0</span><span class="pln">
</span><span class="lit">0x60000fffff89b520</span><span class="pln">
</span><span class="lit">0x60000fffff89b524</span><span class="pln">
diff  </span><span class="lit">4</span></pre>

<p>
	يمكننا أن نرى في المثال السابق المأخوذ من آلة إيتانيوم Itanium أن حاشية ومحاذاة المكدس تغيرت بصورة كبيرة بين إصدارات المصرِّف gcc، وهذا أمر متوقع ويجب على المبرمج مراعاته. كما يجب عليك التأكد من عدم وضع افتراضات حول حجم الأنواع أو قواعد المحاذاة.
</p>

<h5>
	مفاهيم لغة C الخاصة بالمحاذاة
</h5>

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

<p>
	يمكننا أخذ بعض الأمثلة من نواة <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">لينكس</a> Linux kernel التي يتعين عليها في أغلب الأحيان التعامل مع محاذاة صفحات الذاكرة ضمن النظام. إليك مثال عن التعامل مع محاذاة الصفحات:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7940_12" style=""><span class="pun">[</span><span class="pln"> include</span><span class="pun">/</span><span class="kwd">asm</span><span class="pun">-</span><span class="pln">ia64</span><span class="pun">/</span><span class="pln">page</span><span class="pun">.</span><span class="pln">h </span><span class="pun">]</span><span class="pln">

</span><span class="com">/*
 * ‫يحدّد PAGE_SHIFT حجم صفحة النواة الفعلي
 */</span><span class="pln">
</span><span class="com">#if defined(CONFIG_IA64_PAGE_SIZE_4KB)</span><span class="pln">
</span><span class="com"># define PAGE_SHIFT     12</span><span class="pln">
</span><span class="com">#elif</span><span class="pln"> defined</span><span class="pun">(</span><span class="pln">CONFIG_IA64_PAGE_SIZE_8KB</span><span class="pun">)</span><span class="pln">
</span><span class="com"># define PAGE_SHIFT     13</span><span class="pln">
</span><span class="com">#elif</span><span class="pln"> defined</span><span class="pun">(</span><span class="pln">CONFIG_IA64_PAGE_SIZE_16KB</span><span class="pun">)</span><span class="pln">
</span><span class="com"># define PAGE_SHIFT     14</span><span class="pln">
</span><span class="com">#elif</span><span class="pln"> defined</span><span class="pun">(</span><span class="pln">CONFIG_IA64_PAGE_SIZE_64KB</span><span class="pun">)</span><span class="pln">
</span><span class="com"># define PAGE_SHIFT     16</span><span class="pln">
</span><span class="com">#else</span><span class="pln">
</span><span class="com"># error Unsupported page size!</span><span class="pln">
</span><span class="com">#endif</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> PAGE_SIZE               </span><span class="pun">(</span><span class="pln">__IA64_UL_CONST</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> PAGE_SHIFT</span><span class="pun">)</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> PAGE_MASK               </span><span class="pun">(~(</span><span class="pln">PAGE_SIZE </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">))</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> PAGE_ALIGN</span><span class="pun">(</span><span class="pln">addr</span><span class="pun">)</span><span class="pln">        </span><span class="pun">(((</span><span class="pln">addr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> PAGE_SIZE </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln"> PAGE_MASK</span><span class="pun">)</span></pre>

<p>
	يمكننا أن نرى في المثال السابق أن هناك عددًا من الخيارات المختلفة لأحجام الصفحات داخل النواة التي تتراوح من 4 كيلوبايتات إلى 64 كيلوبايت. يَُعد الماكرو <code>PAGE_SIZE</code> واضحًا إلى حد ما، فهو يعطي حجم الصفحة الحالي المحدّد ضمن النظام عن طريق انزياح قيمته 1 باستخدام رقم الانزياح المُعطَى، ويعادل ذلك <code>2n</code> حيث <code>n</code> هو انزياح الصفحة <code>PAGE_SHIFT</code>.
</p>

<p>
	لدينا بعد ذلك تعريف قناع الصفحة <code>PAGE_MASK</code> الذي يسمح لنا بالعثور على تلك البتات الموجودة في الصفحة الحالية فقط، أي إزاحة <code>offset</code> العنوان في صفحته.
</p>

<h4>
	التحسين Optimisation
</h4>

<p>
	يريد المصرِّف بمجرد الحصول على تمثيل داخلي للشيفرة البرمجية إيجادَ أفضل خرج بلغة التجميع لدخل الشيفرة البرمجية المُحدَّد. هذه مشكلة كبيرة ومتنوعة وتتطلب معرفة كل شيء من <a href="https://academy.hsoub.com/programming/advanced/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1282/" rel="">الخوارزميات</a> الفعالة المعتمَدة في <a href="https://academy.hsoub.com/programming/general/%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/" rel="">علوم الحاسوب</a> إلى المعرفة العميقة بالمعالج الذي ستعمل الشيفرة البرمجية عليه.
</p>

<p>
	هناك بعض التحسينات الشائعة التي يمكن أن ينظر إليها المصرِّف عند توليد الخرج، وهناك العديد والعديد من الاستراتيجيات لإنشاء الشيفرة البرمجية الأفضل، ويُعَد ذلك مجال بحث غني. يمكن للمصرِّف أن يرى في كثير من الأحيان أنه لا يمكن استخدام جزء معين من الشيفرة البرمجية، لذا يتركه لتحسين بنية لغة معينة وينتقل إلى شيء أصغر يوصل للنتيجة نفسها.
</p>

<ul>
	<li>
		<p>
			فك الحلقات Unrolling Loops: إذا احتوت الشيفرة البرمجية على حلقة مثل حلقة <code>for</code> أو <code>while</code> وكان لدى المُصرِّف فكرة عن عدد المرات التي ستنفّذ فيها، فسيكون فك الحلقة أكثر فاعلية بحيث تُنفَّذ تسلسليًا، إذ تُكرَّر شيفرة الحلقة الداخلية لتنفيذها عدد المرات ذاك أخرى بدلًا من تنفيذ الجزء الداخلي من الحلقة ثم العودة إلى البداية لتكرار العملية.<br>
			تزيد هذه العملية من حجم الشيفرة البرمجية، إذ يمكن أن تسمح للمعالج بتنفيذ التعليمات بفعالية، حيث يمكن أن تتسبب الفروع في تقليل كفاءة خط أنابيب التعليمات الواردة إلى المعالج.
		</p>
	</li>
	<li>
		<p>
			الدوال المضمنة Inlining Functions: يمكن وضع دوال مُضمَّنة لاستدعائها ضمن المستدعي callee، ويمكن للمبرمج تحديد ذلك للمصرِّف من خلال وضع الكلمة <code>inline</code> في تعريف الدالة، ويمكنك مقايضة حجم الشيفرة البرمجية بتسلسل تنفيذها من خلال ذلك.
		</p>
	</li>
	<li>
		<p>
			توقع الفرع Branch Prediction: إذا صادف الحاسوب تعليمة <code>if</code>، فهناك نتيجتان محتملتان إما صحيحة أو خاطئة. يريد المعالج الاحتفاظ بأنابيبه الواردة ممتلئة قدر الإمكان، لذا لا يمكنه انتظار نتيجة الاختبار قبل وضع الشيفرة البرمجية في خط الأنابيب، وبالتالي يمكن للمصرِّف أن يتنبأ بالطريقة التي يُحتمَل أن يسير بها الاختبار. هناك بعض القواعد البسيطة التي يمكن أن يستخدمها المصرِّف لتخمين هذه الأمور، فمثلًا لا يُحتمَل أن تكون التعليمة <code>if (val == -1)‎</code> صحيحةً، لأن القيمة <code>‎-1</code> تشير عادةً إلى رمز خطأ ونأمل ألّا تُشغَّل هذه التعليمة كثيرًا.
		</p>
	</li>
</ul>

<p>
	يمكن لبعض المصرِّفات تصريف البرنامج، وجعل المستخدم يشغّله ليلاحظ الطريق الذي تسير به الفروع في ظل ظروف واقعية، ويمكنه بعد ذلك إعادة تصريفه بناءً على ما شاهده.
</p>

<h3>
	المجمع Assembler
</h3>

<p>
	تبقى شيفرة التجميع التي أخرجها المصرِّف في صيغة يمكن أن يقرأها الإنسان إذا كنت على معرفة بتفاصيل شيفرة التجميع الخاصة بالمعالج. يُلقي المطورون في أغلب الأحيان نظرة خاطفة على خرج التجميع للتحقق يدويًا من أن الشيفرة البرمجية هي الأفضل أو لاكتشاف أخطاء المصرِّف، ويُعَد ذلك أكثر شيوعًا مما هو متوقع خاصةً عندما يكثِر المصرِّف من التحسينات.
</p>

<p>
	المجمِّع assembly هو عملية آلية لتحويل شيفرة التجميع إلى صيغة ثنائية. يحتفظ المجمّع بجدول كبير لكل تعليمة ممكنة ولنظيرها الثنائي الذي يسمى شيفرة العملية Op Code. يدمج المجمّع شيفرات العمليات مع المسجلات المحدَّدة في شيفرة التجميع لإنتاج ملف ثنائي بوصفه خرجًا.
</p>

<p>
	يُطلق على هذه الشيفرة بشيفرة التعليمات المُصرَّفة Object Code، وهي شيفرة غير قابلة للتنفيذ في هذه المرحلة، وتُعد مجرد تمثيل ثنائي للدخل الذي يمثل شيفرة برمجية مصدرية. يُفضَّل ألّا يضع المبرمج الشيفرة المصدرية بأكملها في ملفٍ واحد.
</p>

<h3>
	الرابط Linker
</h3>

<p>
	ستُقسَم في أغلب الأحيان الشيفرة البرمجية في برنامج كبير إلى ملفات متعددة لتكون الدوال ذات الصلة مع بعضها بعضًا. يمكن تصريف كل ملفٍ من هذه الملفات إلى شيفرة تعليمات مُصرَّفة ولكن هدفك النهائي هو إنشاء ملف قابل للتنفيذ. يجب أن يكون هناك طريقة ما لدمجها في ملف واحد قابل للتنفيذ، حيث نسمي هذه العملية بالربط Linking.
</p>

<p>
	لاحظ أنه لا يزال يجب ربط برنامجك بمكتبات نظام معينة للعمل بصورة صحيحة حتى إن كان برنامجك مناسبًا لملفٍ واحد، إذ يكون الاستدعاء <code>printf</code> مثلًا في مكتبة يجب دمجها مع ملفك القابل للتنفيذ ليعمل، لذا لا تزال هناك بالتأكيد عملية ربط تحدث لإنشاء ملفك القابل للتنفيذ بالرغم من أنه لا داعي للقلق صراحةً بشأن الربط في هذه الحالة.
</p>

<p>
	سنشرح فيما يلي بعض المصطلحات الأساسية لفهم عملية الربط.
</p>

<h4>
	الرموز Symbols
</h4>

<p>
	لجميع المتغيرات والدوال أسماء في الشيفرة المصدرية، إذ نشير إليها باستخدام هذه الأسماء. تتمثل إحدى طرق التفكير في تعليمة التصريح عن متغير <code>int a</code> في أنك تخبر المصرِّف بأن يحجز حيزًا من الذاكرة بحجم <code>sizeof(int)‎</code>، وبالتالي كلما استخدمت اسم المتغير <code>a</code>، فسيشير إلى هذه الذاكرة المخصَّصة، وكذلك الأمر بالنسبة للدالة التي تخبر المصرِّف بأن يحزّن هذه الشيفرة البرمجية في الذاكرة، ثم ينتقل إليها وينفّذها عند استدعاء الدالة <code>function()‎</code>. وبالتالي نستدعي الرمزين <code>a</code> و <code>function</code> لأنهما يُعَدان تمثيلًا رمزيًا لمنطقةٍ من الذاكرة.
</p>

<p>
	تساعد هذه الرموز البشر على فهم البرمجة. لكن يمكنك القول أن المهمة الأساسية لعملية التصريف هي إزالة هذه الرموز، إذ لا يعرف المعالج ما يمثله الرمز <code>a</code>، فكل ما يعرفه هو أن لديه بعض البيانات في عنوان ذاكرة معين. تحوِّل عملية التصريف التعليمة <code>a += 2</code> إلى العبارة "زيادة القيمة الموجودة في العنوان <code>0xABCDE</code> من الذاكرة بمقدار 2".
</p>

<blockquote class="ipsQuote" data-gramm="false" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix" data-gramm="false">
		<p>
			إمكانية رؤية الرموز Symbol Visibility: لا بد أنك شاهدت في البرامج المكتوبة بلغة C المصطلحين <code>static</code> و<code>extern</code> مع المتغيرات، إذ يمكن أن تؤثر هذه المعدِّلات Modifiers على ما نسميه إمكانية رؤية الرموز.
		</p>
	</div>
</blockquote>

<p>
	لنفترض أنك قسمت برنامجك إلى ملفين، ولكن تريد بعضُ الدوال مشاركةَ متغيرٍ ما. نريد تعريفًا Definition أو موقعًا واحدًا فقط في الذاكرة للمتغير المشترك وإلا فلا يمكن مشاركته، ولكن يجب أن يشير كلا الملفين إليه. يمكن ذلك من خلال التصريح عن المتغير في ملف واحد، ثم نصرّح في الملف الآخر عن متغير بالاسم نفسه مع البادئة <code>extern</code> التي ترمز إلى أنه خارجي External وترمز للمبرمج بأن هذا المتغير مُصرَّحٌ عنه في مكان آخر.
</p>

<p>
	تخبر الكلمة <code>extern</code> المصرِّف أنه لا ينبغي تخصيص أي مساحة في الذاكرة لهذا المتغير، ويجب ترك هذا الرمز في التعليمات المُصرَّفة لإصلاحه لاحقًا. لا يمكن للمصرِّف أن يعرف مكان تعريف الرمز فعليًا ولكن الرابط Linker يمكنه ذلك، فوظيفته هي النظر في جميع ملفات التعليمات المُصرَّفة ودمجها في ملف واحد قابل للتنفيذ. لذا سيرى الرابط هذا الرمز في الملف الثاني، وسيقول: "رأيت هذا الرمز مسبقًا في الملف 1، وأعلم أنه يشير إلى موقع الذاكرة <code>0x12345</code>"، وبالتالي يمكن تعديل قيمة الرمز لتكون قيمة الذاكرة للمتغير الموجود في الملف الأول.
</p>

<p>
	تُعَد الكلمة ساكن <code>static</code> عكس خارجي <code>extern</code> تقريبًا، لأنها تضع قيودًا على رؤية الرمز الذي نريد تعديله. إذا صرّحتَ عن متغير بأنه ساكن <code>static</code>، فهذا يعني للمصرّف بألا يترك أيّ رموز لهذا المتغير في شيفرة التعليمات المصرَّفة، وبالتالي لن يرى الرابط هذا الرمز أبدًا عندما يربط ملفات التعليمات المُصرَّفة مع بعضها البعض، أي لا يمكنه القول بأنه رأى هذا الرمز سابقًا.
</p>

<p>
	يُعَد استخدام الكلمة <code>static</code> مفيدًا للفصل بين الرموز وتقليل التعارضات بينها، إذ يمكنك إعادة استخدام اسم المتغير المُصرَّح عنه بأنه <code>static</code> في ملفات أخرى دون وجود تعارضات بين الرموز. يمكن القول بأننا نقيّد رؤية الرمز، لأننا لا نسمح للرابط برؤيته بعكس الرمز الذي لم يُصرَّح عنه بأنه <code>static</code> ويمكن للرابط رؤيته.
</p>

<h4>
	عملية الربط
</h4>

<p>
	تتكون عملية الربط من خطوتين هما: دمج جميع ملفات التعليمات المُصرَّفة في ملف واحد قابل للتنفيذ ثم الانتقال إلى كل ملف لتحليل الرموز. يتطلب ذلك تمريرين، أحدهما لقراءة جميع تعريفات الرموز وتدوين الرموز التي لم تُحلَّل والثاني لإصلاح تلك الرموز التي لم تُحلَّل في المكان الصحيح.
</p>

<p>
	يجب أن يكون الملف القابل للتنفيذ النهائي بدون رموز غير مُحلَّلة، إذ سيفشل الرابط مع وجود خطأ بسبب هذه الرموز. نسمي ذلك بالربط الساكن Static Linking، فالربط الديناميكي هو مفهوم مشابه يُطبَّق ضمن الملف القابل للتنفيذ في وقت التشغيل، حيث سنتطرق إليه لاحقًا.
</p>

<p>
	تعرّفنا في هذا المقال على الخطوات الثلاث لبناء ملف قابل للتنفيذ هي: التصريف Compiling والتجميع Assembling والربط Linking، وسنطبّق في <a href="https://academy.hsoub.com/programming/c/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%B9%D9%85%D9%84%D9%8A-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%8A-%D9%85%D9%86-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D9%85%D8%B5%D8%AF%D8%B1%D9%8A%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-c-r1931/" rel="">المقال التالي</a> هذه الخطوات عمليًا لبناء ملف قابل للتنفيذ.
</p>

<p>
	ترجمة -وبتصرُّف- للأقسام:
</p>

<ul>
	<li>
		<a href="https://www.bottomupcs.com/chapter06.xhtml#compiled_v_interpreted" rel="external nofollow">Compiled v Interpreted Programs</a>
	</li>
	<li>
		<a href="https://www.bottomupcs.com/building_an_executable.xhtml" rel="external nofollow">Building an executable</a>
	</li>
	<li>
		<a href="https://www.bottomupcs.com/compiling.xhtml" rel="external nofollow">Compiling</a>
	</li>
	<li>
		<a href="https://www.bottomupcs.com/assembler.xhtml" rel="external nofollow">Assembler</a>
	</li>
	<li>
		<a href="https://www.bottomupcs.com/linker.xhtml" rel="external nofollow">Linker</a>
	</li>
</ul>

<p>
	من فصل <a href="https://www.bottomupcs.com/chapter06.xhtml" rel="external nofollow">The Toolchain</a> من كتاب <a href="https://www.bottomupcs.com/" rel="external nofollow">Computer Science from the Bottom Up</a> لصاحبه Ian Wienand.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: ت<a href="https://academy.hsoub.com/programming/c/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%B9%D9%85%D9%84%D9%8A-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%8A-%D9%85%D9%86-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D9%85%D8%B5%D8%AF%D8%B1%D9%8A%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-c-r1931/" rel="">طبيق عملي لبناء برنامج تنفيذي من شيفرة مصدرية بلغة سي C</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/advanced/%D8%AF%D8%B9%D9%85-%D8%B9%D8%AA%D8%A7%D8%AF-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-%D9%84%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D9%87%D9%85%D9%8A%D8%A9-virtual-memory-r1922/" rel="">دعم عتاد الحاسوب للذاكرة الوهمية Virtual Memory</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%A3%D9%88%D9%84-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-compilation-%D9%81%D9%8A-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r976/" rel="">مفهوم التصريف Compilation في لغات البرمجة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/cpp/%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A7%D9%84%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D9%88%D8%A8%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-cpp-%D9%88%D8%AA%D8%B4%D8%AE%D9%8A%D8%B5%D9%87%D8%A7-r1191/" rel="">تحسين الشيفرات المكتوبة بلغة Cpp وتشخيصها</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1930</guid><pubDate>Sat, 18 Mar 2023 12:02:00 +0000</pubDate></item><item><title>&#x62F;&#x639;&#x645; &#x639;&#x62A;&#x627;&#x62F; &#x627;&#x644;&#x62D;&#x627;&#x633;&#x648;&#x628; &#x644;&#x644;&#x630;&#x627;&#x643;&#x631;&#x629; &#x627;&#x644;&#x648;&#x647;&#x645;&#x64A;&#x629;  Virtual Memory</title><link>https://academy.hsoub.com/programming/advanced/%D8%AF%D8%B9%D9%85-%D8%B9%D8%AA%D8%A7%D8%AF-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-%D9%84%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D9%87%D9%85%D9%8A%D8%A9-virtual-memory-r1922/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_03/-------.png.0755a04f9ab9fa63d6c0d4cc445ebbc8.png" /></p>
<p>
	ذكرنا من الفصل السابق <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D9%87%D9%85%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D9%82%D9%8A%D9%82%D9%8A%D8%A9-%D9%81%D9%8A-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r1918/" rel="">الذاكرة الوهمية والذاكرة الحقيقية في معمارية الحاسوب</a> من سلسلتنا <a href="https://academy.hsoub.com/tags/%D9%85%D8%AF%D8%AE%D9%84%20%D9%84%D8%B9%D9%84%D9%85%20%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/" rel="">مدخل لعلم الحاسوب</a> حتى الآن أن العتاد يعمل مع نظام التشغيل لتقديم Implementation الذاكرة الوهمية، وألقينا نظرة على تفاصيل كيفية حدوث ذلك. تعتمد الذاكرة الوهمية بصورة كبيرة على معمارية العتاد، حيث يكون لكل معمارية خواصها الدقيقة، ولكن هناك عدد من العناصر الخاصة بالذاكرة الوهمية في العتاد التي سنستعرضها في هذا المقال.
</p>

<h2>
	الوضع الحقيقي والوضع الوهمي
</h2>

<p>
	تحتوي جميع المعالجات على مفهومٍ ما للعمل في الوضع الحقيقي Physical Mode أو الوضع الوهمي Virtual Mode، إذ يتوقع العتاد في الوضع الحقيقي أن يشير أيّ عنوان إلى عنوانٍ موجود في ذاكرة النظام الفعلية، بينما يعرف العتاد في الوضع الوهمي أنه يجب ترجمة العناوين للعثور على العنوان الحقيقي.
</p>

<p>
	يُشار إلى هذين الوضعين في العديد من المعالجات مثل المعالج إيتانيوم Itanium ببساطة على أنهما الوضع الحقيقي والوضع الوهمي. المعالج x86 الأكثر شيوعًا لديه الكثير من الخاصيات منذ الأيام الماضية التي تسبق الذاكرة الوهمية، حيث يُشار إلى هذين الوضعين على أنهما الوضع الفعلي Real Mode والوضع المحمي Protected Mode. كان أول معالج يطبق الوضع المحمي هو المعالج 386، ولا تزال أحدث المعالجات من عائلة x86 بإمكانها العمل في الوضع الفعلي بالرغم من عدم استخدامه. يطبّق المعالج في الوضع الفعلي شكلًا من أشكال تنظيم الذاكرة يسمى التقطيع Segmentation.
</p>

<h3>
	مشاكل التقطيع
</h3>

<p>
	كان التقطيع أمرًا مهمًا سابقًا، ولكن قلّلت الذاكرة الوهمية من أهميته، فللتقطيع عيوبه مثل كونه مربكًا للمبرمجين المبتدئين، لذا اُخترِعت أنظمة الذاكرة الوهمية لحل هذه المشاكل إلى حد كبير.
</p>

<p>
	يوجد في التقطيع عدد من المسجّلات التي تحتوي على عنوان يمثل بداية المقطع، والطريقة الوحيدة للوصول إلى عنوان في الذاكرة هي تحديده بوصفه إزاحة من أحد هذه المسجلات. يمكن تحديد حجم المقطع والحد الأقصى للإزاحة الذي يمكنك تحديده من خلال عدد البتات المتاحة للإزاحة من مسجل المقطع الأساسي. الحد الأقصى للإزاحة في المعالج x86 هو 16 بتًا أو 64 كيلوبايت فقط، مما يؤدي إلى ظهور جميع أشكال الفوضى، حيث إذا أراد شخصٌ ما استخدام عنوان يبعد أكثر من 64 كيلوبايت، فستتحول الأمور من مجرد إزعاج بسيط إلى فشل كامل عندما ينمو حجم الذاكرة إلى عدد من الميجابايتات أو الجيجابايتات.
</p>

<p>
	لنفترض أن أقصى إزاحة هي 32 بتًا، وبالتالي يمكن الوصول إلى فضاء العناوين بالكامل بوصفه إزاحة من مقطع عند العنوان 0x00000000 وسيكون لديك تخطيط مسطح، ولكن لا تضاهي هذه الحالة جودة الذاكرة الوهمية. السبب الوحيد لكون الإزاحة بمقدار 16 بت هو أن معالجات إنتل Intel الأصلية كانت محدودة بهذا المقدار مع محافظة الشرائح على التوافق مع الإصدارات السابقة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="120615" href="https://academy.hsoub.com/uploads/monthly_2023_03/01_segmentation.png.bc330204d965d923bf47ff33a01de40c.png" rel=""><img alt="01_segmentation.png" class="ipsImage ipsImage_thumbnailed" data-fileid="120615" data-unique="wyr2fp0bu" src="https://academy.hsoub.com/uploads/monthly_2023_03/01_segmentation.png.bc330204d965d923bf47ff33a01de40c.png"> </a>
</p>

<p style="text-align: center;">
	التقطيع Segmentation
</p>

<p>
	هناك ثلاثة مسجلات مقاطع في الشكل السابق تؤشّر جميعها إلى المقاطع، ويظهر حد الإزاحة الأقصى المُقيَّد بعدد البتات المتاحة في المنطقة المُظلَّلة. إذا أراد البرنامج عنوانًا خارج هذا النطاق، فيجب إعادة ضبط مسجّلات المقاطع الذي سرعان ما يصبح مصدر إزعاج كبير فيما بعد. بينما تسمح ذاكرة البرنامج الوهمية بتحديد العنوان الذي تريده ويطبّق <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">نظام التشغيل</a> والعتاد عملية الترجمة إلى عنوان حقيقي.
</p>

<h2>
	مخزن الترجمة المؤقت TLB
</h2>

<p>
	يُعَد مخزن الترجمة المؤقت Translation Lookaside Buffer -أو TLB اختصارًا- مكون المعالج الرئيسي المسؤول عن <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%AB%D8%A7%D9%84%D8%AB-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D9%87%D9%85%D9%8A%D8%A9-virtual-memory-%D9%81%D9%8A-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-r978/" rel="">الذاكرة الوهمية</a>، وهو ذاكرةٌ مخبئية لعمليات ترجمة الصفحة الوهمية إلى الإطار الحقيقي في المعالج. يعمل نظام التشغيل والعتاد مع بعضهما البعض لإدارة مخزن TLB أثناء تشغيل النظام.
</p>

<h3>
	أخطاء الصفحات
</h3>

<p>
	إذا طُلِب عنوان وهمي من العتاد باستخدام تعليمة تحميل <code>load</code> مثلًا للحصول على بعض البيانات، فسيبحث المعالج عن ترجمة العنوان الوهمي إلى العنوان الحقيقي في مخزن TLB الخاص به، فإن احتوى على ترجمة صالحة، فيمكن عندئذٍ دمجه مع جزء الإزاحة للانتقال مباشرةً إلى العنوان الحقيقي وإكمال التحميل. لكن إن لم يتمكّن المعالج من العثور على ترجمةٍ في مخزن TLB، فيجب على المعالج إصدار خطأ الصفحة Page Fault الذي يشبه المقاطعة، ويجب أن يعالجه نظام التشغيل، إذ يجب أن يستعرض نظام التشغيل جدول الصفحات للعثور على الترجمة الصحيحة وإدخالها في مخزن TLB عند حدوث خطأ صفحة.
</p>

<p>
	إن لم يتمكّن نظام التشغيل من العثور على ترجمةٍ في جدول الصفحات أو إن تحقّق نظام التشغيل من أذونات الصفحة المطلوبة ولم يُسمَح للعملية بالوصول إليها، فيجب على نظام التشغيل إنهاء العملية. إن رأيتَ مسبقًا خطأ تقطيع أو ما يُسمَّى Segfault، فهذا يعني أن نظام التشغيل ينهي العملية التي تجاوزت حدودها. بينما إن تمكّن نظام التشغيل من العثور على الترجمة وكان مخزن TLB ممتلئًا حاليًا، فيجب إزالة ترجمة قبل إدخال ترجمةٍ أخرى. ليست إزالة الترجمة التي يُحتمَل استخدامها لاحقًا أمرًا منطقيًا، إذ ستتحمل عناء العثور على المدخلة في جداول الصفحات مرةً أخرى.
</p>

<p>
	تستخدم مخازن TLB شيئًا يشبه خوارزمية الأقل استخدامًا مؤخرًا Least Recently Used -أو LRU اختصارًا، حيث تُخرَج أقدم ترجمة غير مُستخدمَة لإدخال ترجمة جديدة. يمكن بعد ذلك محاولة الوصول مرة أخرى وسيسير كل شيء على ما يرام، حيث يجب العثور على الترجمة في مخزن TLB وستحدث الترجمة بصورة صحيحة.
</p>

<h3>
	العثور على جدول الصفحات
</h3>

<p>
	لا بد أنك تساءلت عن كيفية إيجاد نظام التشغيل للذاكرة التي تحتوي على جدول الصفحات page table عندما قلنا أن نظام التشغيل يجد الترجمة في هذا الجدول. يُحتفَظ بقاعدة جدول الصفحات في مسجّل مرتبط بكل عملية، حيث يسمى هذا المسجل بالمسجل الأساسي لجدول الصفحات page-table base-register أو ما شابه ذلك. يمكن تحديد موقع المدخلة الصحيحة بوضع العنوان في هذا المسجل وإضافة رقم الصفحة إليه.
</p>

<h3>
	أخطاء أخرى متعلقة بالصفحات
</h3>

<p>
	هناك عيبان مهمان آخران يمكن أن يولّدهما مخزن TLB ويساعدان على إدارة الصفحات المتسخة dirty pages أي الصفحات التي جرى الوصول إليها مسبقًا، حيث تحتوي كل صفحة على سمة مُمثَّلة ببتٍ واحد تحدّد إذا جرى الوصول إلى الصفحة أي أنها أصبحت صفحة متسخة.
</p>

<p>
	يمكن تمييز الصفحة على أنها صفحة جرى الوصول إليها مسبقًا عند تحميل ترجمة الصفحة مبدئيًا في مخزن TLB. إذا حمّلتَ الصفحة دون وصول معلَّق، فيمكن تسمية ذلك بالتأمّل Speculation مثل تطبيق شيءٍ ما مع التوقّع بأنه سيؤتي ثماره، حيث إذا قرأت الشيفرة البرمجية من الذاكرة خطيًا مثلًا، فيمكن أن يؤدي وضع ترجمة الصفحة التالية في مخزن TLB إلى توفير الوقت وتحسين الأداء.
</p>

<p>
	يعمل نظام التشغيل على تصفح جميع الصفحات دوريًا ويصفّر بت الوصول ليميز الصفحات التي تكون قيد الاستخدام حاليًا من غيرها. تكون الصفحات التي لم يُعاد ضبط بت الوصول الخاص بها (بعد تصفيره) هي أفضل المرشحين للإزالة لأنها لم تُستخدَم لفترة أطول عندما تمتلئ ذاكرة النظام ويحين الوقت لنظام التشغيل لاختيار الصفحات التي ستُبدَّل إلى القرص الصلب.
</p>

<p>
	الصفحة المتسخة هي الصفحة التي تحتوي على بياناتٍ مكتوبةٍ عليها، وبالتالي لا تتطابق مع أيّ بيانات موجودة فعليًا على القرص الصلب. إذا بُدِّلت الصفحة من ثم كتبت فيها عملية مثلًا، فيجب تحديث نسختها الموجودة على <a href="https://academy.hsoub.com/certificates/comptia/%D8%A7%D9%84%D9%82%D8%B1%D8%B5-%D8%A7%D9%84%D8%B5%D9%84%D8%A8-%D8%A7%D9%84%D9%87%D9%8A%D9%83%D9%84%D8%A9%D8%8C-%D8%A7%D9%84%D9%85%D9%88%D8%A7%D8%B5%D9%81%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-r60/" rel="">القرص الصلب</a> قبل تبديلها. لا تُجرَى أيّ تغييرات على الصفحة النظيفة، لذلك لا حاجة لنسخ الصفحة مرةً أخرى إلى القرص الصلب.
</p>

<p>
	يتشابه هذان النوعان من الصفحات من حيث أنهما يساعدان نظام التشغيل في إدارة الصفحات، حيث تحتوي الصفحة على بتين إضافيين هما: البت المتسخ Dirty Bit وبت الوصول Accessed Bit، إذ يُضبَط هذان البتان عند وضع الصفحة في مخزن TLB للإشارة إلى أن وحدة المعالجة المركزية يجب أن تصدر خطأ.
</p>

<p>
	يطبّق العتادُ عملية الترجمة المعتادة عندما تحاول إحدى العمليات الرجوع إلى الذاكرة، ولكنه يجري أيضًا فحصًا إضافيًا للتأكد من عدم ضبط راية الوصول. إذا كان الأمر كذلك، فسيؤدي ذلك إلى حدوث خطأ في نظام التشغيل الذي يجب أن يضبط البت ويسمح للعملية بالاستمرار. إذا اكتشف العتاد أنه يكتب في صفحةٍ يكون بتها المتسخ غير مضبوط، فسيؤدي ذلك إلى حدوث خطأ في نظام التشغيل لتمييز الصفحة بوصفها متسخة.
</p>

<h2>
	إدارة مخزن TLB
</h2>

<p>
	يمكننا القول أن مخزن TLB يستخدمه العتاد ولكن تديره البرمجيات، فالأمر متروك لنظام التشغيل لتحميل المدخلات الصحيحة إلى مخزن TLB ولإزالة المدخلات القديمة منه.
</p>

<h3>
	تفريغ مخزن TLB
</h3>

<p>
	تُسمَّى عملية إزالة المدخلات من مخزن TLB باسم التفريغ Flushing. يُعَد تحديث مخزن TLB جزءًا مهمًا من الحفاظ على فضاءات عناوين العمليات منفصلة، لأن كل عملية يمكن أن تستخدم العنوان الوهمي نفسه دون تحديث مخزن TLB، وهذا يعني أن العملية يمكن أن تكتب فوق ذاكرة العمليات الأخرى، بينما تريد في حالة الخيوط Threads مشاركة فضاء العناوين، وبالتالي لا يُفرَّغ مخزن TLB عند التبديل بين الخيوط في العملية نفسها.
</p>

<p>
	يُفرَّغ مخزن TLB بالكامل في بعض المعالجات في كل مرة يوجد فيها تبديل سياق، ويمكن أن يكون ذلك مرهقًا للغاية، لأنه يعني أن العملية الجديدة يجب أن تمر عبر المراحل كاملةً من أخذ خطأ الصفحة ثم العثور على الصفحة في جداول الصفحات وإدخال الترجمة.
</p>

<p>
	بينما تطبّق المعالجات الأخرى معرّف فضاء عناوين إضافي Address Space ID -أو ASID اختصارًا- يُضاف إلى كل ترجمة في مخزن TLB لجعلها فريدة، وبالتالي يحصل كل فضاء عناوين -أو كل عملية حيث تريد الخيوط مشاركة فضاء العناوين نفسه- على معرّفها الخاص الذي يُخزَّن مع الترجمات في مخزن TLB، أي ليس هناك داعٍ لتفريغ مخزن TLB عند تبديل السياق، لأن العملية التالية سيكون لها معرّف فضاء عناوين مختلف، وسيختلف معرّف فضاء العناوين وستختلف الترجمة إلى الصفحة الحقيقية حتى إن طلبت العملية العنوان الوهمي نفسه. يقلل هذا النظام من عملية التفريغ ويزيد من أداء النظام، ولكنه يتطلب مزيدًا من عتاد TLB ليحتفظ ببتات معرّف ASID.
</p>

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

<h3>
	مخزن TLB المحمل برمجيا وعتاديا
</h3>

<p>
	يُعَد التحكم في مخزن TLB من اختصاص نظام التشغيل، ولكنها ليست القصة كاملة، إذ تشرح العملية الموضَّحة في فقرة "أخطاء الصفحات" خطأ الصفحة الذي يُرفَع إلى نظام التشغيل، ويمر على جدول الصفحات للعثور على ترجمة الصفحة الوهمية إلى الحقيقية وتثبيتها في مخزن TLB. يمكن أن يسمَّى ذلك بمخزن TLB المُحمَّل برمجيًا Software-loaded TLB، وهناك بديل آخر هو مخزن TLB المُحمَّل عتاديًا Hardware-loaded TLB.
</p>

<p>
	تحدد معمارية المعالج تخطيطًا معينًا لمعلومات جدول الصفحات في مخزن TLB المُحمَّل عتاديًا، ويجب اتباعها حتى ترجمة العنوان الوهمي. سيمر المعالج تلقائيًا على جداول الصفحات لتحميل مدخلة الترجمة الصحيحة استجابةً للوصول إلى عنوان وهمي غير موجود في مخزن TLB، وسيرفع المعالج استثناءً ليعالج نظام التشغيل مدخلة الترجمة غير الموجودة في مخزن TLB.
</p>

<p>
	يوفّر التنفيذ المُقدَّم للمرور على جدول الصفحات في العتاد المتخصص مزايا السرعة عند البحث عن الترجمات، ولكنه يزيل المرونة عن مطبّقي أنظمة التشغيل الذين يرغبون في تنفيذ مخططات بديلة لجداول الصفحات.
</p>

<p>
	يمكن تصنيف جميع المعماريات على نطاق واسع ضمن هاتين المنهجيتين السابقتين، وسنطّلع تاليًا على بعض المعماريات الشائعة ودعم الذاكرة الوهمية.
</p>

<h2>
	دعم العتاد للذاكرة الوهمية
</h2>

<p>
	يوفّر عتاد المعالج جدول بحث يربط العناوين الوهمية بالعناوين الحقيقية، حيث تحدد معماريات المعالجات طرقًا مختلفة لإدارة مخزن TLB مع مزايا وعيوب مختلفة. يشار إلى جزء المعالج الذي يتعامل مع الذاكرة الوهمية باسم <a href="https://academy.hsoub.com/certificates/comptia/%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B1%D9%83%D8%B2%D9%8A%D8%A9-r58/" rel="">وحدة إدارة الذاكرة Memory Management Unit</a> أو MMU اختصارًا.
</p>

<h3>
	معالج إيتانيوم
</h3>

<p>
	توفر وحدة MMU في معالج إيتانيوم ميزات متعددة لنظام التشغيل للتعامل مع الذاكرة الوهمية سنوضحها فيما يلي.
</p>

<h4>
	فضاءات العناوين Address spaces
</h4>

<p>
	شرحنا سابقًا مفهوم معرّف فضاء العناوين لتقليل تكلفة تفريغ مخزن TLB عند تبديل السياق، ولكن يستخدم المبرمجون في أغلب الأحيان الخيوط Threads للسماح لسياقات التنفيذ بمشاركة فضاء العناوين، حيث تحتوي جميع الخيوط على معرّف ASID نفسه، وبالتالي يتشاركون بمدخلات مخزن TLB، مما يؤدي إلى زيادة الأداء. لكن يمنع معرّف ASID واحد مخزن TLB من فرض الحماية، حيث تصبح المشاركة متمثلة بنهج "الكل أو لا شيء"، إذ يجب أن تتخلى الخيوط عن الحماية من بعضها البعض لمشاركة بعض البايتات.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="120614" href="https://academy.hsoub.com/uploads/monthly_2023_03/02_ia64-regions-keys.png.5a420733d48203874fe09d0d3b158e63.png" rel=""><img alt="02_ia64-regions-keys.png" class="ipsImage ipsImage_thumbnailed" data-fileid="120614" data-unique="bn9yqwfzr" src="https://academy.hsoub.com/uploads/monthly_2023_03/02_ia64-regions-keys.png.5a420733d48203874fe09d0d3b158e63.png"> </a>
</p>

<p style="text-align: center;">
	رسم توضيحي للمناطق ومفاتيح الحماية في معالج إيتانيوم
</p>

<p>
	تدرس وحدة MMU في معالج إيتانيوم المشاكل السابقة وتوفر القدرة على مشاركة فضاء العناوين ومدخلات الترجمة بدقة أقل بكثير مع الحفاظ على الحماية داخل العتاد. يقسم معالج إيتانيوم فضاء العناوين المؤلف من 64 بتًا إلى 8 مناطق كما هو موضَّح في الشكل السابق. تحتوي كل عملية على ثمانية مسجلات مناطق بحجم 24 بتًا بوصفها جزءًا من حالتها، ويحتوي كل منها على معرّف منطقة Region ID -أو RID اختصارًا- لكل منطقة من المناطق الثمانية الخاصة بفضاء عناوين العملية. تُوسَم ترجمات مخزن TLB بمعرّف RID، وبالتالي لن تتطابق إلا إذا احتوت العملية على معرّف RID نفسه كما هو موضَّح في الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="120613" href="https://academy.hsoub.com/uploads/monthly_2023_03/03_ia64-tlb-translation.png.6e9e496c0c21fd3d2cfe0b311850b0d1.png" rel=""><img alt="03_ia64-tlb-translation.png" class="ipsImage ipsImage_thumbnailed" data-fileid="120613" data-unique="w1uuizm53" src="https://academy.hsoub.com/uploads/monthly_2023_03/03_ia64-tlb-translation.png.6e9e496c0c21fd3d2cfe0b311850b0d1.png"> </a>
</p>

<p style="text-align: center;">
	رسم توضيحي لترجمة مخزن TLB في معالج إيتانيوم
</p>

<p>
	لا تُحتسَب البتات الثلاثة الأولى (بتات المنطقة) في ترجمة العنوان الوهمي، لذلك إذا كانت هناك عمليتان تشتركان في معرّف RID أي تحتفظان بالقيمة نفسها في أحد مسجلات المنطقة الخاصة بهما، فسيكون لديهما اسم بديل لتلك المنطقة. إذا احتوت العملية A على معرّف RID قيمته <code>0x100</code> في مسجل المنطقة 3 واحتوت العملية B معرّف RID نفسه الذي قيمته <code>0x100</code> في مسجّل المنطقة 5، فستُسمَّى المنطقة 3 من العملية A باسم بديل هو process-B, region 5. تعني هذه المشاركة المحدودة أن كلتا العمليتين تتلقيان فوائد مدخلات مخزن TLB المشتركة دون الحاجة إلى منح إذن الوصول إلى كامل فضاء العناوين.
</p>

<h4>
	مفاتيح الحماية Protection Keys
</h4>

<p>
	تُوسَم كل مدخلة من مخزن TLB في معالج إيتانيوم بمفتاح حماية للسماح بمشاركة أكثر دقة. تحتوي كل عملية على عدد إضافي من مسجلات مفاتيح الحماية يحدده نظام التشغيل.
</p>

<p>
	تُوسَم كل صفحة بمفتاح فريد ويمنح نظام التشغيل العمليات المسموح بها للوصول إلى الصفحات التي تستخدم هذا المفتاح عند مشاركة سلسلة من الصفحات مثل شيفرة برمجية لمكتبة نظام مشتركة. يفحص مخزن TLB المفتاح المرتبط بمدخلة الترجمة مقابل المفاتيح التي تحتفظ بها العملية في مسجلات مفتاح الحماية الخاصة بها عند الإشارة إلى صفحة ما، مما يسمح بالوصول إليها في حالة وجود المفتاح أو يؤدي إلى رفع خطأ حماية لنظام التشغيل.
</p>

<p>
	يمكن للمفتاح فرض الأذونات أيضًا، إذ يمكن أن تحتوي إحدى العمليات مثلًا على مفتاح يمنح أذونات الكتابة ويمكن أن تحتوي عملية أخرى على مفتاح للقراءة فقط، مما يسمح بمشاركة مدخلات الترجمة بدقة وفي نطاق أوسع بكثير وصولًا إلى مستوى الصفحة الواحدة، ويؤدي ذلك إلى تحسينات محتملة كبيرة في أداء مخزن TLB.
</p>

<h3>
	أداة إيتانيوم العتادية للمرور على جدول الصفحات Itanium Hardware Page-Table Walker
</h3>

<p>
	يؤدي تبديل السياق إلى نظام التشغيل عند حل خطأ في مخزن TLB إلى إضافة عبء كبير إلى مسار معالجة الخطأ، إذ يواجه معالج إيتانيوم ذلك العبء من خلال السماح بخيار استخدام العتاد المُدمَج لقراءة جدول الصفحات وتحميل ترجمات الصفحة الوهمية إلى الصفحة الحقيقية في مخزن TLB تلقائيًا. تتجنب أداة المرور على جدول الصفحات العتادية Hardware Page-table Walker -أو HPW اختصارًا- عمليات الانتقال المكلفة إلى نظام التشغيل، ولكنه يتطلب أن تكون الترجمات بصيغة ثابتة ومناسبة للعتاد لتفهمه.
</p>

<p>
	يُشار إلى أداة HPW الخاصة بمعالج إيتانيوم في توثيق إنتل على أنها أداة مُعمَّاة وهميًا للمرور على جدول الصفحات Virtually Hashed Page-table Walker أو VHPT walker اختصارًا. يمنح معالج إيتانيوم المطورين خيارين من تقديمات HPW الحصرية تبادليًا، إذ يعتمد أحدهما على جدول الصفحات الخطي الوهمي ويعتمد الآخر على جدول التعمية Hash Table.
</p>

<p>
	تجدر الإشارة إلى أنه يمكن العمل بدون أداة عتادية للمرور على جدول الصفحات، حيث يحل نظام التشغيل كل خطأ TLB ويصبح المعالج معمارية محمَّلة برمجيًا، ولكن يُعَد تأثير تعطيل HPW على الأداء كبيرًا جدًا دون الحصول على أيّ فائدة.
</p>

<h4>
	جدول الصفحات الخطي الوهمي Virtual Linear Page-Table
</h4>

<p>
	يشار إلى تقديم جدول الصفحات الخطي الوهمي في التوثيقات على أنه الصيغة القصيرة لجدول الصفحات المُعمَّاة وهميًا Short Format Virtually Hashed Page-table -أو SF-VHPT اختصارًا، وهو نموذج HPW الافتراضي الذي يستخدمه لينكس على معالج إيتانيوم.
</p>

<p>
	الحل المعتاد هو استخدام جدول صفحات متعدد المستويات أو هرمي، حيث تُستخدَم البتات التي تتكون من رقم الصفحة الوهمية بوصفها فهرسًا إلى مستويات وسيطة من جدول الصفحات. لا توجد مناطق فضاء عناوين وهمية فارغة في جدول الصفحات الهرمي. تُهدَر مساحة صغيرة نسبيًا في الحِمل الإضافي بالنسبة للحالة الواقعية لفضاء العناوين المُجمَّعة Clustered بإحكام والمملوءة بصورة ضئيلة بالمقارنة مع جدول الصفحات الخطي، ولكن العيب الرئيسي هو مراجع الذاكرة المتعددة المطلوبة للبحث.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="120612" href="https://academy.hsoub.com/uploads/monthly_2023_03/04_hierarchical-pt.png.511e28e397a04cade60f144715701118.png" rel=""><img alt="04_hierarchical-pt.png" class="ipsImage ipsImage_thumbnailed" data-fileid="120612" data-unique="pa9joqldg" src="https://academy.hsoub.com/uploads/monthly_2023_03/04_hierarchical-pt.thumb.png.c8a63047fc7bfab34c554ace5cf30e14.png"> </a>
</p>

<p style="text-align: center;">
	رسم توضيحي لجدول صفحات هرمي
</p>

<p>
	يأخذ الجدول الخطي الذي حجمه 512 جيبي بايت GiB مع فضاء عناوين 64 بتًا ما مقداره 0.003% فقط من 16 إكسابايت المتاحة، وبالتالي يمكن إنشاء جدول صفحات خطي وهمي Virtual Linear Page-table -أو VLPT اختصارًا- في منطقة متجاورة من فضاء العناوين الوهمية.
</p>

<p>
	يستخدم العتاد عند حدوث خطأ في مخزن TLB رقم الصفحة الوهمية للإزاحة عن قاعدة جدول الصفحات تمامًا كما هو الحال بالنسبة لجدول صفحات خطي حقيقي. إذا كانت هذه المدخلة صحيحة، فستُقرَأ الترجمة وتُدرَج مباشرةً في مخزن TLB، ولكن يكون عنوان مدخلة الترجمة في حد ذاتها عنوانًا وهميًا باستخدام جدول VLPT، وبالتالي هناك احتمال أن تكون الصفحة الوهمية التي توجد بها غير موجودة في مخزن TLB، وسيُرفَع خطأ متداخل Nested Fault إلى نظام التشغيل في هذه الحالة. يجب على البرمجيات بعد ذلك تصحيح هذا الخطأ عن طريق ربط الصفحة التي تحتوي على مدخلة الترجمة مع جدول VLPT.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="120611" href="https://academy.hsoub.com/uploads/monthly_2023_03/05_ia64-short-format.png.d1111d85521bc618a77a616a1d70438e.png" rel=""><img alt="05_ia64-short-format.png" class="ipsImage ipsImage_thumbnailed" data-fileid="120611" data-unique="bmu0itf90" src="https://academy.hsoub.com/uploads/monthly_2023_03/05_ia64-short-format.png.d1111d85521bc618a77a616a1d70438e.png"> </a>
</p>

<p style="text-align: center;">
	تقديم جدول VHPT بصيغة قصيرة في معالج إيتانيوم
</p>

<p>
	يمكن جعل هذه العملية مباشرةً إذا احتفظ نظام التشغيل بجدول صفحات هرمي، حيث تحتوي الصفحة التي تمثل ورقة من جدول صفحات هرمي على مدخلات ترجمة لمنطقة متجاورة وهميًا من العناوين، وبالتالي يمكن ربطها باستخدام مخزن TLB لإنشاء جدول VLPT كما هو موضح في الشكل السابق.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="120610" href="https://academy.hsoub.com/uploads/monthly_2023_03/06_ia64-ptes.png.425a94c3241199f973fb0bece3c64977.png" rel=""><img alt="06_ia64-ptes.png" class="ipsImage ipsImage_thumbnailed" data-fileid="120610" data-unique="dh4oviome" src="https://academy.hsoub.com/uploads/monthly_2023_03/06_ia64-ptes.png.425a94c3241199f973fb0bece3c64977.png"> </a>
</p>

<p style="text-align: center;">
	صيغ مدخلة PTE في معالج إيتانيوم
</p>

<p>
	تحدث ميزة جدول VLPT الرئيسية عندما يطلب أحد التطبيقات وصولًا متكررًا أو متواصلًا إلى الذاكرة. ضع في حساباتك أن الخطأ الأول في عملية المرور على الذاكرة المتجاورة وهميًا سيؤدي إلى ربط صفحة مليئة بمدخلات الترجمة مع جدول الصفحات الخطي الوهمي. سيتطلب الوصول اللاحق إلى الصفحة الوهمية التالية تحميلَ مدخلة الترجمة التالية في مخزن TLB، والتي تتوفر الآن في جدول VLPT، وبالتالي تُحمَّل بسرعة كبيرة دون استدعاء نظام التشغيل. سيكون ذلك ميزةً عند الاستفادة من تكلفة الخطأ المتداخل الأولي على عمليات مرور HPW الناجحة اللاحقة.
</p>

<p>
	العيب الرئيسي هو أن جدول VLPT يتطلب الآن مدخلات مخزن TLB، مما يؤدي إلى زيادة الضغط عليه. يتطلب كل فضاء عناوين جدول صفحاتٍ خاص به، لذلك تصبح التكلفة أكبر كلما أصبح النظام أكثر نشاطًا، ولكن يجب أن تكون أيّ زيادة في أخطاء الوصول إلى مخزن TLB أكبر من الفائدة الحاصلة عند انخفاض تكاليف إعادة الملء من أداة المرور العتادية الفعالة. لاحظ أن الحالة السيئة يمكن أن تتخطى مدخلات بمقدارٍ يساوي نتيجة قسمة حجم الصفحة <code>page_size</code> على حجم الترجمة <code>translation_size</code>، مما يتسبب في حدوث أخطاء متداخلة ومتكررة، ولكنه يُعَد نمط وصول غير مُحتمَل.
</p>

<p>
	تتوقع أداة المرور العتادية hardware walker أن تكون مدخلات الترجمة بصيغة معينة كما هو موضح على يسار الشكل السابق، حيث يتطلب جدول VLPT ترجمات بصيغة قصيرة مؤلَّفة من 8 بايتات. إذا استخدم نظام التشغيل جدول الصفحات الخاص به بوصفه دعمًا لجدول VLPT كما في الشكل "تقديم جدول VHPT بصيغة قصيرة في معالج إيتانيوم"، فيجب أن تستخدم هذه صيغة الترجمة القصيرة. تتجاهل المعمارية عددًا محدودًا من البتات في هذه الصيغة وبالتالي تكون متاحة لتستخدمها البرمجيات مع عدم احتمال حدوث تعديلات كبيرة.
</p>

<p>
	يعتمد جدول الصفحات الخطي linear page-table على فكرة حجم الصفحة الثابت، ويُعَد دعم أحجام الصفحات المتعددة مشكلةً لأنه يعني أن ترجمة صفحة وهمية معينة لم تَعُد عند إزاحة ثابتة، ولكن يمكن حل هذه المشكلة من خلال احتواء كل منطقة من المناطق الثمانية في فضاء العناوين -كما هو موضّح في الشكل "رسم توضيحي للمناطق ومفاتيح الحماية في معالج إيتانيوم"- على جدول VLPT منفصل يربط عناوين تلك المنطقة فقط. يمكن إعطاء حجم الصفحة الوهمية لكل منطقة، حيث تُخصَّص منطقة واحدة لصفحات أكبر (باستخدام مخزن HugeTLB في لينكس) ولكن لا يمكن استخدام أحجام صفحات متعددة ضمن المنطقة الواحدة.
</p>

<h4>
	جدول التعمية الوهمي Virtual Hash Table
</h4>

<p>
	يمكن أن يكون استخدام مدخلات مخزن TLB لمحاولة تقليل تكاليف إعادة تعبئته -كما هو الحال مع جدول SF-VHPT- مقايضة فعّالة أو يمكن ألّا يكون كذلك. يطبّق معالج إيتانيوم جدول صفحات مُعمَّى hashed page-table مع إمكانية خفض تكاليف مخزن TLB، حيث يعمّي المعالج في هذا المخطط عنوانًا وهميًا للعثور على إزاحة في جدول مجاور.
</p>

<p>
	يُعَد جدول الصفحات الخطي الذي ناقشناه سابقًا جدول صفحات مُعمَّى باستخدام تعمية Hash مثالية لن ينتج عنها تضاربٌ أبدًا، ولكن يتطلب ذلك مقايضة غير عملية لمناطق ضخمة من <a href="https://academy.hsoub.com/certificates/comptia/%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87%D8%A7-r59/" rel="">الذاكرة الحقيقية</a> المتجاورة. يزيد تقييد متطلبات الذاكرة لجدول الصفحات من احتمال حدوث تضاربات عند تعمية عنوانين وهميين إلى الإزاحة نفسها. تتطلب الترجمات المتضاربة مؤشر سلسلة Chain Pointer لإنشاء قائمة مترابطة من المدخلات البديلة الممكنة. يتطلب تمييز المدخلة الصحيحة في القائمة المترابطة وسمًا Tag مشتقًا من العنوان الوهمي الوارد.
</p>

<p>
	تؤدي المعلومات الإضافية المطلوبة لكل مدخلة ترجمة إلى ظهور اسم بديل بصيغة جدول VHPT الطويلة -أو LF-VHPT اختصارًا. تنمو مدخلات الترجمة إلى 32 بايتًا كما هو موضح على الجانب الأيمن من الشكل "صيغ مدخلة PTE في معالج إيتانيوم".
</p>

<p>
	الميزة الرئيسية لهذه الطريقة هي أن جدول التعمية العام يمكن تثبيته باستخدام مدخلة TLB واحدة. تشترك جميع العمليات في الجدول، لذا يجب أن ينمو حجمه بطريقة أفضل من صيغة SF-VHPT، إذ تتطلب كل عملية أعدادًا متزايدة من مدخلات صفحات جدول VLPT في مخزن TLB. لكن تكون المدخلات الأكبر أقل ملاءمة للذاكرة المخبئية، إذ يمكننا ملاءمة أربعة مدخلات ذات صيغة قصيرة بحجم 8 بايتات مع كل مدخلة ذات صيغة طويلة بحجم 32 بايت. يمكن أن تساعد <a href="https://academy.hsoub.com/programming/os-embedded-systems/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B9%D9%85%D9%8A%D9%82%D8%A9-%D8%B9%D9%84%D9%89-%D8%AA%D8%B3%D9%84%D8%B3%D9%84-%D8%A7%D9%84%D8%B0%D9%88%D8%A7%D9%83%D8%B1-%D8%A7%D9%84%D9%87%D8%B1%D9%85%D9%8A-%D9%88%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D8%AE%D8%A8%D8%A6%D9%8A%D8%A9-%D9%81%D9%8A-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r1720/" rel="">الذواكر المخبئية</a> الكبيرة جدًا الموجودة على معالج إيتانيوم في تخفيف هذا التأثير.
</p>

<p>
	تتمثل إحدى مزايا صيغة SF-VHPT في أن نظام التشغيل يمكنه الاحتفاظ بالترجمات في جدول صفحات هرمي، ويمكنه ربط الصفحات الورقية في هذه البنية الهرمية مباشرةً مع جدول VLPT مع الاحتفاظ بصيغة الترجمة العتادية. بينما يجب على نظام التشغيل باستخدام صيغة LF-VHPT إما استخدام جدول التعمية بوصفه مصدرًا أساسيًا لمدخلات الترجمة أو الاحتفاظ بجدول التعمية بوصفه ذاكرة مخبئية لمعلومات الترجمة الخاصة به. يُعَد الاحتفاظ بجدول التعمية بصيغة LF-VHPT بوصفه ذاكرة مخبئية دون المستوى الأمثل إلى حد ما بسبب زيادة الحِمل في مسارات الأخطاء الأساسية في الوقت المناسب، ولكن تُكتسَب الفوائد من الجدول الذي يتطلب مدخلة واحدة فقط في مخزن TLB.
</p>

<p>
	ترجمة -وبتصرُّف- للقسمين <a href="https://www.bottomupcs.com/virtual_memory_hardware.xhtml" rel="external nofollow">Hardware Support</a> و <a href="https://www.bottomupcs.com/hardware_support_for_virtual_memory.xhtml" rel="external nofollow">Hardware support for virtual memory</a> من فصل <a href="https://www.bottomupcs.com/chapter05.xhtml" rel="external nofollow">Virtual Memory</a> من كتاب <a href="https://www.bottomupcs.com/" rel="external nofollow">Computer Science from the Bottom Up</a> لصاحبه Ian Wienand.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/advanced/%D9%83%D9%8A%D9%81-%D8%AA%D9%86%D8%B4%D8%A6-%D9%85%D9%84%D9%81%D8%A7-%D9%82%D8%A7%D8%A8%D9%84%D8%A7-%D9%84%D9%84%D8%AA%D9%86%D9%81%D9%8A%D8%B0-executable-file-%D9%85%D9%86-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%85%D8%B5%D8%AF%D8%B1%D9%8A%D8%A9-r1930/" rel="">كيف تنشئ ملفا قابلا للتنفيذ Executable File من شيفرة برمجية مصدرية</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/advanced/%D8%AF%D8%B9%D9%85-%D8%B9%D8%AA%D8%A7%D8%AF-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-%D9%84%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%88%D9%87%D9%85%D9%8A%D8%A9-virtual-memory-r1922/" rel="">دعم عتاد الحاسوب للذاكرة الوهمية &amp;nbsp;Virtual Memory</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/os-embedded-systems/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B9%D9%85%D9%8A%D9%82%D8%A9-%D8%B9%D9%84%D9%89-%D8%AA%D8%B3%D9%84%D8%B3%D9%84-%D8%A7%D9%84%D8%B0%D9%88%D8%A7%D9%83%D8%B1-%D8%A7%D9%84%D9%87%D8%B1%D9%85%D9%8A-%D9%88%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D8%AE%D8%A8%D8%A6%D9%8A%D8%A9-%D9%81%D9%8A-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8-r1720/" rel="">نظرة عميقة على تسلسل الذواكر الهرمي والذاكرة المخبئية في معمارية الحاسوب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/os-embedded-systems/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%B9%D8%AF-%D8%A7%D9%84%D8%AB%D9%86%D8%A7%D8%A6%D9%8A-binary-%D8%A3%D8%B3%D8%A7%D8%B3-%D8%A7%D9%84%D8%AD%D9%88%D8%B3%D8%A8%D8%A9-r1658/" rel="">تعرف على نظام العد الثنائي Binary أساس الحوسبة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1922</guid><pubDate>Fri, 10 Mar 2023 17:45:00 +0000</pubDate></item><item><title>&#x62D;&#x645;&#x627;&#x64A;&#x629; &#x645;&#x648;&#x642;&#x639;&#x643; &#x645;&#x646; &#x627;&#x644;&#x627;&#x62E;&#x62A;&#x631;&#x627;&#x642;</title><link>https://academy.hsoub.com/programming/advanced/%D8%AD%D9%85%D8%A7%D9%8A%D8%A9-%D9%85%D9%88%D9%82%D8%B9%D9%83-%D9%85%D9%86-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%B1%D8%A7%D9%82-r2076/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_08/aca---(1).png.50d4d1aeca3b79e9986149560e253866.png" /></p>
<p>
	سنتحدث في هذا الفيديو عن طرق الاختراق الشائعة مثل حقن SQL أو SQL Injection وهجمات XSS وتخطي الاستيثاق Auth Bypass مع شرح كيفية التصدي لكل منها ، كما سنتحدث عن التدابير الشائعة لمنع الاختراق مثل المحافظة على تحديث الملحقات والبرامج باستمرار و استخدم كلمات مرور قوية واستخدم أداة Google Webmaster مع نصائح حول طرق حماية مواقع ووردبرس WordPress.
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="603" src="https://academy.hsoub.com/applications/core/interface/index.html" title="حماية موقعك من الاختراق" width="1072" data-embed-src="https://www.youtube.com/embed/5jAgs6BMmYg"></iframe>
</p>

<p>
	إذا أردت التعرف أكثر على الحواسب وعلوم الحاسوب، فننصحك بالانضمام إلى <a href="https://academy.hsoub.com/learn/computer-science/" rel="">دورة علوم الحاسوب</a>، ولا تنسَ الاستعانة خلال رحلة تعلمك وعملك بتوثيقات <a href="https://wiki.hsoub.com/%D8%A7%D9%84%D8%B5%D9%81%D8%AD%D8%A9_%D8%A7%D9%84%D8%B1%D8%A6%D9%8A%D8%B3%D9%8A%D8%A9" rel="external">موسوعة حسوب</a> المجانية. وإذا أردت متابعة المعلومات البرمجية العلمية مكتوبة فيمكنك الاطلاع على <a href="https://academy.hsoub.com/programming/" rel="">قسم البرمجة في أكاديمية حسوب</a>، كما يمكنك متابعة جديد الفيديوهات التقنية المتاحة على <a href="https://www.youtube.com/@HsoubAcademy" rel="external nofollow">يوتيوب أكاديمية حسوب</a> مجانًا.
</p>
]]></description><guid isPermaLink="false">2076</guid><pubDate>Sat, 15 Oct 2022 15:00:00 +0000</pubDate></item><item><title>&#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x627;&#x62A; &#x62A;&#x62D;&#x648;&#x64A;&#x644; &#x641;&#x648;&#x631;&#x64A;&#x64A;&#x647; &#x627;&#x644;&#x633;&#x631;&#x64A;&#x639; Fast Fourier Transform</title><link>https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%AA%D8%AD%D9%88%D9%8A%D9%84-%D9%81%D9%88%D8%B1%D9%8A%D9%8A%D9%87-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-fast-fourier-transform-r1537/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_04/6260e4d161fb5_FFT-01.jpg.b2c78a7327c2a0e17211eb002c737a17.jpg" /></p>
<p>
	يُستخدم <a href="https://ar.wikipedia.org/wiki/%D8%AA%D8%AD%D9%88%D9%8A%D9%84_%D9%81%D9%88%D8%B1%D9%8A%D9%8A%D9%87_%D8%A7%D9%84%D9%85%D8%AA%D9%82%D8%B7%D8%B9" rel="external nofollow">تحويل فورييه المتقطع</a> Discrete Fourier Transforms أو DFT بشكليه الحقيقي والمركّب لتحليل ترددات الإشارات المتقطعة والدورية.
</p>

<p>
	<a href="https://ar.wikipedia.org/wiki/%D8%AA%D8%AD%D9%88%D9%8A%D9%84_%D9%81%D9%88%D8%B1%D9%8A%D9%8A_%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9" rel="external nofollow">تحويل فورييه السريع</a> Fast Fourier Transform أو FFT هو تطبيق خاص لتحويل فورييه المتقطع DFT، يتميّز بالسرعة ويناسب وحدات المعالجة المركزية الحديثة.
</p>

<h2>
	تحويل فورييه السريع من الأساس 2
</h2>

<p>
	لعلّ أبسط طرق حساب تحويل فورييه السريع FFT وأشهرها هي خوارزمية التعشير الزمني من الأساس 2 أي Radix-2 Decimation in Time، وينبني عمل هذه الخوارزمية على تفكيك إشارة معيّنة ذات نطاق زمني مؤلف من N نقطة إلى N إشارة أحادية (أي ذات نطاق زمني مؤلف من نقطة واحدة).
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="96775" href="https://academy.hsoub.com/uploads/monthly_2022_04/KNiJM.png.7fa191482759eda79fe9956f3c050144.png" rel="" data-fileext="png"><img alt="KNiJM.png" class="ipsImage ipsImage_thumbnailed" data-fileid="96775" data-unique="h8n8wat9b" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_04/KNiJM.png.7fa191482759eda79fe9956f3c050144.png"></a>
</p>

<p>
	يمكن تفكيك الإشارة -أو التعشير الزمني decimation in time- عن طريق عكس بتّات المؤشّرات في مصفوفة بيانات النطاق الزمني، فإن كانت الإشارة مؤلفة من ستة عشر نقطة مثلًا، فستُبدّل العينة 1 (ترميزها الثنائي 0001) بالعينة 8 (ترميزها الثنائي 1000)، وتبديل العينة 2 (0010) بـ 4 (0100) وهكذا دواليك. يمكن إجراء مبادلة العينات Sample swapping باستخدام تقنية عكس البتات bit reverse، بيْد أنّ ذلك سيحدّ إمكانية استخدام خوارزمية Radix 2 FFT على الإشارات التي يمكن كتابة أطوالها وفق الصيغة N = 2 ^ M.
</p>

<p>
	تتطابق قيم الإشارات الأحادية داخل النطاق الزمني time domain مع قيمها في نطاق التردد frequency domain، لهذا لا تتطلب هذه المصفوفة المؤلّفة من نقاط النطاق الزمني المفكّكة أيّ تحويل لتصبح مصفوفة لترددات النطاق. بيْد أنّه يجب إعادة بناء النقاط الفردية، وعددها N كما تعلم ، إلى طيف ترددات مؤلف من N نقطة N-point frequency spectra. تُعدّ طريقة <a href="https://en.wikipedia.org/wiki/Butterfly_diagram" rel="external nofollow">الفراشة</a> أفضل طريقة لإعادة بناء طيف الترددات.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="96776" href="https://academy.hsoub.com/uploads/monthly_2022_04/OKYjB.png.23c0f1ab77681f27cccc21fc03355add.png" rel="" data-fileext="png"><img alt="OKYjB.png" class="ipsImage ipsImage_thumbnailed" data-fileid="96776" data-unique="a0pnkin4h" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_04/OKYjB.png.23c0f1ab77681f27cccc21fc03355add.png"></a>
</p>

<p>
	تتجنّب خوارزمية تحويل فورييه السريع الحسابات الزائدة التي يُجريها تحويل فورييه المتقطع DFT، وذلك عبر استغلال التواتر الدوري periodicity لـ Wn ^ R. تستغرق عملية إعادة البناء الطيفي عدد log2 (N)‎‎ مرحلة من مراحل حسابات الفراشة لتُعيد قيمة X [K]‎‎؛ أي بيانات نطاق التردد وفق <a href="https://ar.wikipedia.org/wiki/%D8%B9%D8%AF%D8%AF_%D9%85%D8%B1%D9%83%D8%A8" rel="external nofollow">الشكل الجبري</a>.
</p>

<p>
	ولكي نحول الإحداثيات الجبرية إلى <a href="https://ar.wikipedia.org/wiki/%D9%86%D8%B8%D8%A7%D9%85_%D8%A5%D8%AD%D8%AF%D8%A7%D8%AB%D9%8A_%D9%82%D8%B7%D8%A8%D9%8A" rel="external nofollow">إحداثيات قطبية</a> فإننا نحسب <a href="https://ar.wikipedia.org/wiki/%D9%82%D9%8A%D9%85%D8%A9_%D9%85%D8%B7%D9%84%D9%82%D8%A9" rel="external nofollow">القيمة المطلقة</a> التي تساوي: <code>‎√‎(Re^2 + Im^2)</code>، حيث يمثّل Re الجزء الحقيقي من العدد المركب، ويمثّل Im الجزء التخيلي. سيكون علينا إيضًا حساب وسيط العدد المركّب عبر الصيغة: tan<sup>-1</sup> (Im / Re)‎‎.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="96777" href="https://academy.hsoub.com/uploads/monthly_2022_04/RsWAe.png.07f338189ebdc751ef094f840b64f3d4.png" rel="" data-fileext="png"><img alt="RsWAe.png" class="ipsImage ipsImage_thumbnailed" data-fileid="96777" data-unique="ntdak1w4g" src="https://academy.hsoub.com/uploads/monthly_2022_04/RsWAe.thumb.png.17c1aa534a9f5fd223533b063370661d.png"></a>
</p>

<p>
	حيث:
</p>

<ul>
	<li>
		<strong>N</strong>: عدد النقاط في تحويل فورييه السريع
	</li>
	<li>
		<strong>R</strong>: قيمة W<sub>N</sub> الحالية، وتعتمد على قيمة N و المرحلة الحالية من تحويل فورييه السريع وقيمة حسابات الفراشة في هذه المرحلة.
	</li>
</ul>

<p>
	يمثل الرسم البياني التالي مخطّط الفراشة لخوارزمية فورييه السريع الثُمانِي من الأساس 2 - أي eight point Radix 2 FFT- ولاحظ أن الإشارات المُدخلة رُتَِبت وفقًا لقيم التعشير الزمني المشار إليه سابقًا.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="96774" href="https://academy.hsoub.com/uploads/monthly_2022_04/4plRM.png.75421cfb4c1577203d8db8f2fc62957e.png" rel="" data-fileext="png"><img alt="4plRM.png" class="ipsImage ipsImage_thumbnailed" data-fileid="96774" data-unique="q7lykvpj0" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_04/4plRM.thumb.png.ab42960ecc5cfe1d8548b8df8c8deab3.png"></a>
</p>

<p>
	يأخذ تحويل فورييه السريع أعدادًا مركّبة ويعيد أعدادًا مركّبة، فإن أردت تمرير إشارات حقيقية (أي تنتمي لمجموعة الأعداد الحقيقية R)، فيجب أن تعيّن الجزء التخيلي على القيمة 0، وتعطي للجزء الحقيقي قيمة الإشارة المُمرَّرة، أي <code>x [n]‎‎</code>. يمكن تحديد قيم Wn ^ R المستخدمة خلال أطوار إعادة البناء باستخدام معادلة أسّية موزونة exponential weighting equation.
</p>

<p>
	تُحدَِّد قيمة R (القوة الأسية الموزونة) المرحلة الحالية في عملية إعادة البناء الطيفية ومرحلة حسابات الفراشة.
</p>

<p>
	هذه شيفرة مكتوبة بلغة C / C++‎ لحساب تحويل فورييه السريع من الأساس 2. تعمل هذه الشيفرة مهما كان الحجم N، شرط أن يكون N قوة للعدد 2. هذه الطريقة أبطأ 3 مرات تقريبًا من أسرع تقديم لتحويل فورييه السريع، ولكنّها تقنية واعدة ويمكن تحسينها مستقبلًا، كما أنّها مناسبة لتعليم أساسيات خوارزمية تحويل فورييه السريع.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1962_7" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;math.h&gt;</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> PI       </span><span class="lit">3.1415926535897932384626433832795</span><span class="pln">    </span><span class="com">// PI  قيمة</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> TWOPI    </span><span class="lit">6.283185307179586476925286766559</span><span class="pln">     </span><span class="com">// 2*PI قيمة</span><span class="pln">

</span><span class="com">// ‫معامل التحويل من Degrees إلى Radians </span><span class="pln">
</span><span class="com">#define</span><span class="pln"> </span><span class="typ">Deg2Rad</span><span class="pln">  </span><span class="lit">0.017453292519943295769236907684886</span><span class="pln">  

</span><span class="com">// ‫معامل التحويل من Radians إلى Degrees </span><span class="pln">
</span><span class="com">#define</span><span class="pln"> </span><span class="typ">Rad2Deg</span><span class="pln">  </span><span class="lit">57.295779513082320876798154814105</span><span class="pln">    </span><span class="pun">/</span><span class="pln">

</span><span class="com">#define</span><span class="pln"> log10_2  </span><span class="lit">0.30102999566398119521373889472449</span><span class="pln">   </span><span class="com">// Log10 of 2</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> log10_2_INV </span><span class="lit">3.3219280948873623478703194294948</span><span class="pln"> </span><span class="com">// 1/Log10(2)</span><span class="pln">

</span><span class="com">// بنية لتمثيل الأعداد المركبة</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> complex
</span><span class="pun">{</span><span class="pln">
</span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln">  </span><span class="typ">Re</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Im</span><span class="pun">;</span><span class="pln"> 
</span><span class="pun">};</span><span class="pln">

</span><span class="com">// ‫تعيد true إن كان N قوة للعدد 2</span><span class="pln">
</span><span class="kwd">bool</span><span class="pln"> isPwrTwo</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> N</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">M</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
   </span><span class="com">// ‫تمثل M عدد المراحل التي يجب إجراؤها،‪‫ 2‪^M = N</span><span class="pln">
   </span><span class="pun">*</span><span class="pln">M </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)</span><span class="pln">ceil</span><span class="pun">(</span><span class="pln">log10</span><span class="pun">((</span><span class="kwd">double</span><span class="pun">)</span><span class="pln">N</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> log10_2_INV</span><span class="pun">);</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> NN </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)</span><span class="pln">pow</span><span class="pun">(</span><span class="lit">2.0</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">M</span><span class="pun">);</span><span class="pln">

   </span><span class="com">// ‫تحقق من أنّ N قوة للعدد 2</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">((</span><span class="pln">NN </span><span class="pun">!=</span><span class="pln"> N</span><span class="pun">)</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> </span><span class="pun">(</span><span class="pln">NN </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">))</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> rad2FFT</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> N</span><span class="pun">,</span><span class="pln"> complex </span><span class="pun">*</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> complex </span><span class="pun">*</span><span class="pln">DFT</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> M </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

   </span><span class="com">// ‫تحقق من أنّه قوة للعدد 2، وإن كان خلاف ذلك فأنهِ البرنامج</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">isPwrTwo</span><span class="pun">(</span><span class="pln">N</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">M</span><span class="pun">))</span><span class="pln">
       </span><span class="kwd">throw</span><span class="pln"> </span><span class="str">"Rad2FFT(): N must be a power of 2 for Radix FFT"</span><span class="pun">;</span><span class="pln">

 </span><span class="com">// متغيرات صحيحة</span><span class="pln">

        </span><span class="com">// ‫لتخزين المسافة في الذاكرة بين حسابات الفراشة</span><span class="pln">
        </span><span class="typ">int</span><span class="pln"> </span><span class="typ">BSep</span><span class="pun">;</span><span class="pln">             

        </span><span class="com">// لتخزين المسافة بين الطرفين المتقابلين في حسابات الفراشة</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> </span><span class="typ">BWidth</span><span class="pun">;</span><span class="pln">           

   </span><span class="com">// المتماثلة التي ستُستخدم في هذه المرحلة Wn عدد قيم  P يمثل المتغير </span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> P</span><span class="pun">;</span><span class="pln">  

   </span><span class="com">// في حلقة التكرار لإجراء كل الحسابات في هذه المرحلة j يُستخدم</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> j</span><span class="pun">;</span><span class="pln">

        </span><span class="com">// رقم المرحلة الحالية في تحليل فورييه السريع stage يمثل المتغير </span><span class="pln">
        </span><span class="com">// في المجموعة M هناك </span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> stage </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> 

   </span><span class="com">// الخاصة بالقيمة العليا DFT يمثَّل فهرس مصفوفة</span><span class="pln">
   </span><span class="com">// في كل عملية من حسابات الفراشة</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> </span><span class="typ">HiIndex</span><span class="pun">;</span><span class="pln"> 

   </span><span class="com">// يُستخدم في قلب القناع البتي</span><span class="pln">
   </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> iaddr</span><span class="pun">;</span><span class="pln">        

     </span><span class="com">// يُستخدم في التعشير الزمني</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> ii</span><span class="pun">;</span><span class="pln">                    
   </span><span class="typ">int</span><span class="pln"> MM1 </span><span class="pun">=</span><span class="pln"> M </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> l</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> nMax </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">unsigned</span><span class="pln"> </span><span class="typ">int</span><span class="pun">)</span><span class="pln">N</span><span class="pun">;</span><span class="pln">

   </span><span class="com">// ‫يمثل TwoPi_N ثابتة لتخزين مدّة الحسابات = ‪ 2*PI / N</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> </span><span class="typ">TwoPi_N</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> TWOPI </span><span class="pun">/</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">double</span><span class="pun">)</span><span class="pln">N</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> </span><span class="typ">TwoPi_NP</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// أعداد مركبة</span><span class="pln">
   complex WN</span><span class="pun">;</span><span class="pln">         </span><span class="com">// ‫يمثل دالة الوزن الأسية وفق الشكل ‪  a + jb</span><span class="pln">
   complex TEMP</span><span class="pun">;</span><span class="pln">        </span><span class="com">// ‫يُستخدم لتخزين نتائج حسابات الفراشة</span><span class="pln">
   complex </span><span class="pun">*</span><span class="pln">pDFT </span><span class="pun">=</span><span class="pln"> DFT</span><span class="pun">;</span><span class="pln">    </span><span class="com">// ‫مؤشر يشير إلى العنصر الأول في مصفوفة DFT  </span><span class="pln">
   complex </span><span class="pun">*</span><span class="pln">pLo</span><span class="pun">;</span><span class="pln">       </span><span class="com">// ‫ مؤشّر يشير إلى lo / hi في حسابات الفراشة  </span><span class="pln">
   complex </span><span class="pun">*</span><span class="pln">pHi</span><span class="pun">;</span><span class="pln">
   complex </span><span class="pun">*</span><span class="pln">pX</span><span class="pun">;</span><span class="pln">              </span><span class="com">// x[n] مؤشر إلى</span><span class="pln">

   </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> nMax</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++,</span><span class="pln"> DFT</span><span class="pun">++)</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
                </span><span class="com">// ‫حساب قيمة x[n]‎‎ انطلاقا من العنوان ‎‎*x والفهرس i</span><span class="pln">
                pX </span><span class="pun">=</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">        

                </span><span class="com">// DFT[n] إعادة تعيين عنوان جديد لـ</span><span class="pln">
       ii </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">                 
       iaddr </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">          
       </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">l </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> l </span><span class="pun">&lt;</span><span class="pln"> M</span><span class="pun">;</span><span class="pln"> l</span><span class="pun">++)</span><span class="pln"> </span><span class="com">// ii وتخزين الناتج في i قلب بتات</span><span class="pln">
       </span><span class="pun">{</span><span class="pln">
           </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">iaddr </span><span class="pun">&amp;</span><span class="pln"> </span><span class="lit">0x01</span><span class="pun">)</span><span class="pln">     </span><span class="com">//  تحديد البتة الأقل أهمية</span><span class="pln">
               ii </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">MM1 </span><span class="pun">-</span><span class="pln"> l</span><span class="pun">));</span><span class="pln">  
           iaddr </span><span class="pun">&gt;&gt;=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">           
operations </span><span class="kwd">for</span><span class="pln"> speed increase
           </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">iaddr</span><span class="pun">)</span><span class="pln">
               </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       DFT </span><span class="pun">=</span><span class="pln"> pDFT </span><span class="pun">+</span><span class="pln"> ii</span><span class="pun">;</span><span class="pln">    
       DFT</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> pX</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pun">;</span><span class="pln">    
       DFT</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> pX</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pun">;</span><span class="pln">       </span><span class="com">// العدد التخيلي يساوي 0 دائما</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">stage </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> stage </span><span class="pun">&lt;=</span><span class="pln"> M</span><span class="pun">;</span><span class="pln"> stage</span><span class="pun">++)</span><span class="pln"> 
   </span><span class="pun">{</span><span class="pln">
           </span><span class="com">//‫تقسيم حسابات الفراشة إلى ‪ 2^stage</span><span class="pln">
           </span><span class="typ">BSep</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="pln">pow</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> stage</span><span class="pun">));</span><span class="pln">   

       </span><span class="com">// ‫قيم Wn المتماثلة في هذه المرحلة = ‪N/Bsep</span><span class="pln">
       P </span><span class="pun">=</span><span class="pln"> N </span><span class="pun">/</span><span class="pln"> </span><span class="typ">BSep</span><span class="pun">;</span><span class="pln">         

       </span><span class="com">// ‫عرض المرحلة الحالية في حسابات الفراشة </span><span class="pln">
       </span><span class="com">// (المسافة بين النقاط المتقابلة)</span><span class="pln">
       </span><span class="typ">BWidth</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">BSep</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">     

       </span><span class="typ">TwoPi_NP</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">TwoPi_N</span><span class="pun">*</span><span class="pln">P</span><span class="pun">;</span><span class="pln">
       </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">j </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> j </span><span class="pun">&lt;</span><span class="pln"> </span><span class="typ">BWidth</span><span class="pun">;</span><span class="pln"> j</span><span class="pun">++)</span><span class="pln"> 
       </span><span class="pun">{</span><span class="pln">
           </span><span class="com">//‫حفظ  الحسابات إن كان R = 0، حيث ‪ WN^0 = (1 + j0)</span><span class="pln">
           </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">j </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">             
           </span><span class="pun">{</span><span class="pln">
               </span><span class="com">//WN.Re = cos(TwoPi_NP*j)</span><span class="pln">
               </span><span class="com">// (‫حساب Wn (الجزء الحقيقي والتخيلي </span><span class="pln">
               WN</span><span class="pun">.</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> cos</span><span class="pun">(</span><span class="typ">TwoPi_N</span><span class="pun">*</span><span class="pln">P</span><span class="pun">*</span><span class="pln">j</span><span class="pun">);</span><span class="pln">        
               WN</span><span class="pun">.</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="pln">sin</span><span class="pun">(</span><span class="typ">TwoPi_N</span><span class="pun">*</span><span class="pln">P</span><span class="pun">*</span><span class="pln">j</span><span class="pun">);</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
           </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">HiIndex</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> j</span><span class="pun">;</span><span class="pln"> </span><span class="typ">HiIndex</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> N</span><span class="pun">;</span><span class="pln"> </span><span class="typ">HiIndex</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> </span><span class="typ">BSep</span><span class="pun">)</span><span class="pln">
           </span><span class="pun">{</span><span class="pln">
               pHi </span><span class="pun">=</span><span class="pln"> pDFT </span><span class="pun">+</span><span class="pln"> </span><span class="typ">HiIndex</span><span class="pun">;</span><span class="pln">          </span><span class="com">// التأشير إلى القيمة العليا</span><span class="pln">
               pLo </span><span class="pun">=</span><span class="pln"> pHi </span><span class="pun">+</span><span class="pln"> </span><span class="typ">BWidth</span><span class="pun">;</span><span class="pln">            </span><span class="com">// التأشير إلى القيمة السفلى</span><span class="pln">

               </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">j </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">                     
               </span><span class="pun">{</span><span class="pln">
                   </span><span class="com">//CMult(pLo, &amp;WN, &amp;TEMP);        </span><span class="pln">
                   TEMP</span><span class="pun">.</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pLo</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> WN</span><span class="pun">.</span><span class="typ">Re</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pLo</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> WN</span><span class="pun">.</span><span class="typ">Im</span><span class="pun">);</span><span class="pln">
                   TEMP</span><span class="pun">.</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pLo</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> WN</span><span class="pun">.</span><span class="typ">Im</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pLo</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> WN</span><span class="pun">.</span><span class="typ">Re</span><span class="pun">);</span><span class="pln">
                   </span><span class="com">//CSub (pHi, &amp;TEMP, pLo);</span><span class="pln">
                   pLo</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> pHi</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> TEMP</span><span class="pun">.</span><span class="typ">Re</span><span class="pun">;</span><span class="pln">    
                   pLo</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> pHi</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> TEMP</span><span class="pun">.</span><span class="typ">Im</span><span class="pun">;</span><span class="pln">
                   </span><span class="com">//CAdd (pHi, &amp;TEMP, pHi);      </span><span class="pln">
                   pHi</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pHi</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> TEMP</span><span class="pun">.</span><span class="typ">Re</span><span class="pun">);</span><span class="pln">
                   pHi</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pHi</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> TEMP</span><span class="pun">.</span><span class="typ">Im</span><span class="pun">);</span><span class="pln">
               </span><span class="pun">}</span><span class="pln">
               </span><span class="kwd">else</span><span class="pln">
               </span><span class="pun">{</span><span class="pln">
                   TEMP</span><span class="pun">.</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> pLo</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pun">;</span><span class="pln">
                   TEMP</span><span class="pun">.</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> pLo</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pun">;</span><span class="pln">
                   </span><span class="com">//CSub (pHi, &amp;TEMP, pLo);</span><span class="pln">
                   pLo</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> pHi</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> TEMP</span><span class="pun">.</span><span class="typ">Re</span><span class="pun">;</span><span class="pln">   
                   pLo</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> pHi</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> TEMP</span><span class="pun">.</span><span class="typ">Im</span><span class="pun">;</span><span class="pln">
                   </span><span class="com">//CAdd (pHi, &amp;TEMP, pHi);        </span><span class="pln">
                   pHi</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pHi</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> TEMP</span><span class="pun">.</span><span class="typ">Re</span><span class="pun">);</span><span class="pln">
                   pHi</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pHi</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> TEMP</span><span class="pun">.</span><span class="typ">Im</span><span class="pun">);</span><span class="pln">
               </span><span class="pun">}</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   pLo </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> 
   pHi </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   pDFT </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   DFT </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   pX </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	تحويل فورييه السريع المعكوس من الأساس 2
</h2>

<p>
	يمكن الحصول على تحويل فورييه المعكوس عبر تعديل ناتج تحويل فورييه السريع العادي، ويمكن تحويل البيانات في نطاق الترددات frequency domain إلى النطاق الزمني time domain بالطريقة التالية:
</p>

<ol>
	<li>
		اعثر على <a href="https://ar.wikipedia.org/wiki/%D9%85%D8%B1%D8%A7%D9%81%D9%82_%D8%B9%D8%AF%D8%AF_%D9%85%D8%B1%D9%83%D8%A8" rel="external nofollow">مرافق</a> العدد المركب لبيانات نطاق الترددات.
	</li>
	<li>
		طبّق تحويل فورييه السريع العادي على مرافق بيانات نطاق التردّد conjugated frequency domain data.
	</li>
	<li>
		اقسم كل مخرجات نتيجة تحويل فورييه السريع على N للحصول على القيمة الصحيحة للنطاق الزمني.
	</li>
	<li>
		احسب مرافق العدد المركب للعنصر الناتج عبر عكس المكوّن التخيلي لبيانات النطاق الزمني لجميع قيم n.
	</li>
</ol>

<p>
	<strong>ملاحظة</strong>: تُعدّ كل من بيانات نطاق التردد وبيانات النطاق الزمني متغيّراتٍ مركبة، وعادة ما يساوي المكونّ التخيلي للنطاق الزمني للإشارة التي تعقُب تحويل فورييه السريع المعكوس القيمة 0، وإلا فستُعد خطأ تقريبيًا وتُتجاهل. وإنّ زيادة دِقة المتغيرات من 32 بتّة عشرية 32-bit ﬂoat إلى 64 بتّة مزدوجة 64-bit double أو إلى 128 بتّة مزدوجة سيقلّص أخطاء التقريب الناتجة عن تعاقب عدّة تحويلات فورييه سريعة.
</p>

<p>
	هذا تطبيق مكتوب بلغة C / C++‎:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1962_9" style=""><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;math.h&gt;</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> PI       </span><span class="lit">3.1415926535897932384626433832795</span><span class="pln"> 
</span><span class="com">#define</span><span class="pln"> TWOPI    </span><span class="lit">6.283185307179586476925286766559</span><span class="pln">    
</span><span class="com">#define</span><span class="pln"> </span><span class="typ">Deg2Rad</span><span class="pln">  </span><span class="lit">0.017453292519943295769236907684886</span><span class="pln"> 
</span><span class="com">#define</span><span class="pln"> </span><span class="typ">Rad2Deg</span><span class="pln">  </span><span class="lit">57.295779513082320876798154814105</span><span class="pln">    
</span><span class="com">#define</span><span class="pln"> log10_2  </span><span class="lit">0.30102999566398119521373889472449</span><span class="pln">   </span><span class="com">// Log10 of 2</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> log10_2_INV </span><span class="lit">3.3219280948873623478703194294948</span><span class="pln"> </span><span class="com">// 1/Log10(2)</span><span class="pln">
</span><span class="com">// بنية لتمثل الأعداد المركبة</span><span class="pln">
</span><span class="kwd">struct</span><span class="pln"> complex
</span><span class="pun">{</span><span class="pln">
</span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln">  </span><span class="typ">Re</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Im</span><span class="pun">;</span><span class="pln">  
</span><span class="pun">};</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> rad2InverseFFT</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> N</span><span class="pun">,</span><span class="pln"> complex </span><span class="pun">*</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> complex </span><span class="pun">*</span><span class="pln">DFT</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
   </span><span class="com">// ‫تمثل M عدد المراحل التي يجب إجراؤها،‪‫ 2‪^M = N</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> </span><span class="typ">Mx</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">log10</span><span class="pun">((</span><span class="kwd">double</span><span class="pun">)</span><span class="pln">N</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> log10</span><span class="pun">((</span><span class="kwd">double</span><span class="pun">)</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> a </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pun">)(</span><span class="pln">ceil</span><span class="pun">(</span><span class="pln">pow</span><span class="pun">(</span><span class="lit">2.0</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Mx</span><span class="pun">)));</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> status </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">a </span><span class="pun">!=</span><span class="pln"> N</span><span class="pun">)</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
       x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
       DFT </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
       </span><span class="kwd">throw</span><span class="pln"> </span><span class="str">"rad2InverseFFT(): N must be a power of 2 for Radix 2 Inverse FFT"</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   complex </span><span class="pun">*</span><span class="pln">pDFT </span><span class="pun">=</span><span class="pln"> DFT</span><span class="pun">;</span><span class="pln">     
   complex </span><span class="pun">*</span><span class="pln">pX </span><span class="pun">=</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">        
   </span><span class="kwd">double</span><span class="pln"> NN </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">double</span><span class="pun">)</span><span class="pln">N</span><span class="pun">;</span><span class="pln">  </span><span class="com">//   تعديل معامل تحويل فورييه السريع المعكوس</span><span class="pln">
   </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="typ">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> N</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++,</span><span class="pln"> DFT</span><span class="pun">++)</span><span class="pln">
       DFT</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">*=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">          </span><span class="com">// حساب مرافق التردد الطيفي</span><span class="pln">
   DFT </span><span class="pun">=</span><span class="pln"> pDFT</span><span class="pun">;</span><span class="pln">           
   rad2FFT</span><span class="pun">(</span><span class="pln">N</span><span class="pun">,</span><span class="pln"> DFT</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">);</span><span class="pln"> </span><span class="com">// حساب تحويل فورييه السريع العادي</span><span class="pln">

   </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
   complex</span><span class="pun">*</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> pX</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> N</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++,</span><span class="pln"> x</span><span class="pun">++){</span><span class="pln">
       x</span><span class="pun">-&gt;</span><span class="typ">Re</span><span class="pln"> </span><span class="pun">*=</span><span class="pln"> NN</span><span class="pun">;</span><span class="pln">   </span><span class="com">// ‫تقسيم النطاق الزمني على N لتصحيح السعة</span><span class="pln">
       x</span><span class="pun">-&gt;</span><span class="typ">Im</span><span class="pln"> </span><span class="pun">*=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">    </span><span class="com">// ImX تغيير إشارة</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">   
</span><span class="pun">}</span></pre>

<p>
	ترجمة -بتصرّف- للفصل 55 من كتاب <a href="https://goalkicker.com/AlgorithmsBook/" rel="external nofollow">Algorithms Notes for Professionals</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D9%84%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-matrix-algorithms-r1536/" rel="">خوارزميات للتعامل مع المصفوفات matrix algorithms</a>
	</li>
	<li>
		المرجع الشامل إلى: <a href="https://academy.hsoub.com/programming/advanced/%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA/" rel="">تعلم الخوارزميات للمبتدئين</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D9%81%D9%8A-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-r1415/?msclkid=d7591b2cc12f11ec80205edcdcb6b153" rel="">خوارزميات البحث في النصوص</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%86-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1410/?msclkid=d758d2adc12f11eca45aa1483dd28205" rel="">أمثلة عن أنواع الخوارزميات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8-%D9%88%D8%A3%D8%B4%D9%87%D8%B1%D9%87%D8%A7-r1413/?msclkid=d75aad7dc12f11ec953e649a4a5165c1" rel="">خوارزميات الترتيب وأشهرها</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1537</guid><pubDate>Fri, 20 May 2022 15:00:00 +0000</pubDate></item><item><title>&#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x627;&#x62A; &#x644;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x645;&#x635;&#x641;&#x648;&#x641;&#x627;&#x62A; matrix algorithms</title><link>https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D9%84%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-matrix-algorithms-r1536/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_04/6260e3008d7c1_matrixalgorithms-01.jpg.7282c9a9ea74beb1d5fea8fbfe1c0cf7.jpg" /></p>

<p>
	<a href="https://ar.wikipedia.org/wiki/%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A9_(%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A7%D8%AA)" rel="external nofollow">المصفوفات</a> matrices هي إحدى مفاهيم الجبر الخطي، ولها تطبيقات في الكثير من المجالات، وسوف نستعرض في هذه المقالة خوَارزميتان من خوارزميات المصفوفات.
</p>

<h2>
	طباعة المصفوفات
</h2>

<p>
	سنبدأ بخوارزمية بسيطة لطباعة المصفوفات. تأخذ الخوارزمية مصفوفة ثنائية الأبعاد وتطبع عناصرها الواحد تلو الآخر وفق ترتيب خاص، وإليك مثال على ذلك:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9737_7" style="">
<span class="typ">Input</span><span class="pun">:</span><span class="pln"> </span><span class="pun">الدخل</span><span class="pln">
</span><span class="lit">14</span><span class="pln"> </span><span class="lit">15</span><span class="pln"> </span><span class="lit">16</span><span class="pln"> </span><span class="lit">17</span><span class="pln"> </span><span class="lit">18</span><span class="pln"> </span><span class="lit">21</span><span class="pln">
</span><span class="lit">19</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="lit">20</span><span class="pln"> </span><span class="lit">11</span><span class="pln"> </span><span class="lit">54</span><span class="pln"> </span><span class="lit">36</span><span class="pln">
</span><span class="lit">64</span><span class="pln"> </span><span class="lit">55</span><span class="pln"> </span><span class="lit">44</span><span class="pln"> </span><span class="lit">23</span><span class="pln"> </span><span class="lit">80</span><span class="pln"> </span><span class="lit">39</span><span class="pln">
</span><span class="lit">91</span><span class="pln"> </span><span class="lit">92</span><span class="pln"> </span><span class="lit">93</span><span class="pln"> </span><span class="lit">94</span><span class="pln"> </span><span class="lit">95</span><span class="pln"> </span><span class="lit">42</span><span class="pln">

</span><span class="typ">Output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">الخرج</span><span class="pln">
print value in index </span><span class="com">// اطبع القيم</span><span class="pln">
</span><span class="lit">14</span><span class="pln"> </span><span class="lit">15</span><span class="pln"> </span><span class="lit">16</span><span class="pln"> </span><span class="lit">17</span><span class="pln"> </span><span class="lit">18</span><span class="pln"> </span><span class="lit">21</span><span class="pln"> </span><span class="lit">36</span><span class="pln"> </span><span class="lit">39</span><span class="pln"> </span><span class="lit">42</span><span class="pln"> </span><span class="lit">95</span><span class="pln"> </span><span class="lit">94</span><span class="pln"> </span><span class="lit">93</span><span class="pln"> </span><span class="lit">92</span><span class="pln"> </span><span class="lit">91</span><span class="pln"> </span><span class="lit">64</span><span class="pln"> </span><span class="lit">19</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="lit">20</span><span class="pln"> </span><span class="lit">11</span><span class="pln"> </span><span class="lit">54</span><span class="pln"> </span><span class="lit">80</span><span class="pln"> </span><span class="lit">23</span><span class="pln"> </span><span class="lit">44</span><span class="pln"> </span><span class="lit">55</span><span class="pln">

or print index  </span><span class="com">// أو اطبع الفهارس</span><span class="pln">
</span><span class="lit">00</span><span class="pln"> </span><span class="lit">01</span><span class="pln"> </span><span class="lit">02</span><span class="pln"> </span><span class="lit">03</span><span class="pln"> </span><span class="lit">04</span><span class="pln"> </span><span class="lit">05</span><span class="pln"> </span><span class="lit">15</span><span class="pln"> </span><span class="lit">25</span><span class="pln"> </span><span class="lit">35</span><span class="pln"> </span><span class="lit">34</span><span class="pln"> </span><span class="lit">33</span><span class="pln"> </span><span class="lit">32</span><span class="pln"> </span><span class="lit">31</span><span class="pln"> </span><span class="lit">30</span><span class="pln"> </span><span class="lit">20</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="lit">11</span><span class="pln"> </span><span class="lit">12</span><span class="pln"> </span><span class="lit">13</span><span class="pln"> </span><span class="lit">14</span><span class="pln"> </span><span class="lit">24</span><span class="pln"> </span><span class="lit">23</span><span class="pln"> </span><span class="lit">22</span><span class="pln"> </span><span class="lit">21</span></pre>

<p>
	هذه شيفرة عامة للخوارزمية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9737_10" style="">
<span class="pln">function noOfLooping</span><span class="pun">(</span><span class="pln">m</span><span class="pun">,</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">m </span><span class="pun">&gt;</span><span class="pln"> n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       smallestValue </span><span class="pun">=</span><span class="pln"> n</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       smallestValue </span><span class="pun">=</span><span class="pln"> m</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">smallestValue </span><span class="pun">%</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           </span><span class="kwd">return</span><span class="pln"> smallestValue</span><span class="pun">/</span><span class="lit">2</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">smallestValue</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)/</span><span class="lit">2</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
function squarePrint</span><span class="pun">(</span><span class="pln">m</span><span class="pun">,</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   var looping </span><span class="pun">=</span><span class="pln"> noOfLooping</span><span class="pun">(</span><span class="pln">m</span><span class="pun">,</span><span class="pln">n</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">var i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> looping</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">var j </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln"> j </span><span class="pun">&lt;</span><span class="pln"> m </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> i</span><span class="pun">;</span><span class="pln"> j</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
               console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">i</span><span class="pun">+</span><span class="str">''</span><span class="pun">+</span><span class="pln">j</span><span class="pun">);</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">var k </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln"> k </span><span class="pun">&lt;</span><span class="pln"> n </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> i</span><span class="pun">;</span><span class="pln"> k</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
               console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">k</span><span class="pun">+</span><span class="str">''</span><span class="pun">+</span><span class="pln">j</span><span class="pun">);</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">var l </span><span class="pun">=</span><span class="pln"> j</span><span class="pun">;</span><span class="pln"> l </span><span class="pun">&gt;</span><span class="pln"> i</span><span class="pun">;</span><span class="pln"> l</span><span class="pun">--)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
               console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">k</span><span class="pun">+</span><span class="str">''</span><span class="pun">+</span><span class="pln">l</span><span class="pun">);</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">var x </span><span class="pun">=</span><span class="pln"> k</span><span class="pun">;</span><span class="pln"> x </span><span class="pun">&gt;</span><span class="pln"> i</span><span class="pun">;</span><span class="pln"> x</span><span class="pun">--)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
               console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">x</span><span class="pun">+</span><span class="str">''</span><span class="pun">+</span><span class="pln">l</span><span class="pun">);</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
squarePrint</span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="lit">4</span><span class="pun">);</span></pre>

<h2>
	ضرب المصفوفات
</h2>

<p>
	<a href="https://ar.wikipedia.org/wiki/%D8%B6%D8%B1%D8%A8_%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA" rel="external nofollow">ضرب المصفوفات</a> هي إحدى العمليات الأساسية على المصفوفات، ولها تطبيقات عديدة، مثل حلّ نِظمات المعادلات التفاضلية الخطية وغيرها، سوف نستعرض في هذه الفقرة أحد تطبيقات ضرب المصفوفات، وهو حساب أعداد فيبوناتشي.
</p>

<p>
	نعلم أنّ العثور على العنصر رقم n في <a href="https://wiki.hsoub.com/Algorithms/Fibonacci_numbers" rel="external">متتالية فيبوناتشي</a> سهل للغاية عندما يكون n صغيرًا نسبيًا، إذ يكفي أن نستخدم منظورًا عوديًا بسيطًا، حيث أنّ <code>f‎(n)=‎f‎(n-1)+‎f‎(n-2)‎</code>، أو يمكننا استخدام منظور البرمجة الديناميكية لتجنب تكرار الحسابات. لكنّ هذه الطرق لا تنفع لحل المشاكل المعقّدة، فماذا ستفعل مثلًا إن طُلِب منك حلّ هذه المشكلة:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		ليكن <strong>0 &lt; n &lt; ‏10^9‏</strong>، احسب قيمة <strong>f(n) mod 999983</strong>
	</p>
</blockquote>

<p>
	حيث تمثّل <a href="https://ar.wikipedia.org/wiki/%D8%AD%D8%B3%D8%A7%D8%A8%D9%8A%D8%A7%D8%AA_%D9%85%D8%B9%D9%8A%D8%A7%D8%B1%D9%8A%D8%A9" rel="external nofollow">mod</a> عملية حساب الباقي.
</p>

<p>
	لن تنجح البرمجة الديناميكية هنا، لذا علينا أن نبحث عن حل أفضل لهذه المشكلة، وإحدى الحلول الممكنة لهذه المشكلة هي استخدام ضرب المصفوفات لتمثيل العلاقة العَودية.
</p>

<p>
	قبل أن نواصل، يُستحبّ أن تكون لك معرفة أولية بالمفاهيم الرياضية الآتية.
</p>

<ul>
<li>
		لتكن A و B مصفوفتين، يجب أن تكون قادرًا على حساب ضرب هاتين المصفوفتين، أي BxA.
	</li>
	<li>
		لتكن A و B مصفوفتين، يجب أن تكون قادرًا على إيجاد المصفوفة T التي تحقق B = TxA.
	</li>
	<li>
		لتكن A مصفوفة أبعادها d X d، يجب أن تكون قادرًا على إيجاد قوّتها إلى n (في مدّة <code>O(d3log(n))‎‎</code>).
	</li>
</ul>
<p>
	سنحتاج في البداية إلى إنشاء مصفوفة M تمثّل العلاقة العَودية، بحيث يمكننا انطلاقًا منها حساب الحالة / القيمة المطلوبة انطلاقًا من الحالات / القيم المعروفة سلفًا من المتتالية.
</p>

<p>
	لنفترض أنّنا نعرف قيمة k حالة سابقة من المتتالية العودية، نريد أن نحسب قيمة الحالة رقم (k +1) من المتتالية انطلاقًا من الحالات المعروفة. لتكن M مصفوفة حجمها k X k، سنبني مصفوفة A حجمها k X 1 تحتوى الحالات المعروفة لعلاقة العوديّة، وسننشئ أيضًا مصفوفة B حجمها k X 1 تحتوي قيم الحالات الموالية، أي تحقق MXA = B:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_9737_14" style="">
<span class="pln">           </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">    </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">
           </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
  M X      </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">2</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="pun">=</span><span class="pln">  </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">
           </span><span class="pun">|</span><span class="pln"> </span><span class="pun">......</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln"> </span><span class="pun">......</span><span class="pln">   </span><span class="pun">|</span><span class="pln">
           </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="pln">k</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="pln">k</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">|</span></pre>

<p>
	إن استطعنا تصميم المصفوفة M وفقًا لهذه الخاصيات، فسنكون قادرين على حل المشكلة بيُسر. سنجرّب هذه الطريقة على عدةّ أنواع من علاقات العوديّة.
</p>

<h3>
	النوع 1
</h3>

<p>
	لنبدأ بالحالة الأبسط: <code>‎f(n) = f(n-1) + f(n-2)‎</code>، سنحصل على: <code>‎f(n+1) = f(n) + f(n-1)‎</code>.
</p>

<p>
	لنفترض أنّنا نعرف قيمتي <code>‎f(n)‎</code> و <code>‎f(n-1)‎</code>، ونريد أن نحسب قيمة <code>‎f(n+1)‎</code> انطلاقًا من القيمتين السابقتين. نبدأ بإنشاء المصفوفتين A و B كما وضّحنا سابقًا:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_9737_16" style="">
<span class="pln">    </span><span class="typ">Matrix</span><span class="pln"> A             </span><span class="typ">Matrix</span><span class="pln"> B
 </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">     </span><span class="pun">|</span><span class="pln">         </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
 </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">         </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">    </span><span class="pun">|</span></pre>

<p>
	لاحظ أن المصفوفة A تُصمّم بحيث تحتوي كل الحالات التي تعتمد عليها <code>‎f(n+1)‎</code>، وعلينا الآن تصميم مصفوفة M حجمها 2X2 تحقّق MxA = B.
</p>

<p>
	العنصر الأول من B هو <code>‎f(n+1)‎</code>، ويساوي <code>‎f(n) + f(n-1)‎</code> بحسب علاقة العوديّة. نحن نعلم أنّ العنصر الأول من B يساوي الصف الأول من M مضروبًا في A حسبَ قواعد ضرب المصفوفات، أي أنّ <code>‎f(n)*a + f(n-1)*b = f(n+1)‎</code>، حيث [a b‏‏] يمثّل الصف الأول من المصفوفة M، من جهة أخرى نعلم من علاقة التعود أنّ <code>‎f(n+1)=f(n)+f(n-1)‎</code>، لهذا نستنتج أنّ الصفّ الأول من M سيكون [1 1].
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_9737_19" style="">
<span class="pun">|</span><span class="pln"> </span><span class="lit">1</span><span class="pln">    </span><span class="lit">1</span><span class="pln"> </span><span class="pun">|</span><span class="pln">   X   </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">  </span><span class="pun">=</span><span class="pln">   </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">|</span><span class="pln"> </span><span class="pun">-----</span><span class="pln">  </span><span class="pun">|</span><span class="pln">       </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln"> </span><span class="pun">------</span><span class="pln">  </span><span class="pun">|</span></pre>

<p>
	<strong>ملاحظة</strong>: يشير الرمز ----- إلى عدم اهتمامنا بهذه القيمة حاليًا.
</p>

<p>
	يساوي العنصر الثاني من B القيمة <code>‎f(n)‎</code>، والتي يمكن الحصول عليها عن طريق العملية f(n) x 1، ومن ثم فإنّ الصف الثاني من M هو [1 0].
</p>

<pre class="ipsCode" id="ips_uid_9737_24">
| ----- |  X  |  f(n)   |  =  | ------ |
| 1 0   |     | f(n-1)  |     |  f(n)  |
</pre>

<p>
	لقد حصلنا الآن على المصفوفة M المطلوبة.
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_9737_22" style="">
<span class="pun">|</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   X   </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">  </span><span class="pun">=</span><span class="pln">  </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">|</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">       </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span></pre>

<h3>
	النوع 2
</h3>

<p>
	سنحاول الآن حساب: <code>‎f(n) = a X f(n-1) + b X f(n-2)‎</code>، حيث a و b ثابتتان.
</p>

<p>
	تكافئ الصيغة أعلاه المعادلة: <code>‎f(n+1) = a X f(n) + b X f(n-1)‎</code>.
</p>

<p>
	قد تعلم الآن أنّه ينبغي أن تتساوي أبعاد المصفوفات مع عدد التبعيات، والتي تساوي 2 في هذا المثال، لذا سنبني المصفوفتين A و B، وكلاهما من الحجم 2x1:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_9737_26" style="">
<span class="pln">   </span><span class="typ">Matrix</span><span class="pln"> A            </span><span class="typ">Matrix</span><span class="pln"> B
 </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">    </span><span class="pun">|</span><span class="pln">         </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">|</span><span class="pln">
 </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">         </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span></pre>

<p>
	نحتاج أن تكون [a, b] في الصفّ الأول من المصفوفة M بالنسبة لـ <code>‎f(n+1) = a X f(n) + b X f(n-1)‎</code>، وبالنسبة للعنصر الثاني فيB، أي <code>‎f(n)‎</code>، فهو موجود في المصفوفة A، لذلك سنأخذه، ولهذا نضع [1 0] في الصف الثاني من M لنحصل على:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_9737_28" style="">
<span class="pun">|</span><span class="pln"> a   b </span><span class="pun">|</span><span class="pln">  X   </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">  </span><span class="pun">=</span><span class="pln">  </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">|</span><span class="pln">
</span><span class="pun">|</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">0</span><span class="pln">   </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span></pre>

<h3>
	النوع 3
</h3>

<p>
	نأخذ الآن الحالة التالية: احسب قيمة <code>‎f(n) = a x f ‎(n-1) +‎ c x f ‎(n-3)‎</code>.
</p>

<p>
	لقد كانت الحالات -القيم السابقة التي تُحسب العودية انطلاقًا منها- في المثالين السابقين متجاورات، أمّا هنا فهي غير متجاورة، فالحالة f (n-2)‎‎ مفقودة، فكيف نتعامل مع هذا؟
</p>

<p>
	يمكننا تحويل علاقة العودية إلى العلاقة التالية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9737_46" style="">
<span class="pun">‎</span><span class="pln">f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> a X f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> X f </span><span class="pun">‎(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> c X f </span><span class="pun">‎(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">3</span><span class="pun">)</span><span class="pln">
</span><span class="com">// أو</span><span class="pln">
</span><span class="pun">‎</span><span class="pln">f</span><span class="pun">(</span><span class="pln">n </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> a X f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> X f </span><span class="pun">‎(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> c X f </span><span class="pun">‎(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span></pre>

<p>
	شبه هذه الصيغةُ صيغةَ النوع 2.
</p>

<p>
	هذه هي المصفوفة M، والتي ستكون من الحجم 3x3. لقد حسبنا المصفوفة M بطريقة مشابهة للطريقة التي استخدمناها في النوع 2، ويمكنك تجربتها بالورقة والقلم إن وجدت صعوبة في فهمها.
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_9737_30" style="">
<span class="pun">|</span><span class="pln"> a </span><span class="lit">0</span><span class="pln"> c </span><span class="pun">|</span><span class="pln">       </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">|</span><span class="pln">
</span><span class="pun">|</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">|</span><span class="pln">   X   </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">|</span><span class="pln">  </span><span class="pun">=</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">|</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">|</span><span class="pln">       </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">|</span></pre>

<h3>
	النوع 4
</h3>

<p>
	نأخذ الآن مثالًا أكثر تعقيدًا: <code>‎f(n) = f(n-1) + f(n-2) + c‎</code>، حيث تكون c ثابتًا ما. يختلف هذا المثال عن سابقيه، فقد كنا نحوّل في السابق كل حالة في A إلى الحالة التالية في B عبر عملية الضرب.
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_9737_32" style="">
<span class="pln">f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">     </span><span class="pun">=</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">+</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">2</span><span class="pun">)</span><span class="pln">  </span><span class="pun">+</span><span class="pln"> c
f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">   </span><span class="pun">=</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">    </span><span class="pun">+</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">+</span><span class="pln"> c
f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">2</span><span class="pun">)</span><span class="pln">   </span><span class="pun">=</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">+</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">    </span><span class="pun">+</span><span class="pln"> c
</span><span class="pun">................................</span></pre>

<p>
	أما هنا فلا تصلح الطريقة التي اعتمدنَاها سابقًا، وعلينا أن نبحث في طريقة أخرى. ماذا لو جرّبنا إضَافة <strong>c</strong> كحالة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_9737_34" style="">
<span class="pln">          </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">    </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
  M X     </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">  </span><span class="pun">=</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">
          </span><span class="pun">|</span><span class="pln">    c     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     c    </span><span class="pun">|</span></pre>

<p>
	يمكننا الآن إنشاء M بسهولة:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_9737_36" style="">
<span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">|</span><span class="pln">       </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">    </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">|</span><span class="pln">
 </span><span class="pun">|</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">|</span><span class="pln">   X   </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">  </span><span class="pun">=</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
 </span><span class="pun">|</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">|</span><span class="pln">       </span><span class="pun">|</span><span class="pln">    c     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">    c   </span><span class="pun">|</span></pre>

<h3>
	النوع 5
</h3>

<p>
	لنأخذ الآن مثالًا آخر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9737_48" style="">
<span class="pun">‎</span><span class="pln">f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> a X f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> c X f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> d X f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">4</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> e</span><span class="pun">‎</span></pre>

<p>
	هذا تمرين لك، حاول أولاً تحديد حالات العوديّة، وأنشئ المصفوفتين A و B، ثمّ احسب المصفوفة M:
</p>

<p>
	هذه قيمة M:
</p>

<pre class="ipsCode">
| a 0 c d 1 |
| 1 0 0 0 0 |
| 0 1 0 0 0 |
| 0 0 1 0 0 |
| 0 0 0 0 1 |
</pre>

<h3>
	النوع 6
</h3>

<p>
	هناك أشكال كثيرة من العودية، منها الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9737_38" style="">
<span class="pln">f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> n is odd    </span><span class="com">// في الحالات الفردية</span><span class="pln">
f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> n is even  </span><span class="com">// في الحالات الزوجية</span></pre>

<p>
	أو باختصار:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9737_40" style="">
<span class="pln">f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n</span><span class="pun">&amp;</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> X f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(!(</span><span class="pln">n</span><span class="pun">&amp;</span><span class="lit">1</span><span class="pun">))</span><span class="pln"> X f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">2</span><span class="pun">)</span></pre>

<p>
	يمكننا تقسيم الدوال على أساس الفردية أو الزوجية، وإنشاء مصفوفتين تمثّل إحداهما الفردية، وتمثّل الأخرى الزوجية، ثمّ نحُسب كل واحدة منهما على حدة.
</p>

<h3>
	النوع 7
</h3>

<p>
	قد تحتاج أحيانًا إلى الحفاظ على أكثر من علاقة عودية واحدة كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9737_42" style="">
<span class="pln">g</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2g</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2g</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span></pre>

<p>
	لاحظ أنّ قيمة g (n)‎<code>‎</code> تعتمد على <code>‎f(n)‎</code>.
</p>

<p>
	يمكننا استخدام ضرب المصفوفات كما في الأمثلة السابقة ولكن بأبعاد أكبر. ننشئ أولا المصفوفتين A و B.
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_9737_44" style="">
<span class="pln">  </span><span class="typ">Matrix</span><span class="pln"> A                 </span><span class="typ">Matrix</span><span class="pln"> B
</span><span class="pun">|</span><span class="pln">  g</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">    </span><span class="pun">|</span><span class="pln">             </span><span class="pun">|</span><span class="pln"> g</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">|</span><span class="pln"> g</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">             </span><span class="pun">|</span><span class="pln">  g</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">
</span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">   </span><span class="pun">|</span><span class="pln">             </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">2</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">    </span><span class="pun">|</span><span class="pln">             </span><span class="pun">|</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">  </span><span class="pun">|</span></pre>

<p>
	لدينا: <code>‎g(n+1) = 2g(n-1) + f(n+1)‎‎ و f(n+2) = 2f(n+1) + 2f(n)‎</code>. لننشئ المصفوفة M بالطريقة نفسها التي استخدمناها آنفًا:
</p>

<pre class="ipsCode">
| 2 2 1 0 |
| 1 0 0 0 |
| 0 0 2 2 |
| 0 0 1 0 |
</pre>

<p>
	ترجمة -بتصرّف- للفصلين 51 و 52 من الكتاب <a href="https://goalkicker.com/AlgorithmsBook/" rel="external nofollow">Algorithms Notes for Professionals</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D8%AA%D8%A7%D9%84%D9%8A%D8%A7%D8%AA-subsequences-algorithms-r1535/" rel="">خوارزميات المتتاليات subsequences algorithms</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1345/?msclkid=165f062bc12f11ecadf347424ebee2f1" rel="">مدخل إلى تحليل الخوارزميات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%86-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1410/?msclkid=165d3c7ac12f11ecbc7ec299bbe55871" rel="">أمثلة عن أنواع الخوارزميات</a><span style="display: none;"> </span>
	</li>
</ul>
]]></description><guid isPermaLink="false">1536</guid><pubDate>Thu, 21 Apr 2022 04:56:19 +0000</pubDate></item><item><title>&#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x62A;&#x62A;&#x627;&#x644;&#x64A;&#x627;&#x62A; subsequences algorithms</title><link>https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D8%AA%D8%A7%D9%84%D9%8A%D8%A7%D8%AA-subsequences-algorithms-r1535/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_04/6260e2a9e6178_subsequences-01.jpg.59630f6f411b5c146f467be03d35cc33.jpg" /></p>

<p>
	تستعرض هذه المقالة بعض الخوارزميات المتعلقة بالمتتاليات الرياضية، مثل خوارزمية أطول متتالية جزئية مشتركة، وخوارزمية أطول متتالية متزايدة، وخوارزمية الحيد الزمني الديناميكي.
</p>

<h2>
	أطول متتالية جزئية مشتركة Longest Common Subsequence
</h2>

<p>
	سوف نعرّف بعض المصطلحات الأساسية أولًا:
</p>

<p>
	<strong>المتتالية الجزئية Subsequence</strong>: في الرياضيات، المتتالية الجزئية هي متتالية مشتقة من متتالية أخرى، بحيث يمكن الحصول عليها بحذف بعض عناصر المتتالية الضامّة دون المساس بترتيب عناصرها.
</p>

<p>
	لتكن ABC سلسلة نصية، سنحصل عند حذف حرف واحد أو أكثر من هذه السلسلة النصية على متتالية جزئية من هذه السلسلة النصية، لذا فإنّ السلاسل النصية {‎ ‎A - B - C - AB - AC - BC - ABC …إلخ} كلها متتاليات جزئية من ABC، بل حتى السلسلة الفارغة (الناتجة عن إزالة جميع الأحرف) تُعدّ أيضًا متتالية جزئية.
</p>

<p>
	وهناك طريقة سهلة لاستخراج المتتاليات الجزئية من سلسلة نصية، بالمرور على كل حرف من حروف السلسلة النصية، وأمامك خياران، إمّا أن تأخذه أو تتركه، ومهما كانت خياراتك ستكون المتتالية المؤلّفة من الحروف التي أخذتها متتالية جزئية، كذلك إن كان طول السلسلة النصية يساوي n، فيمكن أن نستخرج منها 2n متتالية جزئية.
</p>

<p>
	<strong>أطول متتالية جزئية مشتركة longest Common Subsequence</strong>: لتكن <strong>س</strong> و <strong>ش</strong> سلسلتين نصيتين، ولتكن <strong>ج</strong> المجموعة المؤلفة من كل المتتاليات الجزئية في <strong>س</strong>، ولتكن <strong>ح</strong> المجموعة المؤلفة من جميع المتتاليات الجزئية في <strong>ش</strong>. هناك على الأقل متتالية جزئية واحدة مشتركة بين هاتين المجموعتين، وهي المتتالية الفارغة، لأنّ المتتالية الفارغة تُعدّ دائما متتالية جزئية من كل المتتاليات.
</p>

<p>
	<a href="https://ar.wikipedia.org/wiki/%D8%A3%D8%B7%D9%88%D9%84_%D8%AA%D8%B3%D9%84%D8%B3%D9%84_%D9%85%D8%B4%D8%AA%D8%B1%D9%83_(%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A7%D8%AA)" rel="external nofollow">أطول متتالية جزئية مشتركة</a> LCS بين السلسلتين النصيتين س و ش هي أطول عنصر مشترك بين المجموعتين ج و ح، فمثلًا: المتتاليات الجزئية المشتركة بين السلسلتين النصيتين HELLOM وHMLD هي H و HL و HM، والسلسلة النصية HLL هي الأطول من بين كل هذه المتتاليات، لذا فهي أطول متتالية جزئية مشتركة بين HMLD و HELLOM.
</p>

<p>
	هناك عدّة طرق للعثور على أطول متتالية جزئية مشتركة، منها الطريقة العنيفة وطريقة البرمجة الديناميكية.
</p>

<p>
	<strong>الطريقة العنيفة</strong>: يمكننا إنشاء جميع المتتاليات الجزئية للسلسلتين النصيتين ثم نوازن بينها لنخرج بالمتتاليات الجزئية المشتركة بينهما، ثم سنحاول العثور على أطول عنصر / متتالية مشتركة. بما أننا قد رأينا أنّ هناك <strong>2n</strong> متتالية جزئية لكل سلسلة نصية طولها <strong>n</strong>، فنحن نعلم أننا سنستغرق أعوامًا من أجل حل المشكلة إن كانت n أكبر من 20، وبناءً على ذلك فهي غير عملية، وهناك طريقة أفضل لإيجاد أطول متتالية جزئية مشتركة، وهي <a href="https://academy.hsoub.com/programming/advanced/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%AF%D9%8A%D9%86%D8%A7%D9%85%D9%8A%D9%83%D9%8A%D8%A9-r1365/" rel="">البرمجة الديناميكية</a>.
</p>

<p>
	<strong>طريقة البرمجة الديناميكية</strong>: لنفترض أنّ لدينا سلسلتين نصيتين abcdaf و acbcf نرمز لهما بالرمزين s1 و s2، وأطول متتالية جزئية مشتركة بين هاتين السلسلتين النصيتين هي abcf، وطولها يساوي 4. بما أن المتتاليات الجزئية لا يشترط أن تكون متواصلة، فيمكن إنشاء المتتالية abcf عبر تجاهل da في s1 و c في s2. والآن، كيف نعرف هذا باستخدام البرمجة الديناميكية؟
</p>

<p>
	سنبدأ بإنشاء جدول (مصفوفة ثنائية الأبعاد) تحتوي جميع أحرف s1 في أحد صفوفها، وتحتوي جميع أحرف s2 في أحد أعمدتها، ونفهرس الجدول انطلاقًا من 0، ونضع الأحرف في المصفوفة ابتداءً من الفهرس 1، وسيبدو الجدول كما يلي:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_2265_25" style="">
<span class="pln">               </span><span class="lit">0</span><span class="pln">     </span><span class="lit">1</span><span class="pln">     </span><span class="lit">2</span><span class="pln">     </span><span class="lit">3</span><span class="pln">     </span><span class="lit">4</span><span class="pln">     </span><span class="lit">5</span><span class="pln">     </span><span class="lit">6</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln"> ch</span><span class="pun">ʳ</span><span class="pln"> </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  b  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  d  </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  f  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  b  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">4</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  f  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span></pre>

<p>
	تمثل كل خانة Table[‎i][j]‎‎ من خانات الجدول طول أطول متتالية جزئية مشتركة بين السلسلتين النصيتين t1 و t2، حيث تساوي t1 سابقة السلسلة النصية s1 التي تبدأ من بدايتها وحتى الحرف رقم j، وتساوي t2 سابقة السلسلة النصية s2 التي تبدأ من بدايتها وحتى الحرف رقم i. على سبيل المثال: تمثّل الخانة Table[2][3]‎‎ طول أطول متتالية جزئية مشتركة بين ac و abc.
</p>

<p>
	يمثّل العمود رقم 0 المتتالية الجزئية الفارغة من s1، وبالمثل فإن الصفّ رقم 0 يمثّل المتتالية الجزئية الفارغة من s2. إذا أخذنا متتالية جزئية فارغة من سلسلة نصية وحاولنا مطابقتها بسلسلة نصية أخرى، فسيساوي طول المتتالية الجزئية المشتركة 0 مهما كان طول السلسلة النصية الفرعية الثانية، لهذا نملأ خانات الصف الأوّل والعمود الأوّل بالقيمة 0، ونحصل على:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_2265_33" style="">
<span class="pln">               </span><span class="lit">0</span><span class="pln">     </span><span class="lit">1</span><span class="pln">     </span><span class="lit">2</span><span class="pln">     </span><span class="lit">3</span><span class="pln">     </span><span class="lit">4</span><span class="pln">     </span><span class="lit">5</span><span class="pln">     </span><span class="lit">6</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln"> ch</span><span class="pun">ʳ</span><span class="pln"> </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  b  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  d  </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  f  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  b  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">4</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  f  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span></pre>

<p>
	نريد الآن ملء الخانة Table[1][1]‎‎، إن كان لدينا سلسلتين نصيتين a و a أخرى، ما هي أطول متتالية جزئية مشتركة يمكننا الحصول عليها هنا؟
</p>

<p>
	نحن نعلم أنّ طول أطول متتالية جزئية مشتركة بين السلسلة النصية a و a هو 1، فنذهب الآن إلى Table[1][2]‎‎ التي تمثّل طول أطول متتالية جزئية مشتركة بين ab و a، والتي تساوي 1، وكما ترى فإن جميع قيم الصف رقم 1 تساوي القيمة 1، وذلك لأنه يحتوي على طول أطول متتالية جزئية مشتركة بين a والسلاسل abcd و abcda و abcdaf.
</p>

<p>
	سيبدو الجدول كما يلي:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_2265_35" style="">
<span class="pln">               </span><span class="lit">0</span><span class="pln">     </span><span class="lit">1</span><span class="pln">     </span><span class="lit">2</span><span class="pln">     </span><span class="lit">3</span><span class="pln">     </span><span class="lit">4</span><span class="pln">     </span><span class="lit">5</span><span class="pln">     </span><span class="lit">6</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln"> ch</span><span class="pun">ʳ</span><span class="pln"> </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  b  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  d  </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  f  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  b  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">4</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  f  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span></pre>

<p>
	نذهب الآن إلى الصف رقم 2 الذي يتضمن الحرف c. تحتوي الخانة Table[2][1]‎‎ طول أكبر متتالية جزئية مشتركة بين السلسلتين النصيتين ac و a والذي يساوي 1، وقد حصلنا على هذه القيمة من القيمة الموجودة في الصف الأعلى، ذلك أنّه إذا كان الحرفان s1 [2]‎‎ و s2 [1]‎‎ غير متساويين فلا بدّ أن يساوي طول أكبر متتالية جزئية مشتركة الحد الأقصى لقيمة LCS (أطول متتالية جزئية مشتركة) الموجودة في الأعلى أو على اليسار.
</p>

<p>
	إن أخذنا قيمة LCS الموجودة في الأعلى فذلك يعني أنّنا تجاهلنا الحرف الحالي في السلسلة النصية s2، وبالمثل إن أخذنا قيمة LCS الموجودة على اليسار فذلك يعني أنّنا تجاهلنا الحرف الحالي في s1.
</p>

<p>
	نحصل على هذا الجدول:
</p>

<pre class="ipsCode" id="ips_uid_2265_37">
               0     1     2     3     4     5     6
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|     | chʳ |     |  a  |  b  |  c  |  d  |  a  |  f  |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|  0  |  0  |  0  |  0  |  0  |  0  |  0  |  0  |  0  |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|  1  |  a  |  0  |  1  |  1  |  1  |  1  |  1  |  1  |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|  2  |  c  |  0  |  1  |     |     |     |     |     |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|  3  |  b  |  0  |     |     |     |     |     |     |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|  4  |  c  |  0  |     |     |     |     |     |     |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|  5  |  f  |  0  |     |     |     |     |     |     |
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
</pre>

<p>
	هذه صيغة الحالة الأولى:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_39" style="">
<span class="kwd">if</span><span class="pln"> s2</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> is not equal to s1</span><span class="pun">[</span><span class="pln">j</span><span class="pun">]</span><span class="pln">
    </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">j</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> max</span><span class="pun">(</span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">j</span><span class="pun">],</span><span class="pln"> </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">j</span><span class="pun">-</span><span class="lit">1</span><span class="pun">]</span><span class="pln">
endif</span></pre>

<p>
	ننتقل الآن إلى Table[2][2]‎‎ التي تحوي طول أطول متتالية جزئية مشتركة بين ab و ac. بما أن الحرفين الأخيرين من هاتين السلسلتين مختلفان (c و b)، فسنَأخذ الحدّ الأقصى الموجود في الأعلى أو على اليسار، وهو 1 في هذه الحالة.
</p>

<p>
	ثم بعد ذلك ننتقل إلى Table[2][3]‎‎ والتي تحتوي طول أكبر متتالية جزئية مشتركة بين abc و ac، ستتساوى القيمتان الحاليتان في كل من الصف والعمود هذه المرة، لذا فإن طول المتتالية الجزئية المشتركة الأطول يساوي قيمة LCS القصوى الحالية + 1.
</p>

<p>
	ولكي تحصل على قيمة LCS القصوى الحالية عليك أن تتحقق من القيمة القطرية diagonal value، والتي تمثل أفضل تطابق بين ab و a، ومن هذه الحالة نضيف محرفًا من محارف s1 وآخر من s2، وقد وجدنا أنّهما متساويين -أي s1 و s2-، لذا سيزيد طول أكبر متتالية جزئية مشتركة حتمًا. نضع القيمة 1 + 1 = 2 في الخانة Table[2][3]‎‎ لنحصل على:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_2265_41" style="">
<span class="pln">               </span><span class="lit">0</span><span class="pln">     </span><span class="lit">1</span><span class="pln">     </span><span class="lit">2</span><span class="pln">     </span><span class="lit">3</span><span class="pln">     </span><span class="lit">4</span><span class="pln">     </span><span class="lit">5</span><span class="pln">     </span><span class="lit">6</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln"> ch</span><span class="pun">ʳ</span><span class="pln"> </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  b  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  d  </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  f  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  b  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">4</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  f  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span></pre>

<p>
	هذه صيغتنا الثانية:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_43" style="">
<span class="kwd">if</span><span class="pln"> s2</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> equals to s1</span><span class="pun">[</span><span class="pln">j</span><span class="pun">]</span><span class="pln">
    </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">j</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">j</span><span class="pun">-</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
endif</span></pre>

<p>
	لقد عرّفنا كلا الحالتين، سنملأ الآن الجدول كله باستخدام هاتين الصيغتين، وسيبدو بعد أن نتمّ ملأه كما يلي:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_2265_45" style="">
<span class="pln">               </span><span class="lit">0</span><span class="pln">     </span><span class="lit">1</span><span class="pln">     </span><span class="lit">2</span><span class="pln">     </span><span class="lit">3</span><span class="pln">     </span><span class="lit">4</span><span class="pln">     </span><span class="lit">5</span><span class="pln">     </span><span class="lit">6</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln"> ch</span><span class="pun">ʳ</span><span class="pln"> </span><span class="pun">|</span><span class="pln">     </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  b  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  d  </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  f  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  a  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  b  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">4</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  c  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">  </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  f  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">4</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+-----+-----+-----+-----+-----+-----+-----+-----+-----+</span></pre>

<p>
	نستنتج من الجدول أنّ طول أكبر متتالية جزئية مشتركة بين <strong>s1</strong> و <strong>s2</strong> هو <strong>Table[5][6] = 4</strong>. لاحظ أنّ 5 و 6 هما طولا <strong>s2</strong> و <strong>s1</strong> على التوالي.
</p>

<p>
	انظر المثال التوضيحي التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_47" style="">
<span class="typ">Procedure</span><span class="pln"> </span><span class="typ">LCSlength</span><span class="pun">(</span><span class="pln">s1</span><span class="pun">,</span><span class="pln"> s2</span><span class="pun">):</span><span class="pln">
</span><span class="typ">Table</span><span class="pun">[</span><span class="lit">0</span><span class="pun">][</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> i from </span><span class="lit">1</span><span class="pln"> to s1</span><span class="pun">.</span><span class="pln">length
   </span><span class="typ">Table</span><span class="pun">[</span><span class="lit">0</span><span class="pun">][</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
endfor
</span><span class="kwd">for</span><span class="pln"> i from </span><span class="lit">1</span><span class="pln"> to s2</span><span class="pun">.</span><span class="pln">length
   </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
endfor
</span><span class="kwd">for</span><span class="pln"> i from </span><span class="lit">1</span><span class="pln"> to s2</span><span class="pun">.</span><span class="pln">length
   </span><span class="kwd">for</span><span class="pln"> j from </span><span class="lit">1</span><span class="pln"> to s1</span><span class="pun">.</span><span class="pln">length
       </span><span class="kwd">if</span><span class="pln"> s2</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> equals to s1</span><span class="pun">[</span><span class="pln">j</span><span class="pun">]</span><span class="pln">
           </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">j</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">j</span><span class="pun">-</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
       </span><span class="kwd">else</span><span class="pln">
           </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">j</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> max</span><span class="pun">(</span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">j</span><span class="pun">],</span><span class="pln"> </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">j</span><span class="pun">-</span><span class="lit">1</span><span class="pun">])</span><span class="pln">
       endif
   endfor
endfor
</span><span class="typ">Return</span><span class="pln"> </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">s2</span><span class="pun">.</span><span class="pln">length</span><span class="pun">][</span><span class="pln">s1</span><span class="pun">.</span><span class="pln">length</span><span class="pun">]</span></pre>

<p>
	التعقيد الزمني لهذه الخوارزمية يساوي O(mn)‎‎، حيث يمثّل m و n طولي السلسلتين النصيتين.
</p>

<p>
	ها قد عرفنا طول أكبر متتالية جزئية مشتركة، فكيف نستخرج هذه المتتالية الجزئية؟
</p>

<p>
	من أجل معرفة أطول متتالية جزئية مشتركة، سننشئ <a href="https://wiki.hsoub.com/Algorithms/stacks" rel="external">مكدسًا</a> لتخزين محارفها، وسنبدأ من الزاوية اليمنى السفلى باحثين عن المصدر الذي أتت منه القيمة، فإذا كانت القيمة قادمة من القطر diagonal -أي إذا كانت Table[‎i][j] - 1 = Table[i-1][j-1]‎‎- فسندفع إمّا [‎i]s2 ‎‎ أو s1 [j]‎‎ (كلاهما متساويان) إلى المكدّس ثمّ نتحرك قُطريًا.
</p>

<p>
	أمّا إذا كانت القيمة آتية من الأعلى -أي إن كانت Table[‎i][j] = Table[i-1][j]‎‎- فإننا ننتقل إلى الأعلى، وإذا كانت القيمة آتية من اليسار، أي إن كانت Table[‎i][j] = Table[‎i][j-1]‎‎، فإننا ننتقل إلى اليسار.
</p>

<p>
	ينتهي بحثنا عندما نصل إلى العمود الموجود في الأعلى أو في أقصى اليسار، وننزع القيم المخزّنة في <a href="https://wiki.hsoub.com/Algorithms/stacks" rel="external">المكدّس</a> ثمّ نطبعها.
</p>

<p>
	انظر المثال التوضيحي:
</p>

<pre class="ipsCode">
Procedure PrintLCS(LCSlength, s1, s2)
temp := LCSlength
S = stack()
i := s2.length
j := s1.length
while i is not equal to 0 and j is not equal to 0
    if Table[i-1][j-1] == Table[i][j] - 1 and s1[j]==s2[i]

       S.push(s1[j])   //or S.push(s2[i])
       i := i - 1
       j := j - 1
   else if Table[i-1][j] == Table[i][j]
       i := i-1
   else
       j := j-1
   endif
endwhile
while S is not empty
   print(S.pop)
endwhile
</pre>

<p>
	<strong>ملاحظة</strong>: إذا كان كلّ من Table[i-1][j]‎‎ و Table[‎i][j-1]‎‎ يساويان Table[‎i][j]‎‎، ولم يكن Table[i-1][j-1]‎‎ مساويًا للقيمة Table[‎i][j] - 1، فذلك يعني أنّه قد تكون هناك أكثر من متتالية جزئية مشتركة طولى (أي كل واحدة منها تعدّ أطول متتالية جزئية مشتركة)، ولا يأخذ المثال التوضيحي المذكور آنفًا هذا الأمر في الحسبان. إن أردت العثور على جميع المتتاليات الجزئية المشتركة القصوية، فسيكون عليك استخدام العوديّة.
</p>

<p>
	التعقيد الزمني لهذه الخوارزمية هو: O (max (m, n))‎‎.
</p>

<h2>
	أطول متتالية جزئية متزايدة Longest Increasing Subsequence
</h2>

<p>
	<a href="https://ar.wikipedia.org/wiki/%D8%A3%D8%B7%D9%88%D9%84_%D9%85%D8%AA%D8%AA%D8%A7%D9%84%D9%8A%D8%A9_%D8%AC%D8%B2%D8%A6%D9%8A%D8%A9_%D9%85%D8%AA%D8%B2%D8%A7%D9%8A%D8%AF%D8%A9" rel="external nofollow">أطول متتالية جزئية متزايدة</a>، هي أطول متتالية جزئية عناصرها مرتبة تصاعديًا أو تنازليًا.
</p>

<p>
	تُستخدم خوارزميات حساب أطول متتالية جزئية متزايدة في العديد من التطبيقات مثل أنظمة إدارة الإصدارات Git وغيرها، وفيما يلي وصفٌ مبسّط للخوارزمية التي تعتمدها أنظمة إدارة الإصدارات للموازنة بين ملفّين يمثّلان إصدارين مختلفين من الملف نفسه:
</p>

<ol>
<li>
		ابحث عن الأسطر المشتركة في كلا الملفين.
	</li>
	<li>
		خذ كل هذه الأسطر من الملف الأوّل ثم رتّبها بحسب ظهورها في الملف الثاني.
	</li>
	<li>
		اعثر على أطول متتالية جزئية متزايدة LIS للمتتالية الناتجة (باستخدام خوارزمية ترتيب الصبر patience sorting). ستحصل على أطول متتالية متطابقة من الأسطر، والتي تمثّل التقابلات بين السطور المشتركة في الملفّين.
	</li>
	<li>
		كرّر الخوارزمية عوديًا على كل مجال من الأسطر بين السطور المُتطابقة.
	</li>
</ol>
<p>
	لننظر الآن في مثال بسيط على مشكلة أطول متتالية جزئية متزايدة:
</p>

<p>
	مدخلات المشكلة هي متتالية من الأعداد الصحيحة المختلفة <code>a1,a2,...,an.‎‎</code>، ونريد أن نجد أطول متتالية جزئية متزايدة. إذا كانت المدخلات تُساوي <code>7,3,8,4,2,6</code> مثلًا، فإنّ أطول متتالية جزئية متزايدة فيها هي <code>3,4,6</code>.
</p>

<p>
	أسهل طريقة للحصول على أطول متتالية جزئية متزايدة هي ترتيب عناصر المدخلات ترتيبًا تزايديًا ثمّ تطبيق خوارزمية LCS على التسلسلات الأصلية والمرتّبة، غير أنّه إذا ألقيت نظرة سريعة على المصفوفة الناتجة، فستلاحظ أنّ العديد من قيمها متساوية إذ تحتوي المصفوفة الكثير من العناصر المكرّرة، هذا يعني أنّه يمكن حل مشكلة LIS بالبرمجة الديناميكية باستخدام مصفوفة من بعد واحد.
</p>

<p>
	المثال التوضيحي:
</p>

<ol>
<li>
		صِف مصفوفة القيم التي نريد حسابها: لكل <code>‎1 &lt;= i &lt;= n‎</code>، ليكن A (i)‎‎ طول أكبر متتالية جزئية متزايدة في المتتالية المؤلفة من أوّل i عنصر في المدخلات. لاحظ أنّ طول أكبر متتالية جزئية متزايدة في المدخلات (بالكامل) يحقق العبارة <code>‎max{A(i)|1 ≤ i ≤ n}‎</code>، أي أنّه:

		<ul>
<li>
				إن كان طول أكبر متتالية جزئية متزايدة في المدخلات هو <code>L</code>، فإنّه يوجد عدد <code>k</code> يحقّق <code>A(k) = L</code>، و لكل <code>1 =&lt; n &gt;= j</code> لدينا: <code>L &gt; A(j)‎‎</code>.
			</li>
		</ul>
</li>
	<li>
		نحسب قيم A (i)‎‎ عوديًا على النحو التالي:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_51" style="">
<span class="typ">For</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">&lt;=</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> n</span><span class="pun">,</span><span class="pln"> A</span><span class="pun">(</span><span class="pln">i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> max </span><span class="pun">{</span><span class="pln"> A</span><span class="pun">(</span><span class="pln">j</span><span class="pun">)|</span><span class="lit">1</span><span class="pln"> </span><span class="pun">≤</span><span class="pln"> j </span><span class="pun">&lt;</span><span class="pln"> i and input</span><span class="pun">(</span><span class="pln">j</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> input</span><span class="pun">(</span><span class="pln">i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">}</span></pre>

<ol start="3">
<li>
		احسب قيم A باستخدام الصيغة السابقة.
	</li>
	<li>
		ابحث عن الحل الأمثل.
	</li>
</ol>
<p>
	يستخدم البرنامج التالي <strong>A</strong> لحساب الحل الأمثل، ويحسب الجزء الأول منه قيمة m حيث تساوي A (m)‎‎ طول متتالية جزئية متزايدة مثلى في المدخلات، فيما يحاول الجزء الثاني العثور على متتالية جزئية متزايدة مثلى.
</p>

<p>
	سنطبع المتتالية بترتيب عكسي لجعلها أوضَح. يستغرق هذا البرنامج O (n)‎‎، وتستغرق الخوارزمية إجمالًا O (n ^ 2)‎‎.
</p>

<p>
	الجزء الأول:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_53" style="">
<span class="pln">m </span><span class="pun">←</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> i </span><span class="pun">:</span><span class="pln"> </span><span class="lit">2.</span><span class="pun">.</span><span class="pln">n
   </span><span class="kwd">if</span><span class="pln"> A</span><span class="pun">(</span><span class="pln">i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> A</span><span class="pun">(</span><span class="pln">m</span><span class="pun">)</span><span class="pln"> then
       m </span><span class="pun">←</span><span class="pln"> i
   end </span><span class="kwd">if</span><span class="pln">
end </span><span class="kwd">for</span></pre>

<p>
	الجزء الثاني:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_55" style="">
<span class="pln">put a
</span><span class="kwd">while</span><span class="pln"> A</span><span class="pun">(</span><span class="pln">m</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="kwd">do</span><span class="pln">
   i </span><span class="pun">←</span><span class="pln"> m</span><span class="pun">−</span><span class="lit">1</span><span class="pln">
   </span><span class="kwd">while</span><span class="pln"> not</span><span class="pun">(</span><span class="pln">ai </span><span class="pun">&lt;</span><span class="pln"> am and A</span><span class="pun">(</span><span class="pln">i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">(</span><span class="pln">m</span><span class="pun">)−</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">do</span><span class="pln">
       i </span><span class="pun">←</span><span class="pln"> i</span><span class="pun">−</span><span class="lit">1</span><span class="pln">
   end </span><span class="kwd">while</span><span class="pln">
   m </span><span class="pun">←</span><span class="pln"> i
   put a
end </span><span class="kwd">while</span></pre>

<p>
	<strong>الحل العودي</strong>، المنظور الأول:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_57" style="">
<span class="pln">LIS</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">n</span><span class="pun">]):</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> then </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
   m </span><span class="pun">=</span><span class="pln"> LIS</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.(</span><span class="pln">n </span><span class="pun">−</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)])</span><span class="pln">
   B is subsequence of A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.(</span><span class="pln">n </span><span class="pun">−</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)]</span><span class="pln"> with only elements less than a</span><span class="pun">[</span><span class="pln">n</span><span class="pun">]</span><span class="pln">
   </span><span class="pun">(*</span><span class="pln"> let h be size of B</span><span class="pun">,</span><span class="pln"> h </span><span class="pun">≤</span><span class="pln"> n</span><span class="pun">-</span><span class="lit">1</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">
   m </span><span class="pun">=</span><span class="pln"> max</span><span class="pun">(</span><span class="pln">m</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> LIS</span><span class="pun">(</span><span class="pln">B</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">h</span><span class="pun">]))</span><span class="pln">
   </span><span class="typ">Output</span><span class="pln"> m</span></pre>

<p>
	التعقيد الزمني للمنظور الأول: <code>‎O(n*2^n)‎</code>.
</p>

<p>
	المنظور الثاني:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_59" style="">
<span class="pln">LIS</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">n</span><span class="pun">],</span><span class="pln"> x</span><span class="pun">):</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> then </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
   m </span><span class="pun">=</span><span class="pln"> LIS</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.(</span><span class="pln">n </span><span class="pun">−</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)],</span><span class="pln"> x</span><span class="pun">)</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="pln">n</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> x</span><span class="pun">)</span><span class="pln"> then
       m </span><span class="pun">=</span><span class="pln"> max</span><span class="pun">(</span><span class="pln">m</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> LIS</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.(</span><span class="pln">n </span><span class="pun">−</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)],</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">n</span><span class="pun">]))</span><span class="pln">
   </span><span class="typ">Output</span><span class="pln"> m
MAIN</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">n</span><span class="pun">]):</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> LIS</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">n</span><span class="pun">],</span><span class="pln"> </span><span class="pun">∞)</span></pre>

<p>
	التعقيد الزمني للمنظور الثاني: <code>‎O(n^2)‎</code>.
</p>

<p>
	المنظور الثالث:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_61" style="">
<span class="pln">LIS</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">n</span><span class="pun">]):</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
   m </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
   </span><span class="kwd">for</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> to n </span><span class="pun">−</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="kwd">do</span><span class="pln">
       </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">n</span><span class="pun">])</span><span class="pln"> then
           m </span><span class="pun">=</span><span class="pln"> max</span><span class="pun">(</span><span class="pln">m</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> LIS</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">i</span><span class="pun">]))</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> m
MAIN</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">n</span><span class="pun">]):</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> LIS</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">i</span><span class="pun">])</span></pre>

<p>
	التعقيد الزمني للمنظور الثالث: <code>‎O(n^2)‎</code>.
</p>

<p>
	<strong>الخوارزمية التكرارية Iterative Algorithm</strong>: تحسب هذه الخوارزمية القيم بطريقة متكررة من أسفل إلى أعلى.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_63" style="">
<span class="pln">LIS</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">n</span><span class="pun">]):</span><span class="pln">
   </span><span class="typ">Array</span><span class="pln"> L</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">n</span><span class="pun">]</span><span class="pln">
   </span><span class="pun">(*</span><span class="pln"> L</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> value of LIS ending</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">i</span><span class="pun">])</span><span class="pln"> </span><span class="pun">*)</span><span class="pln">
   </span><span class="kwd">for</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> to n </span><span class="kwd">do</span><span class="pln">
       L</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
       </span><span class="kwd">for</span><span class="pln"> j </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> to i </span><span class="pun">−</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="kwd">do</span><span class="pln">
           </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="pln">j</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> A</span><span class="pun">[</span><span class="pln">i</span><span class="pun">])</span><span class="pln"> </span><span class="kwd">do</span><span class="pln">
               L</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> max</span><span class="pun">(</span><span class="pln">L</span><span class="pun">[</span><span class="pln">i</span><span class="pun">],</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> L</span><span class="pun">[</span><span class="pln">j</span><span class="pun">])</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> L
MAIN</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">n</span><span class="pun">]):</span><span class="pln">
   L </span><span class="pun">=</span><span class="pln"> LIS</span><span class="pun">(</span><span class="pln">A</span><span class="pun">[</span><span class="lit">1.</span><span class="pun">.</span><span class="pln">n</span><span class="pun">])</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> the maximum value in L</span></pre>

<p>
	<strong>التعقيد الزمني للمنظور التكراري</strong>: <code>‎O(n^2)‎</code>.
</p>

<p>
	<strong>المساحة الإضافية</strong>: <code>‎O(n)‎</code>.
</p>

<p>
	على سبيل المثال، لو كانت المدخلات تساوي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_65" style="">
<span class="pun">{</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">14</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">15</span><span class="pun">}</span></pre>

<p>
	فستكون أطول متتالية جزئية متزايدة في المدخلات هي: <code>{0, 2, 6, 9, 11, 15}</code>.
</p>

<h2>
	الحيد الزمني الديناميكي Dynamic Time Warping
</h2>

<p>
	<a href="https://en.wikipedia.org/wiki/Dynamic_time_warping" rel="external nofollow">خوارزمية الحيد الزمني الديناميكي</a> Dynamic Time Warping -أو DTW اختصارًا- هي خوارزمية لقياس مدى التشابه بين متتاليتين زمنيتين قد تختلفان في السرعة، فمثلًا يمكن رصد أوجه التشابه في مشية شخصين باستخدام خوارزمية DTW حتى لو كان أحدهما يمشي أسرع من الآخر، أو كان هناك تسارع وتباطؤ أثناء المراقبة.
</p>

<p>
	وتُستخدم هذه الخوارزمية لمطابقة مقطع صوتي قياسي مع مقطع صوتي لشخص آخر، حتى لو كان ذلك الشخص يتحدث أسرع من صوت العينة القياسية المسجّلة أو أبطأ منها. تُطبَّق خوارزمية DTW على التسلسلات الزمنية لبيانات الفيديو والصوت والرسومات، أو أيّ نوع من البيانات ما دام يمكن تحويلها إلى متتالية خطية.
</p>

<p>
	تحاول هذه الخوارزمية إيجاد التطابق الأمثل بين متتاليتين زمنيتين مع الأخذ بالحسبان قيودًا معيّنة، فمثلًا لنفترض أنّ لدينا متتاليتين صوتيتين Sample و Test، ونريد أن نتحقق من تطابق هاتين المتتاليتين الزمنيتين. يشير مصطلح متتالية صوتية هنا إلى الإشارة الرقمية للصوت، وقد تُمثَّل بسعة الصوت amplitude أو تردّده frequency. لنفترض أنّ:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_67" style="">
<span class="typ">Sample</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">}</span><span class="pln">
</span><span class="typ">Test</span><span class="pln">   </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">}</span></pre>

<p>
	نريد العثور على أمثل تطابق بين هاتين المتتاليتين، نبدأ بتعريف دالة المسافة <code>d (x، y)‎‎</code>، حيث تمثل <code>x</code> و <code>y</code> النقطتين اللتان نودّ حِساب المسافة الفاصلة بينهما. تكون صيغة حساب المسافة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_69" style="">
<span class="pln">d</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">|</span><span class="pln">x </span><span class="pun">-</span><span class="pln"> y</span><span class="pun">|</span><span class="pln"> </span><span class="com">// القيمة المطلقة للفرق</span></pre>

<p>
	ننشئ الآن مصفوفة ثنائية الأبعاد Table باستخدام قيم المتتاليتين Sample و Test، وسنحسب المسافات بين كل نقطة في Sample وكل نقطة في Test ونبحث عن أفضل تطابق بينهما.
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_2265_71" style="">
<span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">  </span><span class="lit">0</span><span class="pln">   </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">   </span><span class="pun">|</span><span class="pln">  </span><span class="lit">1</span><span class="pln">   </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">   </span><span class="pun">|</span><span class="pln">  </span><span class="lit">2</span><span class="pln">   </span><span class="pun">|</span><span class="pln">  </span><span class="lit">3</span><span class="pln">   </span><span class="pun">|</span><span class="pln">  </span><span class="lit">5</span><span class="pln">   </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">6</span><span class="pln">  </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span></pre>

<p>
	تحتوي كل خانة Table[‎i][j]‎‎ من خانات الجدول المسافة المثلى بين المتتاليتين الزمنيتين الجزئيتين t1 و t2، حيث تساوي t1 المتتالية الجزئية من Test التي تبدأ من بدايتها وحتى العنصر رقم j، وتساوي t2 المتتالية الجزئية من Sample التي تبدأ من بدايتها وحتى العنصر رقم i.
</p>

<p>
	إذا لم نأخذ أيّ قيمة من Sample في الصف الأول فستكون المسافة بينها وبين Test لا نهائية، لذا نضع ما لا نهاية في الصف الأول، وكذلك نفعل مع العمود الأول لأنّنا إن لم نأخذ أيّ قيمة من Test فستكون المسافة بينها وبين Sample لا نهائية كذلك، والمسافة بين 0 و 0 تساوي 0.
</p>

<p>
	نحصل على ما يلي:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_2265_73" style="">
<span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="kwd">in</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">6</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span></pre>

<p>
	سنحسب في كل خطوة المسافة بين النقاط الحالية ثمّ نضيفها إلى الحد الأدنى للمسافة التي وجدناها حتى الآن، وسينتج عن هذا المسافة المثلى بين المتتاليتين الجزئيتين <strong>t1</strong> و <strong>t2</strong> (انظر تعريفيهمَا في الأعلى). هذه هي الصيغة التي سنعمل بها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_75" style="">
<span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">j</span><span class="pun">]</span><span class="pln"> </span><span class="pun">:=</span><span class="pln"> d</span><span class="pun">(</span><span class="pln">i</span><span class="pun">,</span><span class="pln"> j</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> min</span><span class="pun">(</span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">j</span><span class="pun">],</span><span class="pln"> </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">j</span><span class="pun">-</span><span class="lit">1</span><span class="pun">],</span><span class="pln"> </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">j</span><span class="pun">-</span><span class="lit">1</span><span class="pun">])</span></pre>

<p>
	يكون لدينا في البداية d(1, 1) = 0، كما تحتوي الخانة Table[0][0] ‎‎ القيمةَ الصغرى، لذا نضع Table[1][1] = 0 + 0 = 0‎‎. لدينا في الحالة الثانية d(1, 2) = 0، وتحتوي الخانة Table[1][1] ‎‎ القيمةَ الصغرى، لذا نضع Table[1][2] = 0 + 0 = 0. نستمر على هذا المنوال إلى أن نملأ الجدول، الذي سيبدو بعد الانتهاء كما يلي:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_2265_77" style="">
<span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">      </span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">4</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">8</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">4</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">   </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">3</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">   </span><span class="lit">7</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">7</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">4</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">4</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">2</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">   </span><span class="lit">11</span><span class="pln"> </span><span class="pun">|</span><span class="pln">  </span><span class="lit">11</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">7</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">7</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">4</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">5</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">   </span><span class="lit">15</span><span class="pln"> </span><span class="pun">|</span><span class="pln">  </span><span class="lit">15</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">10</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">10</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">6</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">0</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span><span class="pln">
</span><span class="pun">|</span><span class="pln">   </span><span class="lit">6</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  inf </span><span class="pun">|</span><span class="pln">   </span><span class="lit">20</span><span class="pln"> </span><span class="pun">|</span><span class="pln">  </span><span class="lit">20</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">14</span><span class="pln">  </span><span class="pun">|</span><span class="pln">  </span><span class="lit">14</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">9</span><span class="pln">  </span><span class="pun">|</span><span class="pln">   </span><span class="lit">1</span><span class="pln">  </span><span class="pun">|</span><span class="pln">
</span><span class="pun">+------+------+------+------+------+------+------+------+</span></pre>

<p>
	تمثل قيمة الخانة Table[7][6]‎‎ المسافة القصوى بين المتتاليتين Sample و Test، وتشير القيمة 1 هنا إلى أنّ المسافة القصوى بين Sample و Sample تساوي 1.
</p>

<p>
	الآن إذا ارتددنا من النقطة الأخيرة وحتى نقطة البداية (0 , 0) فسنحصل على خط طويل يتحرك أفقيًا وعموديًا وقطريًا. هذا مثال توضيحي لعملية الارتداد:
</p>

<pre class="ipsCode">
if Table[i-1][j-1] &lt;= Table[i-1][j] and Table[i-1][j-1] &lt;= Table[i][j-1]

   i := i - 1
   j := j - 1
else if Table[i-1][j] &lt;= Table[i-1][j-1] and Table[i-1][j] &lt;= Table[i][j-1]
   i := i - 1
else
   j := j - 1
end if
</pre>

<p>
	نواصل على هذا المنوال حتى نصل إلى النقطة (0 , 0)، واعلم أن كل حركة لها معنى خاص:
</p>

<ul>
<li>
		تمثل الحركة الأفقية حذفًا، وهذا يعني أنّ المتتالية Test قد تسرّعت أثناء هذه المدة.
	</li>
	<li>
		تمثل الحركة الرأسية إدراجًا، وهذا يعني تباطؤ المتتالية Test أثناء هذه المدة.
	</li>
	<li>
		تمثل الخطوة القطرية تطابقًا، أي أنّ Test تتطابق مع Sample أثناء هذه المدة.
	</li>
</ul>
<p style="text-align: center;">
	<img alt="1.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="96771" data-unique="rqsftmfiw" src="https://academy.hsoub.com/uploads/monthly_2022_04/1.jpg.59a0084e9e31215d367fd7b4583ae59a.jpg"></p>

<p>
	انظر المثال التوضيحي التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2265_79" style="">
<span class="typ">Procedure</span><span class="pln"> DTW</span><span class="pun">(</span><span class="typ">Sample</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Test</span><span class="pun">):</span><span class="pln">
n </span><span class="pun">:=</span><span class="pln"> </span><span class="typ">Sample</span><span class="pun">.</span><span class="pln">length
m </span><span class="pun">:=</span><span class="pln"> </span><span class="typ">Test</span><span class="pun">.</span><span class="pln">length
</span><span class="typ">Create</span><span class="pln"> </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">n </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">][</span><span class="pln">m </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> i from </span><span class="lit">1</span><span class="pln"> to n
   </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">:=</span><span class="pln"> infinity
end </span><span class="kwd">for</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> i from </span><span class="lit">1</span><span class="pln"> to m
   </span><span class="typ">Table</span><span class="pun">[</span><span class="lit">0</span><span class="pun">][</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">:=</span><span class="pln"> infinity
end </span><span class="kwd">for</span><span class="pln">
</span><span class="typ">Table</span><span class="pun">[</span><span class="lit">0</span><span class="pun">][</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">:=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> i from </span><span class="lit">1</span><span class="pln"> to n
   </span><span class="kwd">for</span><span class="pln"> j from </span><span class="lit">1</span><span class="pln"> to m
       </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">j</span><span class="pun">]</span><span class="pln"> </span><span class="pun">:=</span><span class="pln"> d</span><span class="pun">(</span><span class="typ">Sample</span><span class="pun">[</span><span class="pln">i</span><span class="pun">],</span><span class="pln"> </span><span class="typ">Test</span><span class="pun">[</span><span class="pln">j</span><span class="pun">])</span><span class="pln">
                      </span><span class="pun">+</span><span class="pln"> minimum</span><span class="pun">(</span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">j</span><span class="pun">-</span><span class="lit">1</span><span class="pun">],</span><span class="pln">      </span><span class="com">// مطابقة</span><span class="pln">
                                </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">j</span><span class="pun">-</span><span class="lit">1</span><span class="pun">],</span><span class="pln">        </span><span class="com">// إدراج</span><span class="pln">
                                </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">j</span><span class="pun">])</span><span class="pln">        </span><span class="com">// حذف</span><span class="pln">
   end </span><span class="kwd">for</span><span class="pln">
end </span><span class="kwd">for</span><span class="pln">
</span><span class="typ">Return</span><span class="pln"> </span><span class="typ">Table</span><span class="pun">[</span><span class="pln">n </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">][</span><span class="pln">m </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]</span></pre>

<p>
	يمكن أيضًا أن نضيف قيودًا محلية، كأن نشترط أنّه إذا تطابقت <code>‎Sample[‎i]‎</code> مع <code>‎Test[j]‎</code>، فيجب أن لا تكون قيمة <code>‎|i - j|‎</code> أكبر من w (معامل نافذة - window parameter).
</p>

<p>
	<strong>التعقيد</strong>: التعقيد الزمني لخوارزمية DTW هو O (m * n)‎‎، حيث يمثل كلّ من <strong>m</strong> و <strong>n</strong> طولي المتتاليتين. ويجدر التنويه إلى أنّ هناك تقنيات أخرى أسرع لحساب DTW مثل: PrunedDTW و SparseDTW و FastDTW.
</p>

<p>
	ترجمة -بتصرّف- للفصول 47 و 48 و 54 من كتاب <a href="https://goalkicker.com/AlgorithmsBook/" rel="external nofollow">Algorithms Notes for Professionals</a>.<em><em><em><em><em><em><em><em><em><em><em><em><em><em><em><em><em><em><em><em><em><em> </em></em></em></em></em></em></em></em></em></em></em></em></em></em></em></em></em></em></em></em></em></em><em><em><em><em><em><em><em><em><em><em><em> </em></em></em></em></em></em></em></em></em></em></em>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/advanced/%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%84%D9%89-%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D9%84%D8%AD%D9%84-%D9%85%D8%B4%D9%83%D9%84%D8%A7%D8%AA-%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-r1533/" rel="">أمثلة على خوارزميات لحل مشكلات بسيطة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/java/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1063/?msclkid=095bd040c12e11ec9140c326b8f3e18f" rel="">تطوير الخوارزميات في جافا</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%86-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1410/?msclkid=238d8711c12e11ec9be81c2a9f74b4b2" rel="">أمثلة عن أنواع الخوارزميات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8-%D9%88%D8%A3%D8%B4%D9%87%D8%B1%D9%87%D8%A7-r1413/?msclkid=238e0a7dc12e11ecbaf8b3f33e75887c" rel="">خوارزميات الترتيب وأشهرها</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1535</guid><pubDate>Sat, 23 Apr 2022 05:46:05 +0000</pubDate></item><item><title>&#x623;&#x645;&#x62B;&#x644;&#x629; &#x639;&#x644;&#x649; &#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x627;&#x62A; &#x644;&#x62D;&#x644; &#x645;&#x634;&#x643;&#x644;&#x627;&#x62A; &#x628;&#x633;&#x64A;&#x637;&#x629;</title><link>https://academy.hsoub.com/programming/advanced/%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%84%D9%89-%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D9%84%D8%AD%D9%84-%D9%85%D8%B4%D9%83%D9%84%D8%A7%D8%AA-%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-r1533/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_04/6260e02c89c30_-01.jpg.f5951f87d3f5a99d8cfa6af760ffaba0.jpg" /></p>
<p>
	نستعرض في هذه المقالة ثلاث خوارزميات بسيطة، وهي خوارزمية الحقيبة، وخوارزمية أخرى للتحقق من الألفاظ المقلوبة وخوارزمية لطباعة مثلث باسكال.
</p>

<h2>
	مشكلة الحقيبة Knapsack Problem
</h2>

<p>
	في <a href="https://ar.wikipedia.org/wiki/%D9%85%D8%B3%D8%A3%D9%84%D8%A9_%D8%AD%D9%82%D9%8A%D8%A8%D8%A9_%D8%A7%D9%84%D8%B8%D9%87%D8%B1" rel="external nofollow">مشكلة الحقيبة</a>، نفترض أنّ لدينا مجموعة من العناصر، لكل منها قيمة ووزن، وعليك اختيار مجموعة من تلك العناصر بحيث يكون وزنها الإجمالي أقلّ من حدّ معيّن أو يساويه، وتكون قيمتها الإجمالية أكبر ما يمكن.
</p>

<p>
	هذا مثال توضيحي لحل مشكلة الحقيبة:
</p>

<ul>
	<li>
		<code>v</code>: مصفوفة تمثّل قيم العناصر.
	</li>
	<li>
		<code>w</code>: مصفوفة تمثّل أوزان العناصر.
	</li>
	<li>
		<code>n</code>: مصفوفة تحتوي العناصر غير المكررة.
	</li>
	<li>
		<code>W</code>: تمثّل سعة Capacity الحقيبة.
	</li>
</ul>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4965_7" style=""><span class="kwd">for</span><span class="pln"> j from </span><span class="lit">0</span><span class="pln"> to W </span><span class="kwd">do</span><span class="pun">:</span><span class="pln">
   m</span><span class="pun">[</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> j</span><span class="pun">]</span><span class="pln"> </span><span class="pun">:=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> i from </span><span class="lit">1</span><span class="pln"> to n </span><span class="kwd">do</span><span class="pun">:</span><span class="pln">
   </span><span class="kwd">for</span><span class="pln"> j from </span><span class="lit">0</span><span class="pln"> to W </span><span class="kwd">do</span><span class="pun">:</span><span class="pln">
       </span><span class="kwd">if</span><span class="pln"> w</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> j then</span><span class="pun">:</span><span class="pln">
           m</span><span class="pun">[</span><span class="pln">i</span><span class="pun">,</span><span class="pln"> j</span><span class="pun">]</span><span class="pln"> </span><span class="pun">:=</span><span class="pln"> m</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> j</span><span class="pun">]</span><span class="pln">
       </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
           m</span><span class="pun">[</span><span class="pln">i</span><span class="pun">,</span><span class="pln"> j</span><span class="pun">]</span><span class="pln"> </span><span class="pun">:=</span><span class="pln"> max</span><span class="pun">(</span><span class="pln">m</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> j</span><span class="pun">],</span><span class="pln"> m</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> j</span><span class="pun">-</span><span class="pln">w</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> v</span><span class="pun">[</span><span class="pln">i</span><span class="pun">])</span></pre>

<p>
	هذا تطبيق implementation للشيفرة السالفة بلغة بايثون:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4965_9" style=""><span class="kwd">def</span><span class="pln"> knapSack</span><span class="pun">(</span><span class="pln">W</span><span class="pun">,</span><span class="pln"> wt</span><span class="pun">,</span><span class="pln"> val</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">):</span><span class="pln">
   K </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[[</span><span class="lit">0</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> x </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">W</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)]</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> x </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)]</span><span class="pln">
   </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">n</span><span class="pun">+</span><span class="lit">1</span><span class="pun">):</span><span class="pln">
       </span><span class="kwd">for</span><span class="pln"> w </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">W</span><span class="pun">+</span><span class="lit">1</span><span class="pun">):</span><span class="pln">
           </span><span class="kwd">if</span><span class="pln"> i</span><span class="pun">==</span><span class="lit">0</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> w</span><span class="pun">==</span><span class="lit">0</span><span class="pun">:</span><span class="pln">
               K</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">w</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
           </span><span class="kwd">elif</span><span class="pln"> wt</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&lt;=</span><span class="pln"> w</span><span class="pun">:</span><span class="pln">
               K</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">w</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> max</span><span class="pun">(</span><span class="pln">val</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> K</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">w</span><span class="pun">-</span><span class="pln">wt</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">]],</span><span class="pln">  K</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">w</span><span class="pun">])</span><span class="pln">
           </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
               K</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">w</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> K</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">][</span><span class="pln">w</span><span class="pun">]</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> K</span><span class="pun">[</span><span class="pln">n</span><span class="pun">][</span><span class="pln">W</span><span class="pun">]</span><span class="pln">
val </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">60</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">120</span><span class="pun">]</span><span class="pln">
wt </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="lit">30</span><span class="pun">]</span><span class="pln">
W </span><span class="pun">=</span><span class="pln"> </span><span class="lit">50</span><span class="pln">
n </span><span class="pun">=</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">val</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">knapSack</span><span class="pun">(</span><span class="pln">W</span><span class="pun">,</span><span class="pln"> wt</span><span class="pun">,</span><span class="pln"> val</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">))</span></pre>

<p>
	احفظ هذه الشيفرة في ملفّ يُسمّى knapSack.py، ثمّ نفّذه على النحو التالي:
</p>

<pre class="ipsCode">$ python knapSack.py
220
</pre>

<p>
	<strong>التعقيد الزمني</strong>: يساوي تعقيد الشيفرة السابقة <code>‎O(nW)‎</code>، حيث يمثّل <code>n</code> عدد العناصر، وتمثّل <code>W</code> سعة الحقيبة.
</p>

<p>
	هذا تطبيق آخر بلغة C#‎‎:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4965_11" style=""><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">KnapsackProblem</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="typ">Knapsack</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> w</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> weight</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> value</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> n</span><span class="pun">)</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
       </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
       </span><span class="typ">int</span><span class="pun">[,]</span><span class="pln"> k </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[</span><span class="pln">n </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> w </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">];</span><span class="pln">
       </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln">
       </span><span class="pun">{</span><span class="pln">
           </span><span class="typ">int</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
           </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">b </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> b </span><span class="pun">&lt;=</span><span class="pln"> w</span><span class="pun">;</span><span class="pln"> b</span><span class="pun">++)</span><span class="pln">
           </span><span class="pun">{</span><span class="pln">
               </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i</span><span class="pun">==</span><span class="lit">0</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> b</span><span class="pun">==</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
               </span><span class="pun">{</span><span class="pln">
                   k</span><span class="pun">[</span><span class="pln">i</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
               </span><span class="pun">}</span><span class="pln">
               </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">weight</span><span class="pun">[</span><span class="pln">i </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&lt;=</span><span class="pln"> b</span><span class="pun">)</span><span class="pln">
               </span><span class="pun">{</span><span class="pln">
                   k</span><span class="pun">[</span><span class="pln">i</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="typ">Max</span><span class="pun">(</span><span class="pln">value</span><span class="pun">[</span><span class="pln">i </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> k</span><span class="pun">[</span><span class="pln">i </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> b </span><span class="pun">-</span><span class="pln"> weight</span><span class="pun">[</span><span class="pln">i </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]],</span><span class="pln"> k</span><span class="pun">[</span><span class="pln">i </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">]);</span><span class="pln">
               </span><span class="pun">}</span><span class="pln">
               </span><span class="kwd">else</span><span class="pln">
               </span><span class="pun">{</span><span class="pln">
                   k</span><span class="pun">[</span><span class="pln">i</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> k</span><span class="pun">[</span><span class="pln">i </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">];</span><span class="pln">
               </span><span class="pun">}</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> k</span><span class="pun">[</span><span class="pln">n</span><span class="pun">,</span><span class="pln"> w</span><span class="pun">];</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> </span><span class="typ">Main</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> nItems</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> weights</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pun">[]</span><span class="pln"> values</span><span class="pun">)</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
       </span><span class="typ">int</span><span class="pln"> n </span><span class="pun">=</span><span class="pln"> values</span><span class="pun">.</span><span class="typ">Length</span><span class="pun">;</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Knapsack</span><span class="pun">(</span><span class="pln">nItems</span><span class="pun">,</span><span class="pln"> weights</span><span class="pun">,</span><span class="pln"> values</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	التحقق من العبارات المقلوبة
</h2>

<p>
	لتكن س سلسلة نصية، ولتكن ع سلسلة نصية أخرى نحصل عليها بتغيير مواضع بعض حروف س، فتُسمّى السلسلة النصية ع <a href="https://ar.wikipedia.org/wiki/%D9%84%D9%81%D8%B8_%D9%85%D9%82%D9%84%D9%88%D8%A8#%D8%B9%D8%B1%D8%A8%D9%8A%D8%A9" rel="external nofollow">لفظًا مقلوبًا</a> anagram للسلسلة النصية س. مثلًا، تعدّ الكلمتان <strong>لكم</strong> و <strong>كلم</strong> لفظان مقلوبان للكلمة <strong>ملك</strong>. بتعبير آخر، تكون سلسلة نصية لفظًا مقلوبًا من سلسلة نصية أخرى إن كانت السلسِلتان مؤلّفتان من مجموعة الحروف نفسها، وبالتردّدات نفسها.
</p>

<p>
	لتكن <code>str1</code> و <code>str2</code> سلسلتين نصيتين، نريد أن نتحقّق ممّا إذا كانت <code>str2</code> لفضًا مقلوبًا للسلسلة <code>str1</code>. هذه خطوات الخوارزمية التي سنعتمدها:
</p>

<ol>
	<li>
		أنشئ قاموسًا <code><a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AE%D8%B1%D8%A7%D8%A6%D8%B7-maps-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1430/" rel="">hashMap</a></code> مفاتيحه تساوي حروف <code>str1</code> (غير مكرّرة)، وقيمة كل مفتاح (أو حرف) في القاموس تساوي عدد مرّات تكرار ذلك الحرف.
	</li>
	<li>
		امرر على كل حرف من حروف <code>str2</code> وتحقّق من أنّه موجود في القاموس <code>hashMap</code>.
		<ul>
			<li>
				إن وجدت الحرف في القاموس فانقص قيمته في القاموس بواحد.
			</li>
			<li>
				إن لم تجد الحرف في القاموس فذلك يدل على أنّه غير موجود في السلسلة النصية <code>str1</code>، وإذن لا يمكن أن تكون <code>str2</code> لفضًا مقلوبًا للسلسلة <code>str1</code>. تنتهي الخوارزمية هنا وتعيد <code>false</code>.
			</li>
		</ul>
	</li>
	<li>
		إن كانت جميع قيم مفاتيح القاموس <code>hashMap</code> تساوي الصفر بعد المرور على كل حروف <code>str2</code>، فذلك يدل على أنّ <code>str2</code> لفظًا مقلوبًا للسلسلة <code>str1</code>، نعيد إذن <code>true</code>. خلاف ذلك نعيد <code>false</code>.
	</li>
</ol>

<p>
	انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4965_13" style=""><span class="pln">let str1 </span><span class="pun">=</span><span class="pln"> </span><span class="str">'stackoverflow'</span><span class="pun">;</span><span class="pln">
let str2 </span><span class="pun">=</span><span class="pln"> </span><span class="str">'flowerovstack'</span><span class="pun">;</span></pre>

<p>
	هاتان السلسِلتان النصيتَان مُتقالبتان. وهذا هو قاموس الترددات خاصّتها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4965_15" style=""><span class="pln">hashMap </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    s </span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    t </span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    a </span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    c </span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    k </span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    o </span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln">
    v </span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    e </span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    r </span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    f </span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    l </span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    w </span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ أنّ القيمة المرتبطة بالمفتاح <code>o</code> تساوي 2، ذلك أنّ <code>o</code> تكرّرت مرّتين في السلسلة النصية.
</p>

<p>
	سنمرّ على كل حرف من حروف str2 لنتحقق من أنّه موجود في <code>hashMap</code>، فإن كان كذلك نقصنا قيمة الحرف في <code>hashMap</code> واحدًا، وإلا أعدنا <code>false</code> دلالة على أنّ str2 ليست لفظًا مقلوبًا عن str1.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4965_17" style=""><span class="pln">hashMap </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    s </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    t </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    a </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    c </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    k </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    o </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    v </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    e </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    r </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    f </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    l </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    w </span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نمرّ الآن على كل قيم <code>hashMap</code> ونتحقق من أنّها تساوي جميعًا 0. وبما أن جميع قيم <code>hashMap</code> تساوي 0 في المثال الذي ضربناه آنفًا، فإنّ str2 لفظٌ مقلوب عن str1.
</p>

<p>
	إليك مثال توضيحي لخوارزمية التحقق من الألفاظ المقلوبة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4965_19" style=""><span class="pun">(</span><span class="kwd">function</span><span class="pun">(){</span><span class="pln">
   </span><span class="kwd">var</span><span class="pln"> hashMap </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">

   </span><span class="kwd">function</span><span class="pln"> isAnagram </span><span class="pun">(</span><span class="pln">str1</span><span class="pun">,</span><span class="pln"> str2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

       </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">str1</span><span class="pun">.</span><span class="pln">length </span><span class="pun">!==</span><span class="pln"> str2</span><span class="pun">.</span><span class="pln">length</span><span class="pun">){</span><span class="pln">
           </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">

       createStr1HashMap</span><span class="pun">(</span><span class="pln">str1</span><span class="pun">);</span><span class="pln">

       </span><span class="kwd">var</span><span class="pln"> valueExist </span><span class="pun">=</span><span class="pln"> createStr2HashMap</span><span class="pun">(</span><span class="pln">str2</span><span class="pun">);</span><span class="pln">

       </span><span class="kwd">return</span><span class="pln"> isStringsAnagram</span><span class="pun">(</span><span class="pln">valueExist</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="kwd">function</span><span class="pln"> createStr1HashMap </span><span class="pun">(</span><span class="pln">str1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="pun">[].</span><span class="pln">map</span><span class="pun">.</span><span class="pln">call</span><span class="pun">(</span><span class="pln">str1</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">value</span><span class="pun">,</span><span class="pln"> index</span><span class="pun">,</span><span class="pln"> array</span><span class="pun">){</span><span class="pln">
           hashMap</span><span class="pun">[</span><span class="pln">value</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> value in hashMap </span><span class="pun">?</span><span class="pln">  </span><span class="pun">(</span><span class="pln">hashMap</span><span class="pun">[</span><span class="pln">value</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
           </span><span class="kwd">return</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">});</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="kwd">function</span><span class="pln"> createStr2HashMap </span><span class="pun">(</span><span class="pln">str2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="kwd">var</span><span class="pln"> valueExist </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[].</span><span class="pln">every</span><span class="pun">.</span><span class="pln">call</span><span class="pun">(</span><span class="pln">str2</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">value</span><span class="pun">,</span><span class="pln"> index</span><span class="pun">,</span><span class="pln"> array</span><span class="pun">){</span><span class="pln">
           </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">value in hashMap</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
               hashMap</span><span class="pun">[</span><span class="pln">value</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> hashMap</span><span class="pun">[</span><span class="pln">value</span><span class="pun">]</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
           </span><span class="kwd">return</span><span class="pln"> value in hashMap</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">});</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> valueExist</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   </span><span class="kwd">function</span><span class="pln"> isStringsAnagram </span><span class="pun">(</span><span class="pln">valueExist</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="kwd">if</span><span class="pun">(!</span><span class="pln">valueExist</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           </span><span class="kwd">return</span><span class="pln"> valueExist</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           </span><span class="kwd">var</span><span class="pln"> isAnagram</span><span class="pun">;</span><span class="pln">
           </span><span class="kwd">for</span><span class="pun">(</span><span class="kwd">var</span><span class="pln"> i in hashMap</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
               </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">hashMap</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">!==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                   isAnagram </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
                   </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
               </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                   isAnagram </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
               </span><span class="pun">}</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">

           </span><span class="kwd">return</span><span class="pln"> isAnagram</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">

   isAnagram</span><span class="pun">(</span><span class="str">'stackoverflow'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'flowerovstack'</span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span><span class="pln">
   isAnagram</span><span class="pun">(</span><span class="str">'stackoverflow'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'flowervvstack'</span><span class="pun">);</span><span class="pln"> </span><span class="com">// false</span><span class="pln">
</span><span class="pun">})();</span></pre>

<p>
	<strong>التعقيد الزمني</strong>: 3n أو O (n)‎‎.
</p>

<h2>
	مثلث باسكال
</h2>

<p>
	<a href="https://ar.wikipedia.org/wiki/%D9%85%D8%AB%D9%84%D8%AB_%D8%A8%D8%A7%D8%B3%D9%83%D8%A7%D9%84" rel="external nofollow">مثلث باسكال</a> هو منظومة عددية تُرتّب على هيئة مثلث، ولها استخدامات كثيرة في الرياضيات.
</p>

<p>
	هذه شيفرة مكتوبة بلغة C لطباعة مثلث باسكال:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_4965_22" style=""><span class="typ">int</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> space</span><span class="pun">,</span><span class="pln"> rows</span><span class="pun">,</span><span class="pln"> k</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> count1 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
row</span><span class="pun">=</span><span class="lit">5</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i</span><span class="pun">=</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">&lt;=</span><span class="pln">rows</span><span class="pun">;</span><span class="pln"> </span><span class="pun">++</span><span class="pln">i</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">space</span><span class="pun">=</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> space </span><span class="pun">&lt;=</span><span class="pln"> rows</span><span class="pun">-</span><span class="pln">i</span><span class="pun">;</span><span class="pln"> </span><span class="pun">++</span><span class="pln">space</span><span class="pun">)</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
       printf</span><span class="pun">(</span><span class="str">"  "</span><span class="pun">);</span><span class="pln">
       </span><span class="pun">++</span><span class="pln">count</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">k </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">*</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
       </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">count </span><span class="pun">&lt;=</span><span class="pln"> rows</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
       </span><span class="pun">{</span><span class="pln">
           printf</span><span class="pun">(</span><span class="str">"%d "</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">+</span><span class="pln">k</span><span class="pun">);</span><span class="pln">
           </span><span class="pun">++</span><span class="pln">count</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="kwd">else</span><span class="pln">
       </span><span class="pun">{</span><span class="pln">
           </span><span class="pun">++</span><span class="pln">count1</span><span class="pun">;</span><span class="pln">
           printf</span><span class="pun">(</span><span class="str">"%d "</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i</span><span class="pun">+</span><span class="pln">k</span><span class="pun">-</span><span class="lit">2</span><span class="pun">*</span><span class="pln">count1</span><span class="pun">));</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="pun">++</span><span class="pln">k</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   count1 </span><span class="pun">=</span><span class="pln"> count </span><span class="pun">=</span><span class="pln"> k </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"\n"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الخرج الناتج:
</p>

<pre class="ipsCode">           1
        2 3 2
      3 4 5 4 3
  4 5 6 7 6 5 4
5 6 7 8 9 8 7 6 5
</pre>

<p>
	ترجمة -بتصرّف- للفصول 45 و 49 و 50 من كتاب <a href="https://goalkicker.com/AlgorithmsBook/" rel="external nofollow">Algorithms Notes for Professionals</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%AD%D9%84-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D8%AF%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-r1534/" rel="">خوارزميات حل المعادلات الرياضية</a>
	</li>
	<li>
		المرجع الشامل إلى: <a href="https://academy.hsoub.com/programming/advanced/%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA/" rel="">تعلم الخوارزميات للمبتدئين</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%84%D9%89-%D8%A3%D8%B4%D9%87%D8%B1-%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AE%D8%B7%D8%B7%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%A3%D8%B4%D8%AC%D8%A7%D8%B1-r1427/" rel="">أمثلة على أشهر خوارزميات المخططات والأشجار</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D9%88%D8%A2%D9%84%D9%8A%D8%A9-%D8%B9%D9%85%D9%84%D9%87%D8%A7-r1414/?msclkid=3c5c93a5c12d11ecbb8898c3f9dccf63" rel="">خوارزميات البحث وآلية عملها</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AD%D9%84-%D8%A7%D9%84%D9%85%D8%B4%D9%83%D9%84%D8%A7%D8%AA-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%A7%D8%AD%D8%AA%D8%B1%D8%A7%D9%81-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r760/?msclkid=3c5dbb76c12d11ecbca8d5dfb1db0d0c" rel="">حل المشكلات وأهميتها في احتراف البرمجة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1533</guid><pubDate>Mon, 18 Apr 2022 15:00:00 +0000</pubDate></item><item><title>&#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x627;&#x62A; &#x62D;&#x644; &#x627;&#x644;&#x645;&#x639;&#x627;&#x62F;&#x644;&#x627;&#x62A; &#x627;&#x644;&#x631;&#x64A;&#x627;&#x636;&#x64A;&#x629;</title><link>https://academy.hsoub.com/programming/advanced/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%AD%D9%84-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D8%AF%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A9-r1534/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_04/6260deefbdfe9_-01.jpg.f014c3c3644f9560f2abb71e9bb74d21.jpg" /></p>

<p>
	حل المعادلات هي من المسائل الشائعة في الرياضيات، وهناك بحث مستمر عن طرق جديدة وسريعة لحل المعادلات عبر الحاسوب، وسنستعرض في هذه المقالة بعض خوارزميات حل المعادلات الخطية وغير الخطية.
</p>

<h2>
	المعادلات الخطية Linear Equations
</h2>

<p>
	هناك نوعان من الطرق لحل <a href="https://ar.wikipedia.org/wiki/%D9%85%D8%B9%D8%A7%D8%AF%D9%84%D8%A9_%D8%AE%D8%B7%D9%8A%D8%A9" rel="external nofollow">المعادلات الخطية</a>:
</p>

<ol>
<li>
		<strong>الطرق المباشرة</strong>: يسعى هذا النوع من الطرق إلى تحويل المعادلة الأصلية إلى معادلة مكافئة أيسر حلًّا، أي أنّنا نسعى في هذا النوع إلى إيجاد الحل مباشرة من معادلة.
	</li>
	<li>
		<strong>الطرق التكرارية Iterative Method</strong>: تبدأ هذه الطرق بتخمين قيمة أولية للحل، ثم تُجري عمليات تكرارية تقرِّب من الحل، وتستمر إلى حين الاقتراب من الحل بمقدار محدّد سلفًا.
	</li>
</ol>
<p>
	تعدّ الطرق التكرارية أقل فعالية على العموم من نظيراتها المباشرة لأنّها تجري الكثير من العمليات الإضافية، ولدينا بعض الأمثلة على الطرق التكرارية مثل طريقة جاكوبي التكرارية Jacobi's Iteration Method، وطريقة <a href="https://ar.wikipedia.org/wiki/%D8%B7%D8%B1%D9%8A%D9%82%D8%A9_%D8%AC%D8%A7%D9%88%D8%B3_%D8%B3%D9%8A%D8%AF%D9%84" rel="external nofollow">جاوس - سيدل Gauss-Seidal</a>.
</p>

<p>
	إليك تطبيق لطريقة جاكوبي بلغة C:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_652_7" style="">
<span class="com">// تطبيق لطريقة جاكوبي</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">JacobisMethod</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> n</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> x</span><span class="pun">[</span><span class="pln">n</span><span class="pun">],</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> b</span><span class="pun">[</span><span class="pln">n</span><span class="pun">],</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> a</span><span class="pun">[</span><span class="pln">n</span><span class="pun">][</span><span class="pln">n</span><span class="pun">]){</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">n</span><span class="pun">];</span><span class="pln"> </span><span class="com">// شكل مُعدَّل من المتغيرات</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> rootFound</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> </span><span class="com">// راية</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> j</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">while</span><span class="pun">(!</span><span class="pln">rootFound</span><span class="pun">){</span><span class="pln">
       </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">&lt;</span><span class="pln">n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">      
           </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]=</span><span class="pln">b</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
           </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">j</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> j</span><span class="pun">&lt;</span><span class="pln">n</span><span class="pun">;</span><span class="pln"> j</span><span class="pun">++){</span><span class="pln">
               </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">i</span><span class="pun">!=</span><span class="pln">j</span><span class="pun">)</span><span class="pln"> </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]-</span><span class="pln">a</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">j</span><span class="pun">]*</span><span class="pln">x</span><span class="pun">[</span><span class="pln">j</span><span class="pun">];</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
           </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> a</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       rootFound</span><span class="pun">=</span><span class="lit">1</span><span class="pun">;</span><span class="pln">                    </span><span class="com">// التحقق من قيمة الراية</span><span class="pln">
       </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">&lt;</span><span class="pln">n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
           </span><span class="kwd">if</span><span class="pun">(!(</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]-</span><span class="pln">x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">])/</span><span class="pln">x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0.000001</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]-</span><span class="pln">x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">])/</span><span class="pln">x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0.000001</span><span class="pln"> </span><span class="pun">)){</span><span class="pln">
               rootFound</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
               </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">&lt;</span><span class="pln">n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">             </span><span class="com">// تقييم</span><span class="pln">
           x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]=</span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وإليك تطبيق لطريقة جاوس-سيدل بلغة C أيضًا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_652_9" style="">
<span class="com">// تطبيق لطريقة جاوس سيدل</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> </span><span class="typ">GaussSeidalMethod</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> n</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> x</span><span class="pun">[</span><span class="pln">n</span><span class="pun">],</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> b</span><span class="pun">[</span><span class="pln">n</span><span class="pun">],</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> a</span><span class="pun">[</span><span class="pln">n</span><span class="pun">][</span><span class="pln">n</span><span class="pun">]){</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">n</span><span class="pun">];</span><span class="pln"> </span><span class="com">// شكل معدّل من المتغيرات</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> rootFound</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> </span><span class="com">// راية</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> j</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">&lt;</span><span class="pln">n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">                  </span><span class="com">//تهيئة</span><span class="pln">
       </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]=</span><span class="pln">x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="kwd">while</span><span class="pun">(!</span><span class="pln">rootFound</span><span class="pun">){</span><span class="pln">
       </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">&lt;</span><span class="pln">n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">      
           </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]=</span><span class="pln">b</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
           </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">j</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> j</span><span class="pun">&lt;</span><span class="pln">n</span><span class="pun">;</span><span class="pln"> j</span><span class="pun">++){</span><span class="pln">
               </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">i</span><span class="pun">!=</span><span class="pln">j</span><span class="pun">)</span><span class="pln"> </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]-</span><span class="pln">a</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">j</span><span class="pun">]*</span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">j</span><span class="pun">];</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
           </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> a</span><span class="pun">[</span><span class="pln">i</span><span class="pun">][</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       rootFound</span><span class="pun">=</span><span class="lit">1</span><span class="pun">;</span><span class="pln">                    </span><span class="com">// التحقق من قيمة الراية</span><span class="pln">
       </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">&lt;</span><span class="pln">n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
           </span><span class="kwd">if</span><span class="pun">(!(</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]-</span><span class="pln">x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">])/</span><span class="pln">x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0.000001</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]-</span><span class="pln">x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">])/</span><span class="pln">x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0.000001</span><span class="pln"> </span><span class="pun">)){</span><span class="pln">
               rootFound</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
               </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">&lt;</span><span class="pln">n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">             </span><span class="com">// تقييم</span><span class="pln">
           x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]=</span><span class="typ">Nx</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// طباعة المصفوفة</span><span class="pln">
</span><span class="kwd">void</span><span class="pln"> print</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> n</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">double</span><span class="pln"> x</span><span class="pun">[</span><span class="pln">n</span><span class="pun">]){</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">&lt;</span><span class="pln">n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">
       printf</span><span class="pun">(</span><span class="str">"%lf, "</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]);</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"\n\n"</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(){</span><span class="pln">
   </span><span class="com">// تهيئة المعادلة</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> n</span><span class="pun">=</span><span class="lit">3</span><span class="pun">;</span><span class="pln">    </span><span class="com">// عدد المتغيرات</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> x</span><span class="pun">[</span><span class="pln">n</span><span class="pun">];</span><span class="pln">    </span><span class="com">// المتغيرات</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> b</span><span class="pun">[</span><span class="pln">n</span><span class="pun">],</span><span class="pln">    </span><span class="com">// الثوابت</span><span class="pln">
       a</span><span class="pun">[</span><span class="pln">n</span><span class="pun">][</span><span class="pln">n</span><span class="pun">];</span><span class="pln">    </span><span class="com">// المعاملات</span><span class="pln">
   </span><span class="com">//تعيين القيم</span><span class="pln">
   a</span><span class="pun">[</span><span class="lit">0</span><span class="pun">][</span><span class="lit">0</span><span class="pun">]=</span><span class="lit">8</span><span class="pun">;</span><span class="pln">   a</span><span class="pun">[</span><span class="lit">0</span><span class="pun">][</span><span class="lit">1</span><span class="pun">]=</span><span class="lit">2</span><span class="pun">;</span><span class="pln">   a</span><span class="pun">[</span><span class="lit">0</span><span class="pun">][</span><span class="lit">2</span><span class="pun">]=-</span><span class="lit">2</span><span class="pun">;</span><span class="pln"> b</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]=</span><span class="lit">8</span><span class="pun">;</span><span class="pln">    </span><span class="com">//8x₁+2x₂-2x₃+8=0    </span><span class="pln">
   a</span><span class="pun">[</span><span class="lit">1</span><span class="pun">][</span><span class="lit">0</span><span class="pun">]=</span><span class="lit">1</span><span class="pun">;</span><span class="pln">   a</span><span class="pun">[</span><span class="lit">1</span><span class="pun">][</span><span class="lit">1</span><span class="pun">]=-</span><span class="lit">8</span><span class="pun">;</span><span class="pln">  a</span><span class="pun">[</span><span class="lit">1</span><span class="pun">][</span><span class="lit">2</span><span class="pun">]=</span><span class="lit">3</span><span class="pun">;</span><span class="pln"> b</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]=-</span><span class="lit">4</span><span class="pun">;</span><span class="pln">    </span><span class="com">//x₁-8x₂+3x₃-4=0    </span><span class="pln">
   a</span><span class="pun">[</span><span class="lit">2</span><span class="pun">][</span><span class="lit">0</span><span class="pun">]=</span><span class="lit">2</span><span class="pun">;</span><span class="pln">   a</span><span class="pun">[</span><span class="lit">2</span><span class="pun">][</span><span class="lit">1</span><span class="pun">]=</span><span class="lit">1</span><span class="pun">;</span><span class="pln">   a</span><span class="pun">[</span><span class="lit">2</span><span class="pun">][</span><span class="lit">2</span><span class="pun">]=</span><span class="lit">9</span><span class="pun">;</span><span class="pln"> b</span><span class="pun">[</span><span class="lit">2</span><span class="pun">]=</span><span class="lit">12</span><span class="pun">;</span><span class="pln">    </span><span class="com">//2x₁+x₂+9x₃+12=0</span><span class="pln">

   </span><span class="typ">int</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">&lt;</span><span class="pln">n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">                         </span><span class="com">//  تهيئة</span><span class="pln">
       x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="typ">JacobisMethod</span><span class="pun">(</span><span class="pln">n</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> a</span><span class="pun">);</span><span class="pln">
   print</span><span class="pun">(</span><span class="pln">n</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">i</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">&lt;</span><span class="pln">n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++){</span><span class="pln">                         </span><span class="com">//تهيئة</span><span class="pln">
       x</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   </span><span class="typ">GaussSeidalMethod</span><span class="pun">(</span><span class="pln">n</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> a</span><span class="pun">);</span><span class="pln">
   print</span><span class="pun">(</span><span class="pln">n</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	المعادلات غير الخطية Non-Linear Equations
</h2>

<p>
	تُصنَّف المعادلات من النوع <code>‎‎</code>f(x)= 0 إلى صنفين: إما جبرية أو غير جبرية -أو <a href="https://ar.wikipedia.org/wiki/%D8%AF%D8%A7%D9%84%D8%A9_%D9%85%D8%AA%D8%B3%D8%A7%D9%85%D9%8A%D8%A9" rel="external nofollow">متسامية transcendental</a>- ويمكن حل المعادلات غير الخطية باستخدام أحد أسلوبين:
</p>

<ol>
<li>
		<strong>الأسلوب المباشر</strong>: يعطي هذا الأسلوب القيمة الدقيقة لكل جذور <code>f</code> (حلول المعادلة) مباشرة في عدد محدود من الخطوات.
	</li>
	<li>
		<strong>الأسلوب غير المباشر أو التكراري</strong>: هذا النوع أصلح من النوع الأول لحل المعادلات عبر الحاسوب، ويُبنى على مبدأ التقريب المتتالي، ولدينا طريقتين لحل المعادلة في الأسلوب التكراري:
		<ul>
<li>
				<strong>طريقة الحصار Bracketing Method</strong>: نأخذ نقطتين أوليّتين نعلم أنّ الجذر يقع بينهما، ثم نستمر في تضييق طول المجال الذي يحاصر الجذر إلى أن نصل إلى طول تقريبي معيّن. تُعد خوارزمية <a href="https://ar.wikipedia.org/wiki/%D8%B7%D8%B1%D9%8A%D9%82%D8%A9_%D8%A7%D9%84%D8%AA%D9%86%D8%B5%D9%8A%D9%81" rel="external nofollow">التنصيف</a> من أشهر الخوارزميات التي تستخدم طريقة الحصار.
			</li>
			<li>
				<strong>طريقة النهاية المفتوحة Open End Method</strong>: نأخذ قيمة أولية أو قيمتين، ولا يُشترط أن تحاصر هاتان القيمتان جذر المعادلة، ثم نكرّر إجراء عمليات حسابية على هاتين القيمتين. وعادة ما يحدث هنا أحد أمرين، إمّا أن تتباعد القيمتان مع تكرار العمليات، أو تتقاربان -أي تؤُولان إلى نقطة واحدة، فإن كانتا متقاربتين فإنّ نقطة التقارب ستكون هي الحل. هذه الطريقة أسرع عمومًا من طريقة الحصار، ويُعد أسلوب نيوتن-رافسون Newton-Raphson، وأسلوب التقريب المتتالي Successive Approximation Method، وأسلوب القاطع Secant Method من الأمثلة على هذه الطريقة.
			</li>
		</ul>
</li>
</ol>
<p>
	هذا تطبيق بلغة C للحلول السابقة كلها على معادلات وضعناها في بداية الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_652_11" style="">
<span class="com">// دوال مساعدة</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="pun">((</span><span class="pln">x</span><span class="pun">)*(</span><span class="pln">x</span><span class="pun">)*(</span><span class="pln">x</span><span class="pun">))</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> f2</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="pun">(</span><span class="lit">3</span><span class="pun">*(</span><span class="pln">x</span><span class="pun">)*(</span><span class="pln">x</span><span class="pun">))</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="com">#define</span><span class="pln"> g</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> cbrt</span><span class="pun">(</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="com">/**
* نأخذ قيمتية أوليتين ونقصّر المسافة من كلا الجانبين
**/</span><span class="pln">
</span><span class="kwd">double</span><span class="pln"> </span><span class="typ">BisectionMethod</span><span class="pun">(){</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> root</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> a</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">=</span><span class="lit">2</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> c</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> loopCounter</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">f</span><span class="pun">(</span><span class="pln">a</span><span class="pun">)*</span><span class="pln">f</span><span class="pun">(</span><span class="pln">b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
       </span><span class="kwd">while</span><span class="pun">(</span><span class="lit">1</span><span class="pun">){</span><span class="pln">
           loopCounter</span><span class="pun">++;</span><span class="pln">
           c</span><span class="pun">=(</span><span class="pln">a</span><span class="pun">+</span><span class="pln">b</span><span class="pun">)/</span><span class="lit">2</span><span class="pun">;</span><span class="pln">
           </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">f</span><span class="pun">(</span><span class="pln">c</span><span class="pun">)&lt;</span><span class="lit">0.00001</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">c</span><span class="pun">)&gt;-</span><span class="lit">0.00001</span><span class="pun">){</span><span class="pln">
               root</span><span class="pun">=</span><span class="pln">c</span><span class="pun">;</span><span class="pln">
               </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
           </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">f</span><span class="pun">(</span><span class="pln">a</span><span class="pun">))*(</span><span class="pln">f</span><span class="pun">(</span><span class="pln">c</span><span class="pun">))</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
               b</span><span class="pun">=</span><span class="pln">c</span><span class="pun">;</span><span class="pln">
           </span><span class="pun">}</span><span class="kwd">else</span><span class="pun">{</span><span class="pln">
               a</span><span class="pun">=</span><span class="pln">c</span><span class="pun">;</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"It took %d loops.\n"</span><span class="pun">,</span><span class="pln"> loopCounter</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> root</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">/**
* نأخذ قيمتية أوليتين ونقصّر المسافة من جانب واحد
**/</span><span class="pln">
</span><span class="kwd">double</span><span class="pln"> </span><span class="typ">FalsePosition</span><span class="pun">(){</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> root</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> a</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">=</span><span class="lit">2</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> c</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> loopCounter</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">f</span><span class="pun">(</span><span class="pln">a</span><span class="pun">)*</span><span class="pln">f</span><span class="pun">(</span><span class="pln">b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
       </span><span class="kwd">while</span><span class="pun">(</span><span class="lit">1</span><span class="pun">){</span><span class="pln">
           loopCounter</span><span class="pun">++;</span><span class="pln">
           c</span><span class="pun">=(</span><span class="pln">a</span><span class="pun">*</span><span class="pln">f</span><span class="pun">(</span><span class="pln">b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> b</span><span class="pun">*</span><span class="pln">f</span><span class="pun">(</span><span class="pln">a</span><span class="pun">))</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="pun">(</span><span class="pln">f</span><span class="pun">(</span><span class="pln">b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">a</span><span class="pun">));</span><span class="pln">
           </span><span class="com">/*/printf("%lf\t %lf \n", c, f(c));/**////test</span><span class="pln">
           </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">f</span><span class="pun">(</span><span class="pln">c</span><span class="pun">)&lt;</span><span class="lit">0.00001</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">c</span><span class="pun">)&gt;-</span><span class="lit">0.00001</span><span class="pun">){</span><span class="pln">
               root</span><span class="pun">=</span><span class="pln">c</span><span class="pun">;</span><span class="pln">
               </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
           </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">f</span><span class="pun">(</span><span class="pln">a</span><span class="pun">))*(</span><span class="pln">f</span><span class="pun">(</span><span class="pln">c</span><span class="pun">))</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">){</span><span class="pln">
               b</span><span class="pun">=</span><span class="pln">c</span><span class="pun">;</span><span class="pln">
           </span><span class="pun">}</span><span class="kwd">else</span><span class="pun">{</span><span class="pln">
               a</span><span class="pun">=</span><span class="pln">c</span><span class="pun">;</span><span class="pln">
           </span><span class="pun">}</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"It took %d loops.\n"</span><span class="pun">,</span><span class="pln"> loopCounter</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> root</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">/**
* نستخدم قيمة أولية واحدة، ثمّ نتدرّج نحو القيمة الصحيحة
**/</span><span class="pln">
</span><span class="kwd">double</span><span class="pln"> </span><span class="typ">NewtonRaphson</span><span class="pun">(){</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> root</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> x1</span><span class="pun">=</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> x2</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> loopCounter</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">while</span><span class="pun">(</span><span class="lit">1</span><span class="pun">){</span><span class="pln">
       loopCounter</span><span class="pun">++;</span><span class="pln">
       x2 </span><span class="pun">=</span><span class="pln"> x1 </span><span class="pun">-</span><span class="pln"> </span><span class="pun">(</span><span class="pln">f</span><span class="pun">(</span><span class="pln">x1</span><span class="pun">)/</span><span class="pln">f2</span><span class="pun">(</span><span class="pln">x1</span><span class="pun">));</span><span class="pln">
       </span><span class="com">/*/printf("%lf \t %lf \n", x2, f(x2));/**////test</span><span class="pln">
       </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">f</span><span class="pun">(</span><span class="pln">x2</span><span class="pun">)&lt;</span><span class="lit">0.00001</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">x2</span><span class="pun">)&gt;-</span><span class="lit">0.00001</span><span class="pun">){</span><span class="pln">
           root</span><span class="pun">=</span><span class="pln">x2</span><span class="pun">;</span><span class="pln">
           </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       x1</span><span class="pun">=</span><span class="pln">x2</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"It took %d loops.\n"</span><span class="pun">,</span><span class="pln"> loopCounter</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> root</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">/**
* نستخدم قيمة أولية واحدة، ثمّ نتدرّج نحو القيمة الصحيحة
**/</span><span class="pln">
</span><span class="kwd">double</span><span class="pln"> </span><span class="typ">FixedPoint</span><span class="pun">(){</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> root</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> x</span><span class="pun">=</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> loopCounter</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">while</span><span class="pun">(</span><span class="lit">1</span><span class="pun">){</span><span class="pln">
       loopCounter</span><span class="pun">++;</span><span class="pln">
       </span><span class="kwd">if</span><span class="pun">(</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">-</span><span class="pln">g</span><span class="pun">(</span><span class="pln">x</span><span class="pun">))</span><span class="pln"> </span><span class="pun">&lt;</span><span class="lit">0.00001</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">-</span><span class="pln">g</span><span class="pun">(</span><span class="pln">x</span><span class="pun">))</span><span class="pln"> </span><span class="pun">&gt;-</span><span class="lit">0.00001</span><span class="pun">){</span><span class="pln">
           root </span><span class="pun">=</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
           </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       </span><span class="com">/*/printf("%lf \t %lf \n", g(x), x-(g(x)));/**////test</span><span class="pln">
       x</span><span class="pun">=</span><span class="pln">g</span><span class="pun">(</span><span class="pln">x</span><span class="pun">);</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"It took %d loops.\n"</span><span class="pun">,</span><span class="pln"> loopCounter</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> root</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">/**
*استخدام قيمتين أوليتين، بحيث تقترب كلاهما من الحل الصحيح
**/</span><span class="pln">
</span><span class="kwd">double</span><span class="pln"> </span><span class="typ">Secant</span><span class="pun">(){</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> root</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> x0</span><span class="pun">=</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> x1</span><span class="pun">=</span><span class="lit">2</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> x2</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="typ">int</span><span class="pln"> loopCounter</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">while</span><span class="pun">(</span><span class="lit">1</span><span class="pun">){</span><span class="pln">
       loopCounter</span><span class="pun">++;</span><span class="pln">
       </span><span class="com">/*/printf("%lf \t %lf \t %lf \n", x0, x1, f(x1));/**////test</span><span class="pln">
       </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">f</span><span class="pun">(</span><span class="pln">x1</span><span class="pun">)&lt;</span><span class="lit">0.00001</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">x1</span><span class="pun">)&gt;-</span><span class="lit">0.00001</span><span class="pun">){</span><span class="pln">
           root</span><span class="pun">=</span><span class="pln">x1</span><span class="pun">;</span><span class="pln">
           </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       x2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">((</span><span class="pln">x0</span><span class="pun">*</span><span class="pln">f</span><span class="pun">(</span><span class="pln">x1</span><span class="pun">))-(</span><span class="pln">x1</span><span class="pun">*</span><span class="pln">f</span><span class="pun">(</span><span class="pln">x0</span><span class="pun">)))</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="pun">(</span><span class="pln">f</span><span class="pun">(</span><span class="pln">x1</span><span class="pun">)-</span><span class="pln">f</span><span class="pun">(</span><span class="pln">x0</span><span class="pun">));</span><span class="pln">
       x0</span><span class="pun">=</span><span class="pln">x1</span><span class="pun">;</span><span class="pln">
       x1</span><span class="pun">=</span><span class="pln">x2</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"It took %d loops.\n"</span><span class="pun">,</span><span class="pln"> loopCounter</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> root</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">(){</span><span class="pln">
   </span><span class="kwd">double</span><span class="pln"> root</span><span class="pun">;</span><span class="pln">
   root </span><span class="pun">=</span><span class="pln"> </span><span class="typ">BisectionMethod</span><span class="pun">();</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"Using Bisection Method the root is: %lf \n\n"</span><span class="pun">,</span><span class="pln"> root</span><span class="pun">);</span><span class="pln">

   root </span><span class="pun">=</span><span class="pln"> </span><span class="typ">FalsePosition</span><span class="pun">();</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"Using False Position Method the root is: %lf \n\n"</span><span class="pun">,</span><span class="pln"> root</span><span class="pun">);</span><span class="pln">

   root </span><span class="pun">=</span><span class="pln"> </span><span class="typ">NewtonRaphson</span><span class="pun">();</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"Using Newton-Raphson Method the root is: %lf \n\n"</span><span class="pun">,</span><span class="pln"> root</span><span class="pun">);</span><span class="pln">

   root </span><span class="pun">=</span><span class="pln"> </span><span class="typ">FixedPoint</span><span class="pun">();</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"Using Fixed Point Method the root is: %lf \n\n"</span><span class="pun">,</span><span class="pln"> root</span><span class="pun">);</span><span class="pln">

   root </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Secant</span><span class="pun">();</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"Using Secant Method the root is: %lf \n\n"</span><span class="pun">,</span><span class="pln"> root</span><span class="pun">);</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ترجمة -بتصرّف- للفصل 46 من كتاب <a href="https://goalkicker.com/AlgorithmsBook/" rel="external nofollow">Algorithms Notes for Professionals</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/advanced/%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%84%D9%89-%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D9%84%D8%AD%D9%84-%D9%85%D8%B4%D9%83%D9%84%D8%A7%D8%AA-%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-r1533/" rel=""> أمثلة على خوارزميات لحل مشكلات بسيطة</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/advanced/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D9%82%D8%B7%D9%8A%D8%B9-hash-functions-%D9%81%D9%8A-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1428/" rel="">مفهوم دوال التقطيع Hash Functions في الخوارزميات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%AF%D9%84%D9%8A%D9%84-%D8%B4%D8%A7%D9%85%D9%84-%D8%B9%D9%86-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A9-r1247/?msclkid=99f12c50c12c11eca11a64a22e04ae9b" rel="">دليل شامل عن تحليل تعقيد الخوارزمية</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1534</guid><pubDate>Fri, 08 Apr 2022 15:00:00 +0000</pubDate></item><item><title>&#x646;&#x638;&#x631;&#x629; &#x633;&#x631;&#x64A;&#x639;&#x629; &#x639;&#x644;&#x649; &#x628;&#x639;&#x636; &#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x627;&#x62A; &#x627;&#x644;&#x62A;&#x631;&#x62A;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/advanced/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B3%D8%B1%D9%8A%D8%B9%D8%A9-%D8%B9%D9%84%D9%89-%D8%A8%D8%B9%D8%B6-%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8-r1456/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_01/61f7bb87ef0f9_-----.png.e91814a958f84aeb5c6e85357fedf360.png" /></p>
<p>
	لدى <a href="https://academy.hsoub.com/programming/general/%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/#%D8%AA%D8%AE%D8%B5%D8%B5%D8%A7%D8%AA-%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8" rel="">أقسام علوم الحاسوب</a> هوس غير طبيعي بخوارزميات الترتيب، فبناءً على الوقت الذي يمضيه طلبة <a href="https://academy.hsoub.com/programming/general/%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8" rel="">علوم الحاسوب</a> في دراسة هذا الموضوع، قد تظن أن الاختيار ما بين خوارزميات الترتيب هو حجر الزاوية في <a href="https://academy.hsoub.com/programming/general/%D9%87%D9%86%D8%AF%D8%B3%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA/" rel="">هندسة البرمجيات</a> الحديثة. واقع الأمر هو أن مطوري البرمجيات قد يقضون سنوات قد تصل إلى مسارهم المهني بأكمله دون التفكير في طريقة حدوث عملية الترتيب، فهم يَستخدِمون في كل التطبيقات تقريبًا الخوارزمية متعددة الأغراض التي توفِّرها اللغة أو المكتبة التي يستخدمونها، والتي تكون كافيةً في معظم الحالات.
</p>

<p>
	لذلك لو تجاهلت هذه المقالة ولم تتعلم أي شيء عن <a href="https://wiki.hsoub.com/Algorithms/Sorting_Algorithms" rel="external">خوارزميات الترتيب</a>، فما يزال بإمكانك أن تصبح مُطوِّر برمجيات جيدًا، ومع ذلك، هناك عدة أسباب قد تدفعك لقراءته:
</p>

<ol>
	<li>
		على الرغم من وجود خوارزميات متعددة الأغراض يُمكِنها العمل بشكل جيد في غالبية التطبيقات، هنالك خوارزميّتان مُتخصّصتان قد تحتاج إلى معرفة القليل عنهما: الترتيب بالجذر radix sort والترتيب بالكومة المقيدّة bounded heap sort.
	</li>
	<li>
		تُعدّ خوارزمية الترتيب بالدمج merge sort المثال التعليميّ الأمثل، فهي تُوضِّح استراتيجية "قسِّم واغزُ divide and conquer" المهمة والمفيدة في تصميم الخوارزميات. بالإضافة إلى ذلك، ستتعلم عن ترتيب نمو order of growth لم تَرَهُ من قبل، هو ترتيب النمو "الخطي-اللوغاريتمي linearithmic". ومن الجدير بالذكر أن غالبية الخوارزميات الشهيرة تكون عادةً خوارزميّاتٍ هجينةً وتستخدم بشكلٍ أو بآخر فكرة الترتيب بالدمج.
	</li>
	<li>
		أحد الأسباب الأخرى التي قد تدفعك إلى تعلم خوارزميات الترتيب هي مقابلات العمل التقنية، فعادةً ما تُسأل خلالها عن تلك الخوارزميات. إذا كنت تريد الحصول على وظيفة، فسيُساعدك إظهار اطلاعك على أبجديات علوم الحاسوب.
	</li>
</ol>

<p>
	سنُحلِّل في هذا الفصل خوارزمية الترتيب بالإدراج insertion sort، وسننفِّذ خوارزمية الترتيب بالدمج، وأخيرًا، سنشرح خوارزمية الترتيب بالجذر، وسنكتب نسخةً بسيطةً من خوارزمية الترتيب بالكومة المُقيّدة.
</p>

<h2>
	الترتيب بالإدراج Insertion sort
</h2>

<p>
	سنبدأ بخوارزمية الترتيب بالإدراج، لأنها بسيطة ومهمة. على الرغم من أنها ليست الخوارزمية الأكفأ إلا أنها تملك بعض الميزات المتعلقة بتحرير الذاكرة كما سنرى لاحقًا.
</p>

<p>
	لن نشرح هذه الخوارزمية هنا، ولكن يُفضّلُ لو قرأت مقالة ويكيبيديا عن <a href="https://wiki.hsoub.com/Algorithms/insertion_sort" rel="external">الترتيب بالإدراج</a> Insertion Sort، فهي تحتوي على شيفرة وهمية وأمثلة متحركة. وبعدما تفهم فكرتها العامة يمكنك متابعة القراءة هنا.
</p>

<p>
	تَعرِض الشيفرة التالية تنفيذًا بلغة جافا لخوارزمية الترتيب بالإدراج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5201_7" style=""><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ListSorter</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> insertionSort</span><span class="pun">(</span><span class="typ">List</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> list</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Comparator</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> comparator</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> i</span><span class="pun">=</span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> list</span><span class="pun">.</span><span class="pln">size</span><span class="pun">();</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            T elt_i </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
            </span><span class="kwd">int</span><span class="pln"> j </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
            </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">j </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                T elt_j </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">j</span><span class="pun">-</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
                </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">comparator</span><span class="pun">.</span><span class="pln">compare</span><span class="pun">(</span><span class="pln">elt_i</span><span class="pun">,</span><span class="pln"> elt_j</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
                list</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">j</span><span class="pun">,</span><span class="pln"> elt_j</span><span class="pun">);</span><span class="pln">
                j</span><span class="pun">--;</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
            list</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">j</span><span class="pun">,</span><span class="pln"> elt_i</span><span class="pun">);</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عرَّفنا الصنف <code>ListSorter</code> ليَعمَل كحاوٍ لخوارزميات الترتيب. نظرًا لأننا استخدمنا معامل نوع type parameter، اسمه <code>T</code>، ستتمكَّن التوابع التي سنكتبها من العمل مع قوائم تحتوي على أي نوع من الكائنات.
</p>

<p>
	يستقبل التابع <code>insertionSort</code> معاملين: الأول عبارة عن قائمة من أي نوع ممتدّ من الواجهة <code>List</code> والثاني عبارة عن كائن من النوع <code>Comparator</code> بإمكانه موازنة كائنات النوع <code>T</code>. يُرتِّب هذا التابع القائمة في نفس المكان أي أنه يُعدِّل القائمة الموجودة ولا يحتاج إلى حجز مساحة إضافية جديدة.
</p>

<p>
	تَستدعِي الشيفرة التالية هذا التابع مع قائمة من النوع <code>List</code> تحتوي على كائنات من النوع <code>Integer</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5201_9" style=""><span class="pln">        </span><span class="typ">List</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">list</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayList</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;(</span><span class="pln">
            </span><span class="typ">Arrays</span><span class="pun">.</span><span class="pln">asList</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">));</span><span class="pln">

        </span><span class="typ">Comparator</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> comparator </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Comparator</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="lit">@Override</span><span class="pln">
            </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> compare</span><span class="pun">(</span><span class="typ">Integer</span><span class="pln"> elt1</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Integer</span><span class="pln"> elt2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln"> elt1</span><span class="pun">.</span><span class="pln">compareTo</span><span class="pun">(</span><span class="pln">elt2</span><span class="pun">);</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">};</span><span class="pln">

        </span><span class="typ">ListSorter</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;</span><span class="pln"> sorter </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ListSorter</span><span class="pun">&lt;</span><span class="typ">Integer</span><span class="pun">&gt;();</span><span class="pln">
        sorter</span><span class="pun">.</span><span class="pln">insertionSort</span><span class="pun">(</span><span class="typ">list</span><span class="pun">,</span><span class="pln"> comparator</span><span class="pun">);</span><span class="pln">
        </span><span class="typ">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">println</span><span class="pun">(</span><span class="typ">list</span><span class="pun">);</span></pre>

<p>
	يحتوي التابع <code>insertionSort</code> على حلقتين متداخلتين nested loops، ولذلك، قد تظن أن زمن تنفيذه تربيعي، وهذا صحيحٌ في تلك الحالة، ولكن قبل أن تتوصل إلى تلك النتيجة، عليك أولًا أن تتأكّد من أن عدد مرات تنفيذِ كل حلقةٍ يتناسب مع حجم المصفوفة n.
</p>

<p>
	تتكرر الحلقة الخارجية من 1 إلى <code>list.size()‎</code>، ولذلك تُعدّ خطيّةً بالنسبة لحجم القائمة n، بينما تتكرر الحلقة الداخلية من <code>i</code> إلى صفر، لذلك هي أيضًا خطيّة بالنسبة لقيمة n. بناءً على ذلك، يكون عدد مرات تنفيذ الحلقة الداخلية تربيعيًّا.
</p>

<p>
	إذا لم تكن متأكّدًا من ذلك، انظر إلى البرهان التالي:
</p>

<ul>
	<li>
		في المرة الأولى، قيمة <code>i</code> تساوي 1، وتتكرر الحلقة الداخليّة مرةً واحدةً على الأكثر.
	</li>
	<li>
		في المرة الثانية، قيمة <code>i</code> تساوي 2، وتتكرر الحلقة الداخليّة مرتين على الأكثر.
	</li>
	<li>
		في المرة الأخيرة، قيمة <code>i</code> تساوي n-1، وتتكرر الحلقة الداخليّة عددًا قدره n-1 من المرات على الأكثر.
	</li>
</ul>

<p>
	وبالتالي، يكون عدد مرات تنفيذ الحلقة الداخلية هو مجموع المتتالية 1، 2، … حتى n-1، وهو ما يُساوِي n(n-1)/2. لاحِظ أن القيمة الأساسية (ذات الأس الأكبر) بهذا التعبير هي n<sup>2</sup>‎.
</p>

<p>
	يُعدّ الترتيب بالإدراج تربيعيًا في أسوأ حالة، ومع ذلك:
</p>

<ol>
	<li>
		إذا كانت العناصر مُرتَّبةً أو شبه مُرتَّبةٍ بالفعل، فإن الترتيب بالإدراج يكون خطّيًّا. بالتحديد، إذا لم يكن كل عنصرٍ أبعدَ من موضعه الصحيح مسافةً أكبرَ من k، فإن الحلقة الداخلية لن تُنفَّذ أكثر من عددٍ قدره k من المرات، ويكون زمن التنفيذ الكلي هو O(kn)‎.
	</li>
	<li>
		نظرًا لأن تنفيذ تلك الخوارزمية بسيط، فإن تكلفته منخفضة، أي على الرغم من أن زمن التنفيذ يساوي a n<sup>2</sup>‎، إلا أن المعامل a قد يكون صغيرًا.
	</li>
</ol>

<p>
	ولذلك، إذا عرفنا أن المصفوفة شبه مُرتَّبة أو إذا لم تكن كبيرةً جدًا، فقد يكون الترتيب بالإدراج خيارًا جيدًا، ولكن بالنسبة للمصفوفات الكبيرة، فهنالك خيارات أفضل بكثير.
</p>

<h2>
	تمرين 14
</h2>

<p>
	تُعدّ خوارزمية الترتيب بالدمج merge sort واحدةً من ضمن مجموعةٍ من <a href="https://academy.hsoub.com/programming/advanced/%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%86-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1410/" rel="">الخوارزميات</a> التي يتفوق زمن تنفيذها على الزمن التربيعيّ. ننصحك قبل المتابعة بقراءة مقالة ويكيبيديا عن <a href="https://wiki.hsoub.com/Algorithms/merge_sort" rel="external">الترتيب بالدمج</a> merge sort. بعد أن تفهم الفكرة العامة للخوارزمية، يُمكِنك العودة لاختبار فهمك بكتابة تنفيذٍ لها.
</p>

<p>
	ستجد ملفات الشيفرة التالية الخاصة بالتمرين في <a href="https://github.com/AllenDowney/ThinkDataStructures" rel="external nofollow">مستودع الكتاب</a>:
</p>

<ul>
	<li>
		<a href="https://github.com/AllenDowney/ThinkDataStructures/blob/cc10971a1904eda5f5b818a3d70ee1a836fd8799/solutions/src/com/allendowney/thinkdast/ListSorter.java" rel="external nofollow">ListSorter.java</a>
	</li>
	<li>
		<a href="https://github.com/AllenDowney/ThinkDataStructures/blob/cc10971a1904eda5f5b818a3d70ee1a836fd8799/solutions/src/com/allendowney/thinkdast/ListSorterTest.java" rel="external nofollow">ListSorterTest.java</a>
	</li>
</ul>

<p>
	نفِّذ الأمر <code>ant build</code> لتصريف ملفات الشيفرة ثم نفِّذ الأمر <code>ant ListSorterTest</code>. سيفشل الاختبار كالعادة لأن ما يزال عليك إكمال بعض الشيفرة.
</p>

<p>
	ستجد ضمن الملف ListSorter.java شيفرةً مبدئيّةً للتابعين <code>mergeSortInPlace</code> و <code>mergeSort</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5201_11" style=""><span class="pln">    </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> mergeSortInPlace</span><span class="pun">(</span><span class="typ">List</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> list</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Comparator</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> comparator</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">List</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> sorted </span><span class="pun">=</span><span class="pln"> mergeSortHelper</span><span class="pun">(</span><span class="pln">list</span><span class="pun">,</span><span class="pln"> comparator</span><span class="pun">);</span><span class="pln">
        list</span><span class="pun">.</span><span class="pln">clear</span><span class="pun">();</span><span class="pln">
        list</span><span class="pun">.</span><span class="pln">addAll</span><span class="pun">(</span><span class="pln">sorted</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">private</span><span class="pln"> </span><span class="typ">List</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> mergeSort</span><span class="pun">(</span><span class="typ">List</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> list</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Comparator</span><span class="pun">&lt;</span><span class="pln">T</span><span class="pun">&gt;</span><span class="pln"> comparator</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="com">// TODO: fill this in!</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span></pre>

<p>
	يقوم التابعان بنفس الشيء، ولكنهما يوفران واجهاتٍ مختلفة. يَستقبِل التابع <code>mergeSort</code> قائمةً ويعيد قائمةً جديدةً تحتوي على نفس العناصر بعد ترتيبها ترتيبًا تصاعديًا. في المقابل، يُعدِّل التابع <code>mergeSortInPlace</code> القائمة ذاتها ولا يعيد أيّة قيمة.
</p>

<p>
	عليك إكمال التابع <code>mergeSort</code>. ويمكنك بدلًا من كتابة نسخة تعاودية recursive بالكامل، أن تتبع الطريقة التالية:
</p>

<ol>
	<li>
		قسِّم القائمة إلى نصفين.
	</li>
	<li>
		رتِّب النصفين باستخدام التابع <code>Collections.sort</code> أو التابع <code>insertionSort</code>.
	</li>
	<li>
		ادمج النصفين المُرتَّبين إلى قائمة واحدة مُرتَّبة.
	</li>
</ol>

<p>
	سيعطيك هذا التمرين الفرصة لتنقيح شيفرة الدمج دون التعامل مع تعقيدات التوابع التعاودية.
</p>

<p>
	والآن أضف <a href="https://wiki.hsoub.com/Algorithms/Recursion" rel="external">حالة أساسية base case</a>. إذا كان لديك قائمة تحتوي على عنصر واحد فقط، يُمكِنك أن تعيدها مباشرةً لأنها نوعًا ما مُرتَّبة بالفعل، وإذا كان طول القائمة أقل من قيمة معينة، يُمكِنك أن تُرتِّبها باستخدام التابع <code>Collections.sort</code> أو التابع <code>insertionSort</code>. اختبر الحالة الأساسية قبل إكمال القراءة.
</p>

<p>
	أخيرًا، عدِّل الحل واجعله يُنفِّذ استدعاءين تعاوديّين لترتيب نصفي المصفوفة. إذا عدلته بالشكل الصحيح، ينبغي أن ينجح الاختباران <code>testMergeSort</code> و <code>testMergeSortInPlace</code>.
</p>

<h2>
	تحليل أداء خوارزمية الترتيب بالدمج
</h2>

<p>
	لكي نصنف زمن تنفيذ خوارزمية الترتيب بالدمج، علينا أن نفكر بمستويات التعاود وبكمية العمل المطلوب في كل مستوى. لنفترض أننا سنبدأ بقائمةٍ تحتوي على عددٍ قدره n من العناصر. وفيما يلي خطوات الخوارزمية:
</p>

<ol>
	<li>
		نُنشِئ مصفوفتين وننسخ نصف العناصر إليهما.
	</li>
	<li>
		نُرتِّب النصفين.
	</li>
	<li>
		ندمج النصفين.
	</li>
</ol>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91076" href="https://academy.hsoub.com/uploads/monthly_2022_01/001Merge_Sort_One_Level.PNG.4a594e78302d7ffc388b4d4084faed54.PNG" rel="" data-fileext="PNG"><img alt="001Merge_Sort_One_Level.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="91076" data-unique="4isgvxcq6" src="https://academy.hsoub.com/uploads/monthly_2022_01/001Merge_Sort_One_Level.PNG.4a594e78302d7ffc388b4d4084faed54.PNG"></a>
</p>

<p>
	تنسخ الخطوة الأولى كل عنصر مرةً واحدةً، أي أنها خطّيّة. بالمثل، تنسخ الخطوة الثالثة كل عنصر مرةً واحدةً فقط، أي أنها خطّيّة كذلك. علينا الآن أن نُحدِّد تعقيد الخطوة الثانية. ستساعدنا على ذلك الصورة التالية التي تَعرِض مستويات التعاود.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91077" href="https://academy.hsoub.com/uploads/monthly_2022_01/002Merge_Sort_All_Levels.PNG.d62b06539ae593b91a65356ddb2a8977.PNG" rel="" data-fileext="PNG"><img alt="002Merge_Sort_All_Levels.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="91077" data-unique="61zhns2as" src="https://academy.hsoub.com/uploads/monthly_2022_01/002Merge_Sort_All_Levels.PNG.d62b06539ae593b91a65356ddb2a8977.PNG"></a>
</p>

<p>
	في المستوى الأعلى، سيكون لدينا قائمة واحدة مُكوَّنة من عددٍ قدره n من العناصر. للتبسيط، سنفترض أن n عبارة عن قيمة مرفوعة للأس 2، وبالتالي، سيكون لدينا في المستوى التالي قائمتان تحتويان على عدد n/2 من العناصر. ثمّ في المستوى التالي، سيكون لدينا 4 قوائم تحتوي على عدد قدره n/4 من العناصر، وهكذا حتى نصل إلى عدد n من القوائم تحتوي جميعها على عنصر واحد فقط.
</p>

<p>
	لدينا إذًا عدد قدره n من العناصر في كل مستوى. أثناء نزولنا في المستويات، قسّمنا المصفوفات في كل مستوى إلى نصفين، وهو ما يستغرق زمنًا يتناسب مع n في كل مستوى، وأثناء صعودنا للأعلى، علينا أن ندمج عددًا من العناصر مجموعه n وهو ما يستغرق زمنًا خطيًا أيضًا.
</p>

<p>
	إذا كان عدد المستويات يساوي h، فإن العمل الإجمالي المطلوب يساوي O(nh)‎، والآن، كم هو عدد المستويات؟ يُمكِننا أن نفكر في ذلك بطريقتين:
</p>

<ol>
	<li>
		كم عدد المرات التي سنضطر خلالها لتقسيم n إلى نصفين حتى نصل إلى 1.
	</li>
	<li>
		أو كم عدد المرات التي سنضطرّ خلالها لمضاعفة العدد 1 قبل أن نصل إلى n.
	</li>
</ol>

<p>
	يُمكِننا طرح السؤال الثاني بطريقة أخرى: "ما هي قيمة الأس المرفوع للعدد 2 لكي نحصل على n؟".
</p>

<div style="direction: ltr; text-align: center;">
	2<sup>h</sup> = n
</div>

<p>
	بحساب لوغاريتم أساس 2 لكلا الطرفين، نحصل على التالي:
</p>

<div style="direction: ltr; text-align: center;">
	h = log<sub>2</sub> n
</div>

<p>
	أي أن الزمن الكلي يساوي O(n log(n))‎. لاحظ أننا تجاهلنا قيمة أساس اللوغاريتم لأن اختلاف أساس اللوغاريتم يؤثر فقط بعامل ثابت، أي أن جميع اللوغاريتمات لها نفس ترتيب النمو order of growth.
</p>

<p>
	يُطلَق أحيانًا على الخوارزميات التي تنتمي إلى O(n log(n))‎ اسم "خطي-لوغاريتمي linearithmic"، ولكن في العادة نقول "n log n".
</p>

<p>
	في الواقع، يُعدّ O(n log(n))‎ الحد الأدنى من الناحية النظرية لخوارزميات الترتيب التي تَعتمد على موازنة العناصر مع بعضها البعض. يعني ذلك أنه لا توجد خوارزمية ترتيب بالموازنة ذات ترتيب نموٍّ أفضلَ من n log n.
</p>

<p>
	ولكن كما سنرى في القسم التالي، هناك خوارزميات ترتيبٍ لا تعتمد على الموازنة وتستغرق زمنًا خطيًا.
</p>

<h2>
	خوارزمية الترتيب بالجذر Radix sort
</h2>

<p>
	أثناء الحملة الرئاسية في الولايات المتحدة الأمريكية لعام 2008، طُلِبَ من المرشح باراك أوباما Barack Obama تحليل أداء خوارزمية impromptu أثناء زيارته لمؤسسة جوجل Google. سأله الرئيس التنفيذي إريك شميدت Eric Schmidt مازحًا عن أكفأ طريقة لترتيب مليون عدد صحيح من نوع 32 بت. بدا أن أوباما كان قد اُخبرَ بذلك قبل اللقاء لأنه أجاب سريعًا "لا أظن أن خوارزمية ترتيب الفقاعات bubble sort ستكون الطريقة الأفضل". يُمكِنك مشاهدة <a href="http://thinkdast.com/obama" rel="external nofollow">فيديو لقاء أوباما مع إريك شميدت</a> لو أردت.
</p>

<p>
	كان أوباما على حق، <a href="https://wiki.hsoub.com/Algorithms/bubble_sort" rel="external">فخوارزمية ترتيب الفقاعات</a> bubble sort صحيحٌ أنها بسيطة وسهلة الفهم، لكنّها تستغرق زمنًا تربيعيًا، كما أن أداءها ليس جيدًا بالموازنة مع خوارزميات الترتيب التربيعية الأخرى.
</p>

<p>
	ربما <a href="https://wiki.hsoub.com/Algorithms/radix_sort" rel="external">خوارزمية الترتيب بالجذر</a> radix sort هي الإجابة التي كان ينتظرها شميدت، فهي خوارزمية ترتيب غير مبنيّة على الموازنة، كما أنها تَعمَل بنجاح عندما يكون حجم العناصر مقيّدًا كعدد صحيح من نوع 32 بت أو سلسلة نصية مُكوَّنة من 20 محرفًا.
</p>

<p>
	لكي نفهم طريقة عملها، لنتخيل أن لدينا مكدّسًا stack من البطاقات، وكل واحدة منها تحتوي على كلمة مُكوَّنة من ثلاثة أحرف. ها هي الطريقة التي يُمكِن أن نرتب بها تلك البطاقات:
</p>

<ol>
	<li>
		مرّ عبر البطاقات وقسمها إلى مجموعات بناءً على الحرف الأول، أي ينبغي أن تكون الكلمات البادئة بالحرف <code>a</code> ضمن مجموعة واحدة، يليها الكلمات التي تبدأ بحرف <code>b</code>، وهكذا.
	</li>
	<li>
		قسِّم كل مجموعة مرة أخرى بناءً على الحرف الثاني، بحيث تصبح الكلمات البادئة بالحرفين <code>aa</code> معًا، يليها الكلمات التي تبدأ بالحرفين <code>ab</code>، وهكذا. لن تكون كل المجموعات مملوءةً بالتأكيد، ولكن لا بأس بذلك.
	</li>
	<li>
		قسِّم كل مجموعة مرة أخرى بحسب الحرف الثالث.
	</li>
</ol>

<p>
	والآن، أصبحت كل مجموعة مُكوَّنة من عنصر واحد فقط، كما أصبحت المجموعات مُرتَّبةً ترتيبًا تصاعديًا. تَعرِض الصورة التالية مثالًا عن الكلمات المكوَّنة من ثلاثة أحرف.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="91078" href="https://academy.hsoub.com/uploads/monthly_2022_01/003Radix_Sort.PNG.577727209d1287b60321c76d1ec8c410.PNG" rel="" data-fileext="PNG"><img alt="003Radix_Sort.PNG" class="ipsImage ipsImage_thumbnailed" data-fileid="91078" data-unique="yqoaqsc1b" style="width: 450px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_01/003Radix_Sort.PNG.577727209d1287b60321c76d1ec8c410.PNG"></a>
</p>

<p>
	يَعرِض الصف الأول الكلمات غير المُرتَّبة، بينما يَعرِض الصف الثاني شكل المجموعات بعد اجتيازها للمرة الأولى. تبدأ كلمات كل مجموعة بنفس الحرف.
</p>

<p>
	بعد اجتياز الكلمات للمرة الثانية، تبدأ كلمات كل مجموعة بنفس الحرفين الأوليين، وبعد اجتيازها للمرة الثالثة، سيكون هنالك كلمة واحدة فقط في كل مجموعة، وستكون المجموعات مُرتَّبة.
</p>

<p>
	أثناء كل اجتياز، نمرّ عبر العناصر ونضيفها إلى المجموعات. يُعدّ كل اجتياز منها خطيًا طالما كانت تلك المجموعات تَسمَح بإضافة العناصر إليها بزمن خطي.
</p>

<p>
	تعتمد عدد مرات الاجتياز -التي سنطلق عليها w- على عرض الكلمات، ولكنه لا يعتمد على عدد الكلمات n، وبالتالي، يكون ترتيب النمو O(wn)‎ وهو خطي بالنسبة لقيمة n.
</p>

<p>
	تتوفَّر نسخ أخرى من خوارزمية الترتيب بالجذر، ويُمكِن تنفيذ كُلٍّ منها بطرق كثيرة. يُمكِنك قراءة المزيد عن <a href="http://thinkdast.com/radix" rel="external nofollow">خوارزمية الترتيب بالجذر</a>، كما يُمكِنك أن تحاول كتابة تنفيذ لها.
</p>

<h2>
	خوارزمية الترتيب بالكومة Heap sort
</h2>

<p>
	إلى جانب خوارزمية الترتيب بالجذر التي تُطبَّق عندما يكون حجم الأشياء المطلوب ترتيبها مقيّدًا، هنالك خوارزمية مُخصَّصة أخرى هي <a href="https://wiki.hsoub.com/Algorithms/heap_sort" rel="external">خوارزمية الترتيب بالكومة</a> المُقيّدة، والتي تُطبَّق عندما نعمل مع بياناتٍ ضخمةٍ جدًا ونحتاج إلى معرفة أكبر 10 أو أكبر عدد k حيث k قيمة أصغر بكثير من n.
</p>

<p>
	لنفترض مثلًا أننا نراقب خدمةً عبر الإنترنت تتعامل مع بلايين المعاملات يوميًا، وأننا نريد في نهاية كل يوم معرفة أكبر k من المعاملات (أو أبطأ أو أي معيار آخر). يُمكِننا مثلًا أن نُخزِّن جميع المعاملات، ثم نُرتِّبها في نهاية اليوم، ونختار أول k من المعاملات. سيستغرق ذلك زمنًا يتناسب مع n log n، وسيكون بطيئًا جدًا لأننا من المحتمل ألا نتمكَّن من ملاءمة بلايين المعاملات داخل ذاكرة برنامج واحد، وبالتالي، قد نضطرّ لاستخدام <a href="http://thinkdast.com/extsort" rel="external nofollow">خوارزمية ترتيب بذاكرة خارجية (خارج النواة)</a>.
</p>

<p>
	يُمكِننا بدلًا من ذلك أن نَستخدِم كومة مُقيدّة heap. إليك ما سنفعله في ما تبقى من هذه المقالة:
</p>

<ol>
	<li>
		سنشرح خوارزمية الترتيب بالكومة (غير المقيدة).
	</li>
	<li>
		ستنفذ الخوارزمية.
	</li>
	<li>
		سنشرح خوارزمية الترتيب بالكومة المُقيدة ونُحلِّلها.
	</li>
</ol>

<p>
	لكي تفهم ما يعنيه الترتيب بالكومة، عليك أولًا فهم ماهية الكومة. الكومة ببساطة عبارة عن هيكل بياني data structure مشابه لشجرة البحث الثنائية binary search tree. تتلخص الفروق بينهما في النقاط التالية:
</p>

<ul>
	<li>
		تتمتع أي عقدة <code>x</code> بشجرة البحث الثنائية بـ"خاصية BST" أي تكون جميع عقد الشجرة الفرعية subtree الموجود على يسار العقدة <code>x</code> أصغر من <code>x</code> كما تكون جميع عقد الشجرة الفرعية الموجودة على يمينها أكبر من <code>x</code>.
	</li>
	<li>
		تتمتع أي عقدة <code>x</code> ضمن الكومة بـ"خاصية الكومة" أي تكون جميع عقد الشجرتين الفرعيتين للعقدة <code>x</code> أكبر من <code>x</code>.
	</li>
	<li>
		تتشابه الكومة مع أشجار البحث الثنائية المتزنة من جهة أنه عندما تضيف العناصر إليها أو تحذفها منها، فإنها قد تقوم ببعض العمل الإضافي لضمان استمرارية اتزان الشجرة، وبالتالي، يُمكِن تنفيذها بكفاءة باستخدام مصفوفة من العناصر.
	</li>
</ul>

<p>
	دائمًا ما يكون جذر الكومة هو العنصر الأصغر، وبالتالي، يُمكِننا أن نعثر عليه بزمن ثابت. تستغرق إضافة العناصر وحذفها من الكومة زمنًا يتناسب مع طول الشجرة h، ولأن الكومة دائمًا ما تكون متزنة، فإن h يتناسب مع log n. يُمكِنك قراءة المزيد عن <a href="https://wiki.hsoub.com/Algorithms/heaps" rel="external">الكومة</a> لو أردت.
</p>

<p>
	تُنفِّذ جافا الصنف <code>PriorityQueue</code> باستخدام كومة. يحتوي ذلك الصنف على التوابع المُعرَّفة في الواجهة <code>Queue</code> ومن بينها التابعان <code>offer</code> و <code>poll</code> اللّذان نلخص عملهما فيما يلي:
</p>

<ul>
	<li>
		<code>offer</code>: يضيف عنصرًا إلى الرتل queue، ويُحدِّث الكومة بحيث يَضمَن استيفاء "خاصية الكومة" لجميع العقد. لاحِظ أنه يستغرق زمنًا يتناسب مع log n.
	</li>
	<li>
		<code>poll</code>: يَحذِف أصغر عنصر من الرتل من الجذر ويُحدِّث الكومة. يستغرق أيضًا زمنًا يتناسب مع log n.
	</li>
</ul>

<p>
	إذا كان لديك كائن من النوع <code>PriorityQueue</code>، تستطيع بسهولة ترتيب تجميعة عناصر طولها n على النحو التالي:
</p>

<ol>
	<li>
		أضف جميع عناصر التجميعة إلى كائن الصنف <code>PriorityQueue</code> باستخدام التابع <code>offer</code>.
	</li>
	<li>
		احذف العناصر من الرتل باستخدام التابع <code>poll</code> وأضفها إلى قائمة من النوع <code>List</code>.
	</li>
</ol>

<p>
	نظرًا لأن التابع <code>poll</code> يعيد أصغر عنصر مُتبقٍّ في الرتل، فإن العناصر تُضاف إلى القائمة مُرتَّبةً تصاعديًّا. يُطلَق على هذا النوع من الترتيب اسم <a href="https://wiki.hsoub.com/Algorithms/heap_sort" rel="external">الترتيب بالكومة</a>.
</p>

<p>
	تستغرق إضافة عددٍ قدره n من العناصر إلى رتلٍ زمنًا يتناسب مع n log n، ونفس الأمر ينطبق على حذف عددٍ قدره n من العناصر منه، وبالتالي، تنتمي خوارزمية الترتيب بالكومة إلى المجموعة O(n log(n))‎.
</p>

<p>
	ستجد ضمن الملف <code>ListSorter.java</code> تعريفًا مبدئيًا لتابع اسمه <code>heapSort</code>. أكمله ونفِّذ الأمر <code>ant ListSorterTest</code> لكي تتأكّد من أنه يَعمَل بشكل صحيح.
</p>

<h2>
	الكومة المُقيدّة Bounded heap
</h2>

<p>
	تَعمَل الكومة المقيدة كأي كومة عادية، ولكنها تكون مقيدة بعدد k من العناصر. إذا كان لديك عدد n من العناصر، يُمكِنك أن تحتفظ فقط بأكبر عدد k من العناصر باتباع التالي:
</p>

<p>
	ستكون الكومة فارغة مبدئيًا، وعليك أن تُنفِّذ التالي لكل عنصر <code>x</code>:
</p>

<ul>
	<li>
		التفريع الأول: إذا لم تكن الكومة ممتلئةً، أضف <code>x</code> إلى الكومة.
	</li>
	<li>
		التفريع الثاني: إذا كانت الكومة ممتلئةً، وازن قيمة <code>x</code> مع أصغر عنصر في الكومة. إذا كانت قيمة <code>x</code> أصغر، فلا يُمكِن أن تكون ضمن أكبر عدد k من العناصر، ولذلك، يُمكِنك أن تتجاهلها.
	</li>
	<li>
		التفريع الثالث: إذا كانت الكومة ممتلئةً، وكانت قيمة <code>x</code> أكبر من قيمة أصغر عنصر بالكومة، احذف أصغر عنصر، وأضف <code>x</code> مكانه.
	</li>
</ul>

<p>
	بوجود أصغر عنصر أعلى الكومة، يُمكِننا الاحتفاظ بأكبر عدد k من العناصر. لنحلل الآن أداء هذه الخوارزمية. إننا ننفِّذ ما يلي لكل عنصر:
</p>

<ul>
	<li>
		التفريع الأول: تستغرق إضافة عنصر إلى الكومة زمنًا يتناسب مع O(log k)‎.
	</li>
	<li>
		التفريع الثاني: يستغرق العثور على أصغر عنصر بالكومة زمنًا يتناسب مع O(1)‎.
	</li>
	<li>
		التفريع الثالث: يستغرق حذف أصغر عنصر زمنًا يتناسب مع O(log k)‎، كما أن إضافة <code>x</code> تستغرق نفس مقدار الزمن.
	</li>
</ul>

<p>
	في الحالة الأسوأ، تكون العناصر مُرتَّبة تصاعديًا، وبالتالي، نُنفِّذ التفريع الثالث دائمًا، ويكون الزمن الإجمالي لمعالجة عدد n من العناصر هو O(n log K)‎ أي خطّيّ مع n.
</p>

<p>
	ستجد في الملف <code>ListSorter.java</code> تعريفًا مبدئيًا لتابع اسمه <code>topK</code>. يَستقبِل هذا التابع قائمةً من النوع <code>List</code> وكائنًا من النوع <code>Comparator</code> وعددًا صحيحًا <code>k</code>، ويعيد أكبر عدد k من عناصر القائمة بترتيب تصاعدي. أكمل متن التابع ثم نفِّذ الأمر <code>ant ListSorterTest</code> لكي تتأكّد من أنه يَعمَل بشكل صحيح.
</p>

<h2>
	تعقيد المساحة Space complexity
</h2>

<p>
	تحدثنا حتى الآن عن تحليل زمن التنفيذ فقط، ولكن بالنسبة لكثير من الخوارزميات، ينبغي أن نُولِي للمساحة التي تتطلّبها الخوارزمية بعض الاهتمام. على سبيل المثال، تحتاج خوارزمية الترتيب بالدمج merge sort إلى إنشاء نسخ من البيانات، وقد كانت مساحة الذاكرة الإجمالية التي تطلّبها تنفيذنا لتلك الخوارزمية هو O(n log n)‎. في الواقع، يُمكِننا أن نُخفِّض ذلك إلى O(n)‎ إذا نفذنا نفس الخوارزمية بطريقة أفضل.
</p>

<p>
	في المقابل، لا تنسخ خوارزمية الترتيب بالإدراج insertion sort البيانات لأنها تُرتِّب العناصر في أماكنها، وتَستخدِم متغيراتٍ مؤقتةً لموازنة عنصرين في كل مرة، كما تَستخدِم عددًا قليلًا من المتغيرات المحلية local الأخرى، ولكن المساحة التي تتطلبها لا تعتمد على n.
</p>

<p>
	تُنشِئ نسختنا من خوارزمية الترتيب بالكومة كائنًا جديدًا من النوع <code>PriorityQueue</code>، وتُخزِّن فيه العناصر، أي أن المساحة المطلوبة تنتمي إلى المجموعة O(n)‎، وإذا سمحنا بترتيب عناصر القائمة في نفس المكان، يُمكِننا أن نُخفِّض المساحة المطلوبة لتنفيذ خوارزمية الترتيب بالكومة إلى المجموعة O(1)‎.
</p>

<p>
	من مميزات النسخة التي نفَّذناها من تلك الخوارزمية هي أنها تحتاج فقط إلى مساحة تتناسب مع k (أي عدد العناصر المطلوب الاحتفاظ بها)، وعادةً ما تكون k أصغر بكثير من قيمة n.
</p>

<p>
	يميل مطورو البرمجيات إلى التركيز على زمن التشغيل وإهمال حيز الذاكرة المطلوب، وهذا في الحقيقة، مناسب لكثير من التطبيقات، ولكن عند التعامل مع بيانات ضخمة، تكون المساحة المطلوبة بنفس القدر من الأهمية إن لم تكن أهم، كما في الحالات التالية مثلًا:
</p>

<ol>
	<li>
		إذا لم تكن مساحة ذاكرة البرنامج ملائمةً للبيانات، عادةً ما يزداد زمن التشغيل إلى حد كبير وقد لا يَعمَل البرنامج من الأساس. إذا اخترت خوارزمية تتطلّب حيزًا أقل من الذاكرة، وتَسمَح بملائمة المعالجة ضمن الذاكرة، فقد يَعمَل البرنامج بسرعةٍ أعلى بكثير. بالإضافة إلى ذلك، تَستغِل البرامج التي تتطلَّب مساحة ذاكرة أقل <a href="https://academy.hsoub.com/certificates/comptia/%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B1%D9%83%D8%B2%D9%8A%D8%A9-r58/" rel="">الذاكرة المؤقتة لوحدة المعالجة المركزية CPU caches</a> بشكل أفضل وتَعمَل بسرعة أكبر.
	</li>
	<li>
		في الخوادم التي تُشغِّل برامج كثيرة في الوقت نفسه، إذا أمكنك تقليل المساحة التي يتطلبها كل برنامج، فقد تتمكّن من تشغيل برامج أكثر على نفس الخادم، مما يُقلل من تكلفة الطاقة والعتاد المطلوبة.
	</li>
</ol>

<p>
	كانت هذه بعض الأسباب التي توضح أهمية الاطلاع على متطلبات الخوارزميات المتعلقة بالذاكرة.
</p>

<p>
	يمكنك التوسع أكثر في الموضوع بقراءة توثيق <a href="https://wiki.hsoub.com/Algorithms/Sorting_Algorithms" rel="external">خوارزميات الترتيب</a> في توثيق موسوعة حسوب.
</p>

<p>
	ترجمة -بتصرّف- للفصل <a href="https://greenteapress.com/thinkdast/html/thinkdast018.html" rel="external nofollow">Chapter 17: Sorting</a> من كتاب <a href="https://greenteapress.com/thinkdast/html/index.html" rel="external nofollow">Think Data Structures: Algorithms and Information Retrieval in Java</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/advanced/%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%A7%D9%84%D8%AB%D9%86%D8%A7%D8%A6%D9%8A-boolean-search-%D9%88%D8%AF%D9%85%D8%AC-%D9%86%D8%AA%D8%A7%D8%A6%D8%AC-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D9%88%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8%D9%87%D8%A7-r1455/" rel="">البحث الثنائي Boolean search ودمج نتائج البحث وترتيبها</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B9%D9%82%D9%8A%D8%AF-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-algorithms-complexity-r1284/" rel="">تعقيد الخوارزميات Algorithms Complexity</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1345/" rel="">مدخل إلى تحليل الخوارزميات</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1456</guid><pubDate>Wed, 16 Mar 2022 16:05:00 +0000</pubDate></item></channel></rss>
