<?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/page/3/?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>&#x645;&#x634;&#x631;&#x648;&#x639; &#x628;&#x646;&#x627;&#x621; &#x644;&#x63A;&#x629; &#x628;&#x631;&#x645;&#x62C;&#x629; &#x62E;&#x627;&#x635;&#x629;</title><link>https://academy.hsoub.com/programming/advanced/%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%A8%D9%86%D8%A7%D8%A1-%D9%84%D8%BA%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AE%D8%A7%D8%B5%D8%A9-r1305/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/6129cfaca5c82_-----.png.f1d64588123f83e275ce7a4704e7d20d.png" /></p>

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

	<p>
		المقيِّم evaluator الذي يحدِّد معاني التعبيرات في لغة البرمجة هو برنامج بذاته.
	</p>

	<p>
		ـــ هال أبيلسون Hal Abelson وجيرالد سوسْمَن Gerald Sussman، هياكل برامج الحاسوب وتفسيرها.
	</p>
</blockquote>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="75596" href="https://academy.hsoub.com/uploads/monthly_2021_08/chapter-12.jpg.b67063d5c2c2d83637914b33292ff32e.jpg" rel=""><img alt="chapter-12.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="75596" data-unique="xcsw5ba0y" src="https://academy.hsoub.com/uploads/monthly_2021_08/chapter-12.jpg.b67063d5c2c2d83637914b33292ff32e.jpg"></a>
</p>

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

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

<h2>
	التحليل
</h2>

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

<p>
	ستكون لغتنا ذات بنية لغوية بسيطة وموحدة، حيث سيكون كل شيء في Egg تعبيرًا، وقد يكون التعبير expresion اسم رابطة binding أو عددًا أو سلسلةً نصيةً string أو حتى تطبيقًا، كما تُستخدَم التطبيقات لاستدعاءات الدوال وللبنى اللغوية مثل <code>if</code> أو <code>while</code>.
</p>

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

<p>
	تُكتَب التطبيقات بالطريقة نفسها المكتوبة بها في جافاسكربت، وذلك بوضع أقواس بعد التعبير، ويمكنها امتلاك أيّ عدد من الوسائط arguments بين هذين القوسين مفصول بين كل منها بفاصلة أجنبية <code>,</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_10" style="">
<span class="kwd">do</span><span class="pun">(</span><span class="pln">define</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">),</span><span class="pln">
   </span><span class="kwd">if</span><span class="pun">(&gt;(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">),</span><span class="pln">
      print</span><span class="pun">(</span><span class="str">"large"</span><span class="pun">),</span><span class="pln">
      print</span><span class="pun">(</span><span class="str">"small"</span><span class="pun">)))</span></pre>

<p>
	يعني توحيد لغة Egg أنّ الأشياء التي تُرى على أساس عوامل operators في جافاسكربت -مثل <code>‎&gt;‎</code>- هي رابطات عادية في هذه اللغة، وتُطبَّق مثل أيّ دالة أخرى، كما نحتاج إلى بنية <code>do</code> للتعبير عن تنفيذ عدة أشياء في تسلسل واحد، وذلك لعدم وجود مبدأ الكتل blocks في البنية التركيبية للغة.
</p>

<p>
	يتكون هيكل البيانات الذي سيستخدمه المحلل لوصف برنامج ما من كائنات تعابير، لكل منها خاصية <code>type</code> توضِّح نوع التعبير وخصائص أخرى تصف محتواه.
</p>

<p>
	تمثِّل التعابير التي من النوع <code>"value"</code> سلاسلًا نصيةً مجردةً أو أعدادًا، وتحتوي خاصية <code>value</code> لها على السلسلة النصية أو قيمة العدد الذي تمثله؛ أما التعابير التي من نوع <code>"word"</code> فتُستخدَم للمعرِّفات -أي الأسماء-، فمثل تلك الكائنات لها خاصية <code>name</code> تحمل اسم المعرِّف على أساس سلسلة نصية، وأخيرًا تمثِّل تعابير <code>"apply"</code> التطبيقات، إذ تملك خاصية <code>operator</code> التي تشير مرجعيًا إلى تعبير يُطَبَّق، بالإضافة إلى خاصية <code>args</code> التي تحمل مصفوفةً من تعابير الوسائط argument expressions.
</p>

<p>
	سيُمثَّل جزء <code>‎&gt;(x, 5)‎</code> من البرنامج السابق كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_12" style="">
<span class="pun">{</span><span class="pln">
  type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"apply"</span><span class="pun">,</span><span class="pln">
  </span><span class="kwd">operator</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"word"</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"&gt;"</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">
    </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"word"</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"x"</span><span class="pun">},</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"value"</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">}</span><span class="pln">
  </span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="75595" href="https://academy.hsoub.com/uploads/monthly_2021_08/structure-of-syntax-tree.png.a6ba26bc10ef56da5a76621724a7646a.png" rel=""><img alt="structure-of-syntax-tree.png" class="ipsImage ipsImage_thumbnailed" data-fileid="75595" data-unique="xmr2l3kg2" src="https://academy.hsoub.com/uploads/monthly_2021_08/structure-of-syntax-tree.png.a6ba26bc10ef56da5a76621724a7646a.png"></a>
</p>

<p>
	هذا على عكس المحلل الذي كتبناه لصيغة ملف الإعدادات في مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regular-expressions-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1278/" rel="">التعابير النمطية Regular Expressions في جافاسكريبت</a>، والذي كانت بنيته بسيطةً نوعًا ما، إذ يُقسِّم الدخل إلى أسطر ويعالج هذه الأسطر واحدًا واحدًا، حيث فلم يكن ممكنًا للسطر الواحد إلا بضع صور بسيطة يمكنه أن يكون عليها؛ أما هنا فيجب إيجاد طريقة أخرى، فالتعابير ليست مقسمةً إلى أسطر، كما تملك بنيةً تعاوديةً recursive، وتحتوي تعابير التطبيقات على تعابير أخرى.
</p>

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

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

<p>
	يكون أول جزء في المحلل كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_14" style="">
<span class="kwd">function</span><span class="pln"> parseExpression</span><span class="pun">(</span><span class="pln">program</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  program </span><span class="pun">=</span><span class="pln"> skipSpace</span><span class="pun">(</span><span class="pln">program</span><span class="pun">);</span><span class="pln">
  let match</span><span class="pun">,</span><span class="pln"> expr</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">match </span><span class="pun">=</span><span class="pln"> </span><span class="str">/^"([^"]*)"/</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">program</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    expr </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"value"</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> match</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">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">match </span><span class="pun">=</span><span class="pln"> </span><span class="str">/^\d+\b/</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">program</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    expr </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"value"</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Number</span><span class="pun">(</span><span class="pln">match</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="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">match </span><span class="pun">=</span><span class="pln"> </span><span class="str">/^[^\s(),#"]+/</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">program</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    expr </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"word"</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> match</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="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SyntaxError</span><span class="pun">(</span><span class="str">"Unexpected syntax: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> program</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"> parseApply</span><span class="pun">(</span><span class="pln">expr</span><span class="pun">,</span><span class="pln"> program</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="pln">match</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">length</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"> skipSpace</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">
  let first </span><span class="pun">=</span><span class="pln"> string</span><span class="pun">.</span><span class="pln">search</span><span class="pun">(</span><span class="str">/\S/</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">first </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="kwd">return</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> string</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="pln">first</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب قص المسافات الفارغة من بداية السلسلة النصية للبرنامج بما أنّ لغة Egg التي نكتبها تشبه جافاسكربت في كونها تسمح لأيّ عدد من المسافات الفارغة بين عناصرها، وهنا يأتي دور دالة <code>skipSpace</code>.
</p>

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

<p>
	نحن نستخدم <code>SyntaxError</code> بدلًا من <code>Error</code> على أساس باني اعتراضات exception، وهو نوع قياسي آخر للأخطاء لأنه أكثر تحديدًا، وهو أيضًا نوع الخطأ الذي يرمى عند محاولة تشغيل برنامج JavaScript غير صالح، ثم نقص الجزء المطابق من سلسلة البرنامج النصية ونمرره مع كائن التعبير إلى <code>parseApply</code> الذي ينظر هل التعبير تطبيق أم لا، فإذا كان تطبيقًا، فسيحلَِل قائمةً من الوسائط محصورةً بين قوسين.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_16" style="">
<span class="kwd">function</span><span class="pln"> parseApply</span><span class="pun">(</span><span class="pln">expr</span><span class="pun">,</span><span class="pln"> program</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  program </span><span class="pun">=</span><span class="pln"> skipSpace</span><span class="pun">(</span><span class="pln">program</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">program</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="str">"("</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">expr</span><span class="pun">:</span><span class="pln"> expr</span><span class="pun">,</span><span class="pln"> rest</span><span class="pun">:</span><span class="pln"> program</span><span class="pun">};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  program </span><span class="pun">=</span><span class="pln"> skipSpace</span><span class="pun">(</span><span class="pln">program</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">
  expr </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"apply"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">operator</span><span class="pun">:</span><span class="pln"> expr</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">
  </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">program</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="str">")"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let arg </span><span class="pun">=</span><span class="pln"> parseExpression</span><span class="pun">(</span><span class="pln">program</span><span class="pun">);</span><span class="pln">
    expr</span><span class="pun">.</span><span class="pln">args</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">arg</span><span class="pun">.</span><span class="pln">expr</span><span class="pun">);</span><span class="pln">
    program </span><span class="pun">=</span><span class="pln"> skipSpace</span><span class="pun">(</span><span class="pln">arg</span><span class="pun">.</span><span class="pln">rest</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">program</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="str">","</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      program </span><span class="pun">=</span><span class="pln"> skipSpace</span><span class="pun">(</span><span class="pln">program</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">
    </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">program</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="str">")"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SyntaxError</span><span class="pun">(</span><span class="str">"Expected ',' or ')'"</span><span class="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"> parseApply</span><span class="pun">(</span><span class="pln">expr</span><span class="pun">,</span><span class="pln"> program</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">
</span><span class="pun">}</span></pre>

<p>
	إذا كان المحرف التالي في البرنامج ليس قوسًا بادئًا <code>)</code> فلن يكون هذا تطبيقًا، وتُعيد <code>parseApply</code> التعبير المعطى لها، وإلا فستتخطى القوس البادئ وتنشئ كائن شجرة بُنى syntax tree object لتعبير التطبيق ذاك، ثم تستدعي <code>parseExpression</code> تعاوديًا لتحلِّل كل وسيط حتى تجد القوس الغالق، كما يكون التعاود هنا غير مباشر من خلال استدعاء <code>parseApply</code> لدالة <code>parseExpression</code> والعكس، ولأن تعبير التطبيق يمكن تطبيقه كما في <code>multiplier(2)(1)‎</code>، فيجب أن تستدعي <code>parseApply</code> نفسها مرةً أخرى لتنظر فيما إذا كان يوجد زوج آخر من الأقواس أم لا، وذلك بعد تحليل تطبيق ما.
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_18" style="">
<span class="kwd">function</span><span class="pln"> parse</span><span class="pun">(</span><span class="pln">program</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let </span><span class="pun">{</span><span class="pln">expr</span><span class="pun">,</span><span class="pln"> rest</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> parseExpression</span><span class="pun">(</span><span class="pln">program</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">skipSpace</span><span class="pun">(</span><span class="pln">rest</span><span class="pun">).</span><span class="pln">length </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">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SyntaxError</span><span class="pun">(</span><span class="str">"Unexpected text after program"</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"> expr</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">parse</span><span class="pun">(</span><span class="str">"+(a, 10)"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → {type: "apply",</span><span class="pln">
</span><span class="com">//    operator: {type: "word", name: "+"},</span><span class="pln">
</span><span class="com">//    args: [{type: "word", name: "a"},</span><span class="pln">
</span><span class="com">//           {type: "value", value: 10}]}</span></pre>

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

<h2>
	المقيم
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_20" style="">
<span class="kwd">const</span><span class="pln"> specialForms </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">create</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> evaluate</span><span class="pun">(</span><span class="pln">expr</span><span class="pun">,</span><span class="pln"> scope</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">expr</span><span class="pun">.</span><span class="pln">type </span><span class="pun">==</span><span class="pln"> </span><span class="str">"value"</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"> expr</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="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">expr</span><span class="pun">.</span><span class="pln">type </span><span class="pun">==</span><span class="pln"> </span><span class="str">"word"</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">expr</span><span class="pun">.</span><span class="pln">name in scope</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"> scope</span><span class="pun">[</span><span class="pln">expr</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="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ReferenceError</span><span class="pun">(</span><span class="pln">
        </span><span class="pun">`</span><span class="typ">Undefined</span><span class="pln"> binding</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">expr</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="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">expr</span><span class="pun">.</span><span class="pln">type </span><span class="pun">==</span><span class="pln"> </span><span class="str">"apply"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let </span><span class="pun">{</span><span class="kwd">operator</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"> expr</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">operator</span><span class="pun">.</span><span class="pln">type </span><span class="pun">==</span><span class="pln"> </span><span class="str">"word"</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln">
        </span><span class="kwd">operator</span><span class="pun">.</span><span class="pln">name in specialForms</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"> specialForms</span><span class="pun">[</span><span class="kwd">operator</span><span class="pun">.</span><span class="pln">name</span><span class="pun">](</span><span class="pln">expr</span><span class="pun">.</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> scope</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">
      let op </span><span class="pun">=</span><span class="pln"> evaluate</span><span class="pun">(</span><span class="kwd">operator</span><span class="pun">,</span><span class="pln"> scope</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">typeof</span><span class="pln"> op </span><span class="pun">==</span><span class="pln"> </span><span class="str">"function"</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"> op</span><span class="pun">(...</span><span class="pln">args</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">arg </span><span class="pun">=&gt;</span><span class="pln"> evaluate</span><span class="pun">(</span><span class="pln">arg</span><span class="pun">,</span><span class="pln"> scope</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">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TypeError</span><span class="pun">(</span><span class="str">"Applying a non-function."</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><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>
	يحتوي المقيِّم على شيفرة لكل نوع من أنواع التعابير، ويُخرج لنا تعبير القيمة مصنفة النوع literal value expression قيمته، كما في حالة التعبير <code>100</code> الذي يقيِّم إلى العدد 100 فقط؛ أما في حالة الرابطة، فيجب التحقق مما إذا كانت معرَّفةً على الحقيقة في النطاق أم لا، وإن كانت فسنجلب قيمتها.
</p>

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

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

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

<h2>
	الصيغ الخاصة
</h2>

<p>
	يُستخدَم كائن الصيغ الخاصة <code>specialForms</code> لتعريف البُنى الخاصة في لغة Egg، حيث يربط الكلمات بالدوال التي تقيِّم مثل تلك الصيغ، ولنضف <code>if</code> بما أنه فارغ الآن:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_22" style="">
<span class="pln">specialForms</span><span class="pun">.</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> scope</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">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">args</span><span class="pun">.</span><span class="pln">length </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">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SyntaxError</span><span class="pun">(</span><span class="str">"Wrong number of args to if"</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">evaluate</span><span class="pun">(</span><span class="pln">args</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln"> scope</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!==</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">
    </span><span class="kwd">return</span><span class="pln"> evaluate</span><span class="pun">(</span><span class="pln">args</span><span class="pun">[</span><span class="lit">1</span><span class="pun">],</span><span class="pln"> scope</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"> evaluate</span><span class="pun">(</span><span class="pln">args</span><span class="pun">[</span><span class="lit">2</span><span class="pun">],</span><span class="pln"> scope</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	تتوقع بنية <code>if</code> ثلاثة وسائط بالضبط، وستقيّم الأول منها، فإذا لم تكن النتيجة هي القيمة <code>false</code> فستقيِّم الثاني، وإلا فالثالث هو الذي يُقيَّم، وتُعَد صيغة <code>if</code> هذه أقرب إلى عامل <code>‎?:‎</code> الثلاثي في جافاسكربت منها إلى عامل <code>if</code> في جافاسكربت أيضًا، فهو تعبير وليس تعليمة، ويُنتِج قيمةً تكون نتيجة الوسيط الثاني أو الثالث.
</p>

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

<p>
	السبب الذي يجعلنا في حاجة إلى تمثيل <code>if</code> على أساس صيغة خاصة بدلًا من دالة عادية، هو أن جميع وسائط الدوال تُقيَّم قبل استدعاء الدالة، بينما يجب ألا تقيِّم <code>if</code> إلا وسيطها الثاني أو الثالث بناءً على قيمة الوسيط الأول.
</p>

<p>
	صيغة <code>while</code> شبيهة بهذا، فبما أن <code>undefined</code> غير موجودة في لغة Egg، فسنُعيد <code>false</code> لعدم وجود نتيجة أفضل وأكثر فائدة، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_24" style="">
<span class="pln">specialForms</span><span class="pun">.</span><span class="kwd">while</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> scope</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">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">args</span><span class="pun">.</span><span class="pln">length </span><span class="pun">!=</span><span class="pln"> </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">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SyntaxError</span><span class="pun">(</span><span class="str">"Wrong number of args to while"</span><span class="pun">);</span><span class="pln">
  </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">evaluate</span><span class="pun">(</span><span class="pln">args</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln"> scope</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!==</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">
    evaluate</span><span class="pun">(</span><span class="pln">args</span><span class="pun">[</span><span class="lit">1</span><span class="pun">],</span><span class="pln"> scope</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="kwd">false</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_26" style="">
<span class="pln">specialForms</span><span class="pun">.</span><span class="kwd">do</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> scope</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">
  let value </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">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let arg of args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    value </span><span class="pun">=</span><span class="pln"> evaluate</span><span class="pun">(</span><span class="pln">arg</span><span class="pun">,</span><span class="pln"> scope</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</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_28" style="">
<span class="pln">specialForms</span><span class="pun">.</span><span class="pln">define </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> scope</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">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">args</span><span class="pun">.</span><span class="pln">length </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"> args</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">type </span><span class="pun">!=</span><span class="pln"> </span><span class="str">"word"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SyntaxError</span><span class="pun">(</span><span class="str">"Incorrect use of define"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  let value </span><span class="pun">=</span><span class="pln"> evaluate</span><span class="pun">(</span><span class="pln">args</span><span class="pun">[</span><span class="lit">1</span><span class="pun">],</span><span class="pln"> scope</span><span class="pun">);</span><span class="pln">
  scope</span><span class="pun">[</span><span class="pln">args</span><span class="pun">[</span><span class="lit">0</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"> value</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></pre>

<h2>
	البيئة
</h2>

<p>
	يكون النطاق الذي تقبَله <code>evaluate</code> كائنًا له خصائص تتوافق أسماؤها مع أسماء الرابطات، كما تتوافق قيمها مع القيم التي ترتبط بهذه الرابطات، وسنعرِّف كائنًا ليمثل النطاق العام.
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_30" style="">
<span class="kwd">const</span><span class="pln"> topScope </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">create</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">);</span><span class="pln">

topScope</span><span class="pun">.</span><span class="kwd">true</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
topScope</span><span class="pun">.</span><span class="kwd">false</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span></pre>

<p>
	نستطيع الآن تقييم تعبير بسيط يرفض قيمةً بوليانيةً.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_32" style="">
<span class="pln">let prog </span><span class="pun">=</span><span class="pln"> parse</span><span class="pun">(`</span><span class="kwd">if</span><span class="pun">(</span><span class="kwd">true</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">true</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">evaluate</span><span class="pun">(</span><span class="pln">prog</span><span class="pun">,</span><span class="pln"> topScope</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_34" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let op of </span><span class="pun">[</span><span class="str">"+"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"-"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"*"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"/"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"=="</span><span class="pun">,</span><span class="pln"> </span><span class="str">"&lt;"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"&gt;"</span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  topScope</span><span class="pun">[</span><span class="pln">op</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Function</span><span class="pun">(</span><span class="str">"a, b"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">`</span><span class="kwd">return</span><span class="pln"> a $</span><span class="pun">{</span><span class="pln">op</span><span class="pun">}</span><span class="pln"> b</span><span class="pun">;`);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا كانت لدينا طريقة لإخراج القيم، فسيكون ذلك مفيدًا لنا بلا شك، لذا سنغلِّف <code>console.log</code> في دالة ونسميها <code>print</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_36" style="">
<span class="pln">topScope</span><span class="pun">.</span><span class="pln">print </span><span class="pun">=</span><span class="pln"> value </span><span class="pun">=&gt;</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">value</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></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_38" style="">
<span class="kwd">function</span><span class="pln"> run</span><span class="pun">(</span><span class="pln">program</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"> evaluate</span><span class="pun">(</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">program</span><span class="pun">),</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">create</span><span class="pun">(</span><span class="pln">topScope</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنستخدم سلاسل كائن النموذج الأولي لتمثيل النطاقات المتشعِّبة nested scopes كي يتمكن البرنامج من إضافة روابط إلى نطاقه المحلي دون تغيير النطاق الأعلى top-level scope.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_40" style="">
<span class="pln">run</span><span class="pun">(`</span><span class="pln">
</span><span class="kwd">do</span><span class="pun">(</span><span class="pln">define</span><span class="pun">(</span><span class="pln">total</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">),</span><span class="pln">
   define</span><span class="pun">(</span><span class="pln">count</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">while</span><span class="pun">(&lt;(</span><span class="pln">count</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">),</span><span class="pln">
         </span><span class="kwd">do</span><span class="pun">(</span><span class="pln">define</span><span class="pun">(</span><span class="pln">total</span><span class="pun">,</span><span class="pln"> </span><span class="pun">+(</span><span class="pln">total</span><span class="pun">,</span><span class="pln"> count</span><span class="pun">)),</span><span class="pln">
            define</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">count</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)))),</span><span class="pln">
   print</span><span class="pun">(</span><span class="pln">total</span><span class="pun">))</span><span class="pln">
</span><span class="pun">`);</span><span class="pln">
</span><span class="com">// → 55</span></pre>

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

<h2>
	الدوال
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_42" style="">
<span class="pln">specialForms</span><span class="pun">.</span><span class="pln">fun </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> scope</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">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">args</span><span class="pun">.</span><span class="pln">length</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SyntaxError</span><span class="pun">(</span><span class="str">"Functions need a body"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  let body </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">[</span><span class="pln">args</span><span class="pun">.</span><span class="pln">length </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">];</span><span class="pln">
  let params </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">length </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">).</span><span class="pln">map</span><span class="pun">(</span><span class="pln">expr </span><span class="pun">=&gt;</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">expr</span><span class="pun">.</span><span class="pln">type </span><span class="pun">!=</span><span class="pln"> </span><span class="str">"word"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SyntaxError</span><span class="pun">(</span><span class="str">"Parameter names must be words"</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"> expr</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="kwd">return</span><span class="pln"> </span><span class="kwd">function</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">arguments</span><span class="pun">.</span><span class="pln">length </span><span class="pun">!=</span><span class="pln"> params</span><span class="pun">.</span><span class="pln">length</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TypeError</span><span class="pun">(</span><span class="str">"Wrong number of arguments"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    let localScope </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">create</span><span class="pun">(</span><span class="pln">scope</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">let 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"> arguments</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="pun">{</span><span class="pln">
      localScope</span><span class="pun">[</span><span class="pln">params</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"> arguments</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">return</span><span class="pln"> evaluate</span><span class="pun">(</span><span class="pln">body</span><span class="pun">,</span><span class="pln"> localScope</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	تحصل الدوال في لغة Egg على نطاقاتها المحلية الخاصة بها، إذ تُنشِئ الدالة المنتَجة بواسطة صيغة <code>fun</code> هذا النطاق المحلي، وتضيف الرابطات الوسيطة إليه، ثم تقيِّم متن الدالة في هذا النطاق وتُعيد النتيجة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_44" style="">
<span class="pln">run</span><span class="pun">(`</span><span class="pln">
</span><span class="kwd">do</span><span class="pun">(</span><span class="pln">define</span><span class="pun">(</span><span class="pln">plusOne</span><span class="pun">,</span><span class="pln"> fun</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">a</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">))),</span><span class="pln">
   print</span><span class="pun">(</span><span class="pln">plusOne</span><span class="pun">(</span><span class="lit">10</span><span class="pun">)))</span><span class="pln">
</span><span class="pun">`);</span><span class="pln">
</span><span class="com">// → 11</span><span class="pln">

run</span><span class="pun">(`</span><span class="pln">
</span><span class="kwd">do</span><span class="pun">(</span><span class="pln">define</span><span class="pun">(</span><span class="pln">pow</span><span class="pun">,</span><span class="pln"> fun</span><span class="pun">(</span><span class="pln">base</span><span class="pun">,</span><span class="pln"> exp</span><span class="pun">,</span><span class="pln">
     </span><span class="kwd">if</span><span class="pun">(==(</span><span class="pln">exp</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</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">base</span><span class="pun">,</span><span class="pln"> pow</span><span class="pun">(</span><span class="pln">base</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-(</span><span class="pln">exp</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)))))),</span><span class="pln">
   print</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"> </span><span class="lit">10</span><span class="pun">)))</span><span class="pln">
</span><span class="pun">`);</span><span class="pln">
</span><span class="com">// → 1024</span></pre>

<h2>
	التصريف
</h2>

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

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

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

<h2>
	الاحتيال
</h2>

<p>
	لعلك لاحظت حين عرَّفنا <code>if</code> و<code>while</code> على أنهما ليستا سوى تغليف بسيط نوعًا ما حول نظيرتيهما في جافاسكربت، وبالمثل، فإنّ القيم في Egg ما هي إلا قيم جافاسكربت العادية.
</p>

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

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_46" style="">
<span class="pln">behavior walk
  perform when
    destination ahead
  actions
    move left</span><span class="pun">-</span><span class="pln">foot
    move right</span><span class="pun">-</span><span class="pln">foot

behavior attack
  perform when
    </span><span class="typ">Godzilla</span><span class="pln"> in</span><span class="pun">-</span><span class="pln">view
  actions
    fire laser</span><span class="pun">-</span><span class="pln">eyes
    launch arm</span><span class="pun">-</span><span class="pln">rockets</span></pre>

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

<h2>
	تدريبات
</h2>

<h3>
	المصفوفات
</h3>

<p>
	اجعل لغة Egg تدعم المصفوفات من خلال إضافة الدوال الثلاثة التالية إلى النطاق العلوي top scope:
</p>

<ul>
<li>
		<code>array(...values)‎</code> لبناء مصفوفة تحتوي على قيم وسيطة.
	</li>
	<li>
		<code>length(array)‎</code> للحصول على طول مصفوفة ما.
	</li>
	<li>
		<code>element(array, n)‎</code> لجلب العنصر رقم n من مصفوفة ما.
	</li>
</ul>
<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_48" style="">
<span class="com">// عدِّل هذه التعريفات...</span><span class="pln">

topScope</span><span class="pun">.</span><span class="pln">array </span><span class="pun">=</span><span class="pln"> </span><span class="str">"..."</span><span class="pun">;</span><span class="pln">

topScope</span><span class="pun">.</span><span class="pln">length </span><span class="pun">=</span><span class="pln"> </span><span class="str">"..."</span><span class="pun">;</span><span class="pln">

topScope</span><span class="pun">.</span><span class="pln">element </span><span class="pun">=</span><span class="pln"> </span><span class="str">"..."</span><span class="pun">;</span><span class="pln">

run</span><span class="pun">(`</span><span class="pln">
</span><span class="kwd">do</span><span class="pun">(</span><span class="pln">define</span><span class="pun">(</span><span class="pln">sum</span><span class="pun">,</span><span class="pln"> fun</span><span class="pun">(</span><span class="pln">array</span><span class="pun">,</span><span class="pln">
     </span><span class="kwd">do</span><span class="pun">(</span><span class="pln">define</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">
        define</span><span class="pun">(</span><span class="pln">sum</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">while</span><span class="pun">(&lt;(</span><span class="pln">i</span><span class="pun">,</span><span class="pln"> length</span><span class="pun">(</span><span class="pln">array</span><span class="pun">)),</span><span class="pln">
          </span><span class="kwd">do</span><span class="pun">(</span><span class="pln">define</span><span class="pun">(</span><span class="pln">sum</span><span class="pun">,</span><span class="pln"> </span><span class="pun">+(</span><span class="pln">sum</span><span class="pun">,</span><span class="pln"> element</span><span class="pun">(</span><span class="pln">array</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">))),</span><span class="pln">
             define</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">i</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)))),</span><span class="pln">
        sum</span><span class="pun">))),</span><span class="pln">
   print</span><span class="pun">(</span><span class="pln">sum</span><span class="pun">(</span><span class="pln">array</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="pun">`);</span><span class="pln">
</span><span class="com">// → 6</span></pre>

<h4>
	إرشادات للحل
</h4>

<p>
	إن أسهل طريقة لإضافة الدعم هي تمثيل مصفوفات Egg بمصفوفات جافاسكربت، ويجب أن تكون القيم المضافة إلى النطاق العلوي دوالًا، كما يمكن أن يكون تعريف <code>array</code> بسيطًا جدًا إذا استَخدَمت وسيط rest مع الصيغة ثلاثية النقاط triple-dot notation.
</p>

<h3>
	التغليف Closure
</h3>

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

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_50" style="">
<span class="pln">run</span><span class="pun">(`</span><span class="pln">
</span><span class="kwd">do</span><span class="pun">(</span><span class="pln">define</span><span class="pun">(</span><span class="pln">f</span><span class="pun">,</span><span class="pln"> fun</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> fun</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">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)))),</span><span class="pln">
   print</span><span class="pun">(</span><span class="pln">f</span><span class="pun">(</span><span class="lit">4</span><span class="pun">)(</span><span class="lit">5</span><span class="pun">)))</span><span class="pln">
</span><span class="pun">`);</span><span class="pln">
</span><span class="com">// → 9</span></pre>

<p>
	ارجع الآن إلى تعريف صيغة <code>fun</code> واشرح الآلية المسببة لعملها.
</p>

<h4>
	إرشادات للحل
</h4>

<p>
	سنستخدِم آليات جافاسكربت مرةً أخرى للحصول على مزايا مشابهة في Egg، حيث تُمرَّر الصيغ الخاصة إلى النطاق المحلي الذي تقيَّم فيه كي تقيِّم هي صيغها الفرعية في ذلك النطاق، ويكون للدالة التي تعيدها <code>fun</code> وصول إلى وسيط <code>scope</code> المعطى للدالة المغلِّفة، كما تستخدِم ذلك لإنشاء نطاق الدالة المحلي حين تُستدعى.
</p>

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

<h3>
	التعليقات
</h3>

<p>
	أليس من الجميل أن تتمكن من كتابة تعليقات في لغة Egg؟ فلو جعلنا التعليق يبدأ بعلامة <code>#</code> مثلًا كما في حال علامتي // في جافاسكربت -ولا تقلق بشأن المحلِّل، إذ لن نجري أيّ تغييرات جوهرية فيه كي يدعم التعليقات-، فما علينا سوى تغيير <code>skipspace</code> كي تتخطى التعليقات كذلك، كما لو كانت مسافات فارغة، بحيث نتخطى التعليقات في كل مرة نستدعي فيها <code>skipspace</code>.
</p>

<p>
	الشيفرة أدناه تمثل <code>skipspace</code>، عدِّلها لتضيف دعم التعليقات في لغة Egg، مسترشدًا بما سبق.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_53" style="">
<span class="kwd">function</span><span class="pln"> skipSpace</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">
  let first </span><span class="pun">=</span><span class="pln"> string</span><span class="pun">.</span><span class="pln">search</span><span class="pun">(</span><span class="str">/\S/</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">first </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="kwd">return</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> string</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="pln">first</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">parse</span><span class="pun">(</span><span class="str">"# hello\nx"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → {type: "word", name: "x"}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">parse</span><span class="pun">(</span><span class="str">"a # one\n   # two\n()"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → {type: "apply",</span><span class="pln">
</span><span class="com">//    operator: {type: "word", name: "a"},</span><span class="pln">
</span><span class="com">//    args: []}</span></pre>

<h4>
	إرشادات للحل
</h4>

<ul>
<li>
		تأكد من جعل حلّك قادرًا على معالجة التعليقات المتعددة في صف مع مسافات فارغة محتملة بينها أو بعدها.
	</li>
	<li>
		سيكون التعبير النمطي أسهل طريقة تستخدمها في هذا التمرين للحل، لهذا اكتب شيئًا يطابق "مسافةً فارغةً أو تعليقًا أو صفرًا أو أكثر من مرة". واستخدم التابع <code>exec</code> أو<code>match</code>، وانظر إلى طول أول عنصر في المصفوفة المعادة -أي التطابق الكامل- لتعرف كم عدد المحارف التي عليك قصها.
	</li>
</ul>
<h3>
	إيجاد النطاق
</h3>

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

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

<p>
	أضف صيغة <code>set</code> الخاصة التي تشبه <code>define</code>، وتعطي قيمةً جديدةً للرابطة لتحدِّث الرابطة في نطاق خارجي إذا لم تكن موجودةً سلفًا في النطاق الداخلي، وإن لم تكن هذه الرابطة معرفةً، فأبلغ بالخطأ <code>ReferenceError</code> الذي هو نوع خطأ قياسي.
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6052_55" style="">
<span class="typ">Object</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">.</span><span class="pln">hasOwnProperty</span><span class="pun">.</span><span class="pln">call</span><span class="pun">(</span><span class="pln">scope</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">);</span><span class="pln">
specialForms</span><span class="pun">.</span><span class="kwd">set</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> scope</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="com">// ضع شيفرتك هنا.</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

run</span><span class="pun">(`</span><span class="pln">
</span><span class="kwd">do</span><span class="pun">(</span><span class="pln">define</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">),</span><span class="pln">
   define</span><span class="pun">(</span><span class="pln">setx</span><span class="pun">,</span><span class="pln"> fun</span><span class="pun">(</span><span class="pln">val</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">set</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> val</span><span class="pun">))),</span><span class="pln">
   setx</span><span class="pun">(</span><span class="lit">50</span><span class="pun">),</span><span class="pln">
   print</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="com">// → 50</span><span class="pln">
run</span><span class="pun">(`</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">quux</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">)`);</span><span class="pln">
</span><span class="com">// → ReferenceError أحد صور </span></pre>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<h4>
	إرشادات للحل
</h4>

<p>
	سيكون عليك التكرار على نطاق واحد في كل مرة باستخدام <code>Object.getPrototypeOf</code> للذهاب إلى النطاق الخارجي اللاحق. واستخدِم <code>hasOwnProperty</code> لتعرف ما إذا كانت الرابطة الموضَّحة بخاصية <code>name</code> في أول وسيط لـ <code>set</code> موجودةً في ذلك النطاق أم لا، فإن كانت كذلك فاضبطها على نتيجة تقييم الوسيط الثاني لـ <code>set</code> ثم أعد تلك القيمة، وإذا وصلت إلى أقصى نطاق خارجي -بحيث تكون إعادة <code>Object.getPrototypeOf</code> هي <code>null</code>- ولم تعثر على الرابطة بعد، فستكون هذه الرابطة غير موجودة ويجب رمي خطأ هنا.
</p>

<p>
	ترجمة -بتصرف- <a href="https://eloquentjavascript.net/12_language.html" rel="external nofollow">للفصل الثاني عشر من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>

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

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1304/" rel="">البرمجة غير المتزامنة في جافاسكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">دليلك الشامل لتعلم البرمجة</a>
	</li>
	<li>
		النسخة الكامة من كتاب <a href="https://academy.hsoub.com/files/17-%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%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-%D9%88%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A2%D9%84%D8%A9/" rel="">مدخل إلى الذكاء الاصطناعي وتعلم الآلة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1305</guid><pubDate>Sun, 29 Aug 2021 15:06:00 +0000</pubDate></item><item><title>&#x645;&#x641;&#x647;&#x648;&#x645; &#x627;&#x644;&#x631;&#x633;&#x648;&#x645; &#x627;&#x644;&#x62A;&#x62E;&#x637;&#x64A;&#x637;&#x64A;&#x629; Graphs &#x641;&#x64A; &#x627;&#x644;&#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x627;&#x62A;</title><link>https://academy.hsoub.com/programming/advanced/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%AE%D8%B7%D9%8A%D8%B7%D9%8A%D8%A9-graphs-%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-r1293/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/61178119a9466_5(1).png.603fdf87571e0cdfaa8f60e5abf75744.png" /></p>

<p id="-graphs">
	الرسم التخطيطي أو المخطط هو مجموعة من النقاط والخطوط التي ترتبط ببعضها (يمكن أن تكون فارغة)، وتسمى نقاط المخطط رؤوسًا vertices أو عقدًا nodes، بينما تسمى الخطوط التي تربط رؤوس المخطط أضلاعًا edges أو أقواسًا أو خطوطًا.
</p>

<p>
	يعرَّف مخطط G مثلًا كزوْج (V، E)، حيث تمثّل V مجموعة من الحروف، وتمثّل E مجموعة الأضلاع التي تربط تلك الحروف، انظر:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_5727_7" style="">
<span class="pln">E </span><span class="pun">⊆</span><span class="pln"> </span><span class="pun">{(</span><span class="pln">u</span><span class="pun">,</span><span class="pln">v</span><span class="pun">)</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> u</span><span class="pun">,</span><span class="pln"> v </span><span class="pun">∈</span><span class="pln"> V</span><span class="pun">}‎‎</span></pre>

<h2 id="-">
	تخزين المخططات
</h2>

<p>
	هناك طريقتان شائعتان لتخزين المخططات، وهما:
</p>

<ul>
<li>
		مصفوفة التجاور Adjacency Matrix.
	</li>
	<li>
		قائمة التجاور.
	</li>
</ul>
<h3>
	مصفوفة التجاور
</h3>

<p>
	<a data-ss1629880682="1" href="https://ar.wikipedia.org/wiki/%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A9_%D8%A7%D9%84%D9%85%D8%AC%D8%A7%D9%88%D8%B1%D8%A9" rel="external nofollow">مصفوفة التجاور</a> هي مصفوفة تُستخدم لتمثيل مخطط محدود ﬁnite graph، وتشير عناصر المصفوفة إلى ما إذا كانت أزواج الرؤوس متجاورة (مترابطة) في المخطط أم لا.
</p>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74512" data-ss1629880682="1" href="https://academy.hsoub.com/uploads/monthly_2021_08/Oh7b1.jpg.bb944a8a71d373608ad34ce99017f72f.jpg" rel=""><img alt="Oh7b1.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="74512" data-unique="dtami6uy2" src="https://academy.hsoub.com/uploads/monthly_2021_08/Oh7b1.thumb.jpg.b476f9ee4104ef1001be3de9a6822e4c.jpg" style=""></a>
</p>

<p>
	نرى في الشكل الموضح أعلاه جدولًا إلى جانب المخطط، ويمثّل هذا الجدول مصفوفة التجاور الخاصة بالمخطط المجاور له، وتمثل <code>Matrix[‎i][j] = 1</code>  هنا وجود ضلع بين i و j. بالمقابل، سنكتب <code>Matrix[‎i][j] = 0</code> إذا لم يكن هناك أيّ ضلع يربطهما.
</p>

<p>
	نستطيع وزن تلك الأضلاع، أي إلحاق رقم بكل ضلع -قد يمثل هذا الرقم المسافة بين مدينتين مثلًا-، وهنا نضع الوزن في الموضع <code>Matrix[‎i][j]‎‎</code> بدلًا من 1.
</p>

<p>
	والمخطط الموضح أعلاه ثنائي الاتجاه Bidirectional، أو غير موجّه Undirected، أي أنّه إذا كان بإمكاننا الانتقال من العقدة 2 إلى العقدة 1 فيمكننا أيضًا الانتقال من العقدة 1 إلى العقدة 2. إن لم تتحقّق هذه الخاصية نقول أنّ المخطط موجّه Directed. وتوضع أسهم بدل الخطوط إذا كان المخطط موجَّهًا، كما يمكن استخدام مصفوفات التجاور لتمثيل هذا النوع من المخططات.
</p>

<p style="text-align: center;">
	<img alt="MBM3s.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="74511" data-unique="b7mrbvn2d" src="https://academy.hsoub.com/uploads/monthly_2021_08/MBM3s.jpg.b0666a36c9146db0cf12bfe096adf2a9.jpg" style=""></p>

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

<p>
	انظر الشيفرة التوضيحية pseudo-code التالية لإنشاء مصفوفة التجاور، حيث يمثل N عدد العُقد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5727_10" style="">
<span class="typ">Procedure</span><span class="pln"> </span><span class="typ">AdjacencyMatrix</span><span class="pun">(</span><span class="pln">N</span><span class="pun">):</span><span class="pln">
</span><span class="typ">Matrix</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">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 N
        </span><span class="typ">Take</span><span class="pln"> input </span><span class="pun">-&gt;</span><span class="pln"> </span><span class="typ">Matrix</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">
    endfor
endfor</span></pre>

<p>
	فيما يلي طريقة أخرى لتعبئة المصفوفة، يمثل N فيها عدد العُقَد بينما يمثل E عدد الأضلاع:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5727_12" style="">
<span class="typ">Procedure</span><span class="pln"> </span><span class="typ">AdjacencyMatrix</span><span class="pun">(</span><span class="pln">N</span><span class="pun">,</span><span class="pln"> E</span><span class="pun">):</span><span class="pln"> 
</span><span class="typ">Matrix</span><span class="pun">[</span><span class="pln">N</span><span class="pun">][</span><span class="pln">E</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 E
    input </span><span class="pun">-&gt;</span><span class="pln"> n1</span><span class="pun">,</span><span class="pln"> n2</span><span class="pun">,</span><span class="pln"> cost
    </span><span class="typ">Matrix</span><span class="pun">[</span><span class="pln">n1</span><span class="pun">][</span><span class="pln">n2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> cost
    </span><span class="typ">Matrix</span><span class="pun">[</span><span class="pln">n2</span><span class="pun">][</span><span class="pln">n1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> cost
endfor</span></pre>

<p>
	يمكنك إزالة السطر
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5727_14" style="">
<span class="pln"> </span><span class="typ">Matrix</span><span class="pun">[</span><span class="pln">n2</span><span class="pun">][</span><span class="pln">n1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> cost </span></pre>

<p>
	من الشيفرة في المخططات الموجّهة.
</p>

<h4 id="-">
	عيوب استخدام مصفوفة التجاور
</h4>

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

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

<p>
	والفائدة الوحيدة لمصفوفة التجاور هي أنها تمكّننا من العثور بسهولة على مسار بين عقدتين مثل u-v، وكذلك تكلفة cost ذلك المسار، أي مجموع أوزان الأضلاع التي تؤلف المسار.
</p>

<p>
	شيفرة جافا التالية تطبق الشيفرة العامّة أعلاه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5727_16" style="">
<span class="kwd">import</span><span class="pln"> java</span><span class="pun">.</span><span class="pln">util</span><span class="pun">.</span><span class="typ">Scanner</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">Represent_Graph_Adjacency_Matrix</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">private</span><span class="pln"> final </span><span class="kwd">int</span><span class="pln"> vertices</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">int</span><span class="pun">[][]</span><span class="pln"> adjacency_matrix</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Represent_Graph_Adjacency_Matrix</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> v</span><span class="pun">)</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
       vertices </span><span class="pun">=</span><span class="pln"> v</span><span class="pun">;</span><span class="pln">
       adjacency_matrix </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">int</span><span class="pun">[</span><span class="pln">vertices </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">][</span><span class="pln">vertices </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">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> makeEdge</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> to</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> from</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> edge</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">
           adjacency_matrix</span><span class="pun">[</span><span class="pln">to</span><span class="pun">][</span><span class="pln">from</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> edge</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="typ">ArrayIndexOutOfBoundsException</span><span class="pln"> index</span><span class="pun">)</span><span class="pln">
       </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 vertices does not exists"</span><span class="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">public</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> getEdge</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> to</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> from</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="kwd">return</span><span class="pln"> adjacency_matrix</span><span class="pun">[</span><span class="pln">to</span><span class="pun">][</span><span class="pln">from</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="typ">ArrayIndexOutOfBoundsException</span><span class="pln"> index</span><span class="pun">)</span><span class="pln">
       </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 vertices does not exists"</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="lit">1</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="kwd">void</span><span class="pln"> main</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> args</span><span class="pun">[])</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
       </span><span class="kwd">int</span><span class="pln"> v</span><span class="pun">,</span><span class="pln"> e</span><span class="pun">,</span><span class="pln"> count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> to </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> from </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
       </span><span class="typ">Scanner</span><span class="pln"> sc </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Scanner</span><span class="pun">(</span><span class="typ">System</span><span class="pun">.</span><span class="pln">in</span><span class="pun">);</span><span class="pln">
       </span><span class="typ">Represent_Graph_Adjacency_Matrix</span><span class="pln"> graph</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">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">"Enter the number of vertices: "</span><span class="pun">);</span><span class="pln">
           v </span><span class="pun">=</span><span class="pln"> sc</span><span class="pun">.</span><span class="pln">nextInt</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">"Enter the number of edges: "</span><span class="pun">);</span><span class="pln">
           e </span><span class="pun">=</span><span class="pln"> sc</span><span class="pun">.</span><span class="pln">nextInt</span><span class="pun">();</span><span class="pln">
           graph </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Represent_Graph_Adjacency_Matrix</span><span class="pun">(</span><span class="pln">v</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">"Enter the edges: &lt;to&gt; &lt;from&gt;"</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">count </span><span class="pun">&lt;=</span><span class="pln"> e</span><span class="pun">)</span><span class="pln">
           </span><span class="pun">{</span><span class="pln">
               to </span><span class="pun">=</span><span class="pln"> sc</span><span class="pun">.</span><span class="pln">nextInt</span><span class="pun">();</span><span class="pln">
               from </span><span class="pun">=</span><span class="pln"> sc</span><span class="pun">.</span><span class="pln">nextInt</span><span class="pun">();</span><span class="pln">
               graph</span><span class="pun">.</span><span class="pln">makeEdge</span><span class="pun">(</span><span class="pln">to</span><span class="pun">,</span><span class="pln"> from</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</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="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 adjacency matrix for the given graph is: "</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">print</span><span class="pun">(</span><span class="str">"  "</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="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> v</span><span class="pun">;</span><span class="pln"> i</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">print</span><span class="pun">(</span><span class="pln">i </span><span class="pun">+</span><span class="pln"> </span><span class="str">" "</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="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="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;=</span><span class="pln"> v</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">System</span><span class="pun">.</span><span class="pln">out</span><span class="pun">.</span><span class="pln">print</span><span class="pun">(</span><span class="pln">i </span><span class="pun">+</span><span class="pln"> </span><span class="str">" "</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"> j </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln"> j </span><span class="pun">&lt;=</span><span class="pln"> v</span><span class="pun">;</span><span class="pln"> j</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">print</span><span class="pun">(</span><span class="pln">graph</span><span class="pun">.</span><span class="pln">getEdge</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="str">" "</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="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="typ">Exception</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">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">"Something went wrong"</span><span class="pun">);</span><span class="pln">
       </span><span class="pun">}</span><span class="pln">
       sc</span><span class="pun">.</span><span class="pln">close</span><span class="pun">();</span><span class="pln">
   </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لتشغيل الشيفرة أعلاه، احفظ الملف، ثم صرّف compile الشيفرة باستخدام التعليمة الآتية:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_375_7" style="">
<span class="pln"> </span><span class="pun">‎</span><span class="pln">javac </span><span class="typ">Represent_Graph_Adjacency_Matrix</span><span class="pun">.</span><span class="pln">java</span></pre>

<p>
	انظر المثال التالي الذي يوضح هذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5727_18" style="">
<span class="pln">$ java </span><span class="typ">Represent_Graph_Adjacency_Matrix</span><span class="pln">
</span><span class="typ">Enter</span><span class="pln"> the number of vertices</span><span class="pun">:</span><span class="pln">
</span><span class="lit">4</span><span class="pln">
</span><span class="typ">Enter</span><span class="pln"> the number of edges</span><span class="pun">:</span><span class="pln">
</span><span class="lit">6</span><span class="pln">
</span><span class="typ">Enter</span><span class="pln"> the edges</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">3</span><span class="pln"> </span><span class="lit">4</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">1</span><span class="pln"> </span><span class="lit">4</span><span class="pln">
</span><span class="lit">2</span><span class="pln"> </span><span class="lit">4</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="typ">The</span><span class="pln"> adjacency matrix </span><span class="kwd">for</span><span class="pln"> the given graph is</span><span class="pun">:</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">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="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">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="lit">1</span><span class="pln">
</span><span class="lit">3</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">0</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
</span><span class="lit">4</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">0</span><span class="pln"> </span><span class="lit">0</span></pre>

<h3 id="-">
	تخزين المخططات (قوائم التجاور)
</h3>

<p>
	<a data-ss1629880682="1" href="https://en.wikipedia.org/wiki/Adjacency_list" rel="external nofollow">قائمة التجاور</a> هي مجموعة من القوائم غير المرتبة تُستخدم لتمثيل المخططات المحدودة finite graphs، وتصف كل قائمة في المجموعة جيرانَ كلّ حرف من حروف المخطط. وميزة قوائم التجاور أنّها تحتاج مساحة ذاكرة أقل لتخزين المخططات.
</p>

<p>
	انظر المثال التالي عن مخططٍ ومصفوفة التجاور الخاصة به:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74514" data-ss1629880682="1" href="https://academy.hsoub.com/uploads/monthly_2021_08/PwJ3D.jpg.a3b2f0611e7093e1af41eb4cb5f778cf.jpg" rel=""><img alt="PwJ3D.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="74514" data-unique="w2nsaih89" src="https://academy.hsoub.com/uploads/monthly_2021_08/PwJ3D.thumb.jpg.f8dd7191a79afe3c0246646f790e2205.jpg" style=""></a>
</p>

<p>
	وهذه قائمة التجاور الخاصة بالمخطط أعلاه:
</p>

<p style="text-align: center;">
	<img alt="WEEcx.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="74516" data-unique="v6legp0ji" src="https://academy.hsoub.com/uploads/monthly_2021_08/WEEcx.jpg.18fffc76864bd6d06155b26ca1964b8c.jpg" style=""></p>

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

<p>
	تدعم العديد من لغات البرمجة نوعي البيانات المتجهات Vector والقوائم List ، والتي يمكننا استخدامها لتخزين قائمة التجاور، وهكذا لن يكون علينا تحديد حجم القائمة إذ يكفي أن نحدّد الحد الأقصى لعدد العقد. انظر الشيفرة العامة لذلك، حيث يمثل <code>maxN</code> الحد الأقصى للعُقد، بينما يمثل <code>E</code> عدد الأضلاع، ويشير التعبير <code>x, y</code> إلى وجود ضلع يربط بين x وy:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_5727_20" style="">
<span class="typ">Procedure</span><span class="pln"> </span><span class="typ">Adjacency</span><span class="pun">-</span><span class="typ">List</span><span class="pun">(</span><span class="pln">maxN</span><span class="pun">,</span><span class="pln"> E</span><span class="pun">):</span><span class="pln">
edge</span><span class="pun">[</span><span class="pln">maxN</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Vector</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 E
    input </span><span class="pun">-&gt;</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> y              
    edge</span><span class="pun">[</span><span class="pln">x</span><span class="pun">].</span><span class="pln">push</span><span class="pun">(</span><span class="pln">y</span><span class="pun">)</span><span class="pln">
    edge</span><span class="pun">[</span><span class="pln">y</span><span class="pun">].</span><span class="pln">push</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">end</span><span class="pln"> </span><span class="kwd">for</span><span class="pln">
</span><span class="typ">Return</span><span class="pln"> edge</span></pre>

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

<p>
	أما بالنسبة للمخططات الموزونة فسنحتاج إلى تخزين التكلفة (الوزن) أيضًا، من خلال إنشاء متجه أو قائمة أخرى باسم <code>cost[]‎‎</code> لتخزينها، انظر الشيفرة العامة لذلك:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_5727_22" style="">
<span class="typ">Procedure</span><span class="pln"> </span><span class="typ">Adjacency</span><span class="pun">-</span><span class="typ">List</span><span class="pun">(</span><span class="pln">maxN</span><span class="pun">,</span><span class="pln"> E</span><span class="pun">):</span><span class="pln">
edge</span><span class="pun">[</span><span class="pln">maxN</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Vector</span><span class="pun">()</span><span class="pln">
cost</span><span class="pun">[</span><span class="pln">maxN</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Vector</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 E
    input </span><span class="pun">-&gt;</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> w
    edge</span><span class="pun">[</span><span class="pln">x</span><span class="pun">].</span><span class="pln">push</span><span class="pun">(</span><span class="pln">y</span><span class="pun">)</span><span class="pln">
    cost</span><span class="pun">[</span><span class="pln">x</span><span class="pun">].</span><span class="pln">push</span><span class="pun">(</span><span class="pln">w</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">end</span><span class="pln"> </span><span class="kwd">for</span><span class="pln">
</span><span class="typ">Return</span><span class="pln"> edge</span><span class="pun">,</span><span class="pln"> cost</span></pre>

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

<h2 id="-">
	مقدمة إلى نظرية الرسوم التخطيطية
</h2>

<p>
	نظرية المخططات أو الرسوم التخطيطية Graph Theory هي فرع من فروع الرياضيات يهتم بدراسة الرسوم التخطيطية، وهي كائنات رياضية تُستخدم لنمذجة العلاقات الزوجية بين الكائنات.
</p>

<p>
	طُوِّرت نظرية المخططات من قبل اختراع الحاسوب، إذ كتب <a data-ss1629880682="1" href="https://ar.wikipedia.org/wiki/%D9%84%D9%8A%D9%88%D9%86%D9%87%D8%A7%D8%B1%D8%AA_%D8%A3%D9%88%D9%8A%D9%84%D8%B1" rel="external nofollow">ليونهارت أويلر</a> Leonhard Euler ورقة حول <a data-ss1629880682="1" href="https://ar.wikipedia.org/wiki/%D8%AC%D8%B3%D9%88%D8%B1_%D9%83%D9%88%D9%86%D9%8A%D8%BA%D8%B3%D8%A8%D8%B1%D8%BA_%D8%A7%D9%84%D8%B3%D8%A8%D8%B9%D8%A9" rel="external nofollow">جسور كونيجسبرج السبعة</a> Seven Bridges of Königsberg والتي تُعدّ أوّل ورقة علمية عن نظرية المخططات، وأدرك الناس منذ ذلك الحين أنه إذا أمكننا تحويل المشاكل إلى مسائل من نوع مدينة-طريق City-Road، فيمكننا حلها بسهولة باستخدام نظرية المخططات. وهناك تطبيقات عديدة لهذه النظرية، لعل أشهرها هو العثور على أقصر مسافة بين مدينتين.
</p>

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

<p>
	إليك تعاريف من المهم الاطلاع عليها ومعرفتها:
</p>

<ul>
<li>
		<strong>المخططات</strong>: لنقل أنّ لدينا 6 مدن، نرقّم هذه المدن من 1 إلى 6. سننشئ الآن مخططًا يمثّل هذه المدن، حيث تمثل الرؤوسُ المدن، مع ربط المدن التي تربطها طرق فيما بينها بأضلاع.
	</li>
</ul>
<p style="text-align: center;">
	<img alt="YzZHT.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74517" data-unique="ptlv29whk" src="https://academy.hsoub.com/uploads/monthly_2021_08/YzZHT.png.bf4bb51ba4c3cf654430c553286f56ee.png" style=""></p>

<p>
	هذا مخطط بسيط لتمثيل المدن والطرق الرابطة بينها، ونسمي هذه المدن في نظرية المخططات عقدًا Nodes أو حروفًا Vertex فيما نسمّي الطرق أضلاعًا Edge.
</p>

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

<p style="text-align: center;">
	<img alt="2EAW1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74509" data-unique="v26hnbfmb" src="https://academy.hsoub.com/uploads/monthly_2021_08/2EAW1.png.18fa2985e33d518aae19312798dd3465.png" style=""></p>

<p>
	تمثيل لمسار الفارس على رقعة الشطرنج
</p>

<p>
	وببساطة، تمثّل العقدة أيّ نوع من الكائنات، وتمثّل الأضلاع العلاقات بين تلك الكائنات.
</p>

<ul>
<li>
		<p>
			<strong>العقدة المتجاورة Adjacent Node</strong>: تكون B مجاورة لـ A إذا اشتركت عقدة A مع عقدة أخرى B في ضلع واحد، أي نقول أنّ العقدتين متجاورتان إذا اتصلت عقدتان اتصالًا مباشرًا، ويمكن لكل عقدة أن يكون لها عدة عقد مجاورة.
		</p>
	</li>
	<li>
		<p>
			<strong>المخططات الموجّهة وغير الموجّهة Directed and Undirected Graph</strong>: توضع علامات توجيهية (مثل الأسهم) على الأضلاع في المخططات الموجّهة للدلالة على أنّ الضلع أحادي الاتجاه. من ناحية أخرى، تحتوي أضلاع المخططات غير الموجّهة على علامات اتجاه على كلا الجانبين، للدلالة على أنها ثنائية الاتجاه. لكن تُحذف علامات التوجيه تلك في الغالب من المخططات غير الموجّهة، وتمثّل حينها الأضلاع كخطوط وحسب.
		</p>
	</li>
</ul>
<p style="margin-right: 40px;">
	وإذا افترضنا وجود حفلٍ في مكان ما، فسنمثّل الأشخاص الحاضرين بالعُقد، وسنرسم خطًا بين شخصين إذا تصافحا. لا شك أن هذه المخططات غير موجّهة هنا، لأنّه إذا صافح عمرو زيدًا فهذا يعني أنّ زيدًا صافح عَمرًا كذلك، فهي عملية ثنائية. بالمقابل، إذا رسمنا ضلعًا من عمرو إلى زيد إن كان زيد يقدّر عَمرًا ويحترمه فإنّ هذه المخططات ستكون موجّهة، ذلك أن الإعجاب لا يشترط أن يكون متبادلًا.
</p>

<p style="margin-right: 40px;">
	يُطلق على النوع الأول مخططات غير موجّهة undirected graphs، وتسمّى الأضلاع أضلاعًا غير موجّهة undirected edges، بالمقابل، يسمّى النوع الثاني مخططات موجّهة directed graph وتسمّى الأضلاع أضلاعًا موجّهة directed edges.
</p>

<ul>
<li>
		<strong>المخططات الموزونة وغير الموزونة Weighted and Unweighted Graph</strong>: المخطط الموزون هو مخطط يكون لكلّ ضلع من أضلاعه رقم (وزن)، يمكن أن تمثّل هذه الأوزان التكاليف أو الأطوال أو السعات وغير ذلك، وذلك اعتمادًا على المشكلة المطروحة.
	</li>
</ul>
<p style="text-align: center;">
	<img alt="pnP5z.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74513" data-unique="8dd631y2p" src="https://academy.hsoub.com/uploads/monthly_2021_08/pnP5z.png.36b9419347dd680482944d1abff33ffd.png" style=""></p>

<p style="margin-right: 40px;">
	بالمقابل، المخططات غير الموزونة هي مخططات نفترض أنّ أوزان جميع أضلاعها متساوية (تساوي1 افتراضيًا ).
</p>

<ul>
<li>
		<strong>المسارات</strong>: يمثّل المسار طريقًا للانتقال من عقدة إلى أخرى ويتألّف من سلسلة من الأضلاع، ولا شيء يمنع وجود عدة مسارات بين عقدتين.
	</li>
</ul>
<p style="text-align: center;">
	<img alt="3IPXO.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74510" data-unique="z6wjv7wst" src="https://academy.hsoub.com/uploads/monthly_2021_08/3IPXO.png.8f1b8ff70d508cf403b0ba558e228f4b.png" style=""></p>

<p>
	في المثال أعلاه، هناك مساران من A إلى D، الأول هو A-&gt; B ،B-&gt; C ،C-&gt; D ، وكلفته (مجموع أوزان الأضلاع التي تؤلّفه) هي 3 + 4 + 2 = 9، أما المسار الآخر فهو A-&gt; D، وكلفته 10. يقال أن المسار الذي يكلّف أدنى قدر هو المسار الأقصر.
</p>

<ul>
<li>
		<strong>الدرجة degree</strong>: درجة الحرف degree of a vertex هي عدد الأضلاع المرتبطة به، فإذا كان هناك ضلع يرتبط بالحرف في كلا الطرفين (حلقة loop)، فسيُحسب مرتين.
	</li>
</ul>
<p>
	يكون للعُقد في المخططات الموجّهة نوعان مختلفان من الدرجات:
</p>

<ul>
<li>
		<strong>الدرجة الداخلية In-degree</strong>: عدد الأضلاع التي تشير إلى العقدة.
	</li>
	<li>
		<strong>الدرجة الخارجية Out-degree</strong>: عدد الأضلاع التي تنطلق من العقدة الحالية وتشير إلى العقد الأخرى. بالنسبة للمخططات غير الموجّهة، يكون هناك نوع واحد طبعا، ويُسمّى درجة الحرف.
	</li>
</ul>
<p>
	بعض الخوارزميات المتعلقة بنظرية المخططات:
</p>

<ul>
<li>
		خوارزمية بلمان‎ فورد Bellman–Ford
	</li>
	<li>
		خوارزمية ديكسترا Dijkstra
	</li>
	<li>
		خوارزمية فورد فولكرسون Ford–Fulkerson
	</li>
	<li>
		خوارزمية كروسكال Kruskal.
	</li>
	<li>
		خوارزمية الجار الأقرب Nearest neighbour algorithm.
	</li>
	<li>
		خوارزمية بْرِم Prim.
	</li>
	<li>
		خوارزمية البحث العميق أولا Depth-ﬁrst search.
	</li>
	<li>
		خوارزمية البحث العريض أولًا Breadth-ﬁrst search.
	</li>
</ul>
<p>
	سوف نستعرضُ بعض هذه الخوارزميات لاحقًا.
</p>

<h2 id="-topological-sort">
	الترتيب الطوبولوجي Topological Sort
</h2>

<p>
	يرتّب الترتيب الطوبولوجي حروف مخطط موجّه ترتيبًا خطيًا، إذ يضعها في قائمة مُرتّبة حسب الأضلاع الموجّهة التي تربط تلك الحروف. وليكون هذا الترتيب ممكنا، يجب ألّا يحتوي المخطط على دورة موجّهة directed cycle، فإن كان لدينا مخطط <code>‎G = (V, E)‎</code>، فالترتيب الخطي رياضيًا هو ترتيب متوافق مع المخطط، أي يحقّق ما يلي:
</p>

<ul>
<li>
		إن كانت <code>‎G‎</code> تحتوي الضلع <code>‎(u, v) ∈‎ E</code> الذي ينتمي إلى E وينطلق من الحرف u إلى v، فستكون u أصغر من v وفق هذا الترتيب.
	</li>
</ul>
<p>
	وهنا من المهم ملاحظة أنّ كلّ مخطط موجّه غير دوري directed acyclic graph، أو DAG اختصارًا له ترتيب طوبولوجي واحد على الأقل، وهناك عدد من الخوارزميات التي تمكّننا من إنشاء ترتيب طوبولوجي لمخطط موجّه غير دوري في وقتٍ خطي، هذا مثال عام على إحداها:
</p>

<ol>
<li>
		استدع دالة <code>‎depth_first_search(G)‎</code> لحساب أوقات الإنتهاء ﬁnishing times ‏بـ <code>‎v.f‎</code> لكل حرف <code>‎v‎</code> ‏
	</li>
	<li>
		عقب الانتهاء من حرف ما، أدرِجه في مقدّمة <a data-ss1629880682="1" href="https://en.wikipedia.org/wiki/Linked_list" rel="external nofollow">قائمة مرتبطة</a> linked list.
	</li>
	<li>
		يُحدَّد الترتيب الطوبولوجي بقائمة الحروف المرتبطة التي نتجت من الخطوتين السابقتين.
	</li>
</ol>
<p>
	يمكن إجراء ترتيب طوبولوجي في مدة <code>‎V + E</code>، لأنّ "خوارزمية البحث العميق أولًا depth-ﬁrst search" تستغرق مدّة ‎‎(V + E)‎‎ ـ كما ستستغرق <code>‎Ω(1)‎</code> (وقت ثابت) لإدراج كل الحروف <code>‎|V|‎</code> في مقدمة القائمة المرتبطة.
</p>

<p>
	تستخدم العديدُ من التطبيقات المخططاتَ الموجّهة الدورية directed acyclic graphs لتمثيل الأسبقية بين الأحداث، إذ يُستخدم الترتيب الطوبولوجي للحصول على الترتيب الصحيح لمعالجة كل حرف من حروف المخطط. وقد تمثّل حروف المخططُ المهامَ التي يتعيّن إنجازها، فيما تمثل الأضلاع أسبقية تنفيذ تلك المهام، وهكذا يمثّل الترتيب الطوبولوجي التسلسل المناسب لأداء مجموعة المهام الموضّحة في <code>‎V‎</code>.
</p>

<h3 id="-">
	مثال
</h3>

<p>
	ليكن <code>‎v‎</code> حرفًا يمثّل مهمّة <code>‎Task(hours_to_complete: int)‎</code>، بحيث يمثّل الوسيط hours_to_complete الوقت المُستغرَق لتنفيذ المهمة. فمثلًا، تمثّل <code>‎Task(4)‎</code> مهمّة تستغرق <code>‎4‎</code> ساعات لإكمالها.
</p>

<p>
	من جهة أخرى، يمثّل ضلع <code>‎e‎</code> قيمة <code>‎Cooldown(hours: int)‎</code>، والتي تمثّل المدة الزمنية التي تنقضي قبل استئناف المهمة التالية (أي التي يشير إليها الضلع) بعد الانتهاء من المهمة الحالية (التي ينطلق منها الضلع). فإن كان هناك ضلع <code>‎Cooldown(3)‎</code> يربط بين مهمّتين <strong>أ</strong> و <strong>ب</strong>، فذلك يعني أنه بعد الانتهاء من المهمة <strong>أ</strong>، ستحتاج أن تنتظر 3 ساعات حتى تستطيع تنفيذ المهمة <strong>ب</strong> (مثلا ليبرد المحرّك).
</p>

<p>
	فيما يلي، المخطط غير الدوري والموجّه <code>‎dag‎</code> يحتوي 5 رؤوس:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5727_24" style="">
<span class="pln">A &lt;- dag.add_vertex(Task(4));
B &lt;- dag.add_vertex(Task(5));
C &lt;- dag.add_vertex(Task(3));
D &lt;- dag.add_vertex(Task(2));
E &lt;- dag.add_vertex(Task(7));</span></pre>

<p>
	نربط الحروف عبر أضلاع موجّهة بحيث يكون المخططات غير دوري، انظر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2389_7" style="">
<span class="com">// A ---&gt; C -----+</span><span class="pln">
</span><span class="com">// |      |      |</span><span class="pln">
</span><span class="com">// v      v      v</span><span class="pln">
</span><span class="com">// B ---&gt; D ---&gt; E</span><span class="pln">
dag</span><span class="pun">.</span><span class="pln">add_edge</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"> </span><span class="typ">Cooldown</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
dag</span><span class="pun">.</span><span class="pln">add_edge</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="typ">Cooldown</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
dag</span><span class="pun">.</span><span class="pln">add_edge</span><span class="pun">(</span><span class="pln">B</span><span class="pun">,</span><span class="pln"> D</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Cooldown</span><span class="pun">(</span><span class="lit">1</span><span class="pun">));</span><span class="pln">
dag</span><span class="pun">.</span><span class="pln">add_edge</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"> </span><span class="typ">Cooldown</span><span class="pun">(</span><span class="lit">1</span><span class="pun">));</span><span class="pln">
dag</span><span class="pun">.</span><span class="pln">add_edge</span><span class="pun">(</span><span class="pln">C</span><span class="pun">,</span><span class="pln"> E</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Cooldown</span><span class="pun">(</span><span class="lit">1</span><span class="pun">));</span><span class="pln">
dag</span><span class="pun">.</span><span class="pln">add_edge</span><span class="pun">(</span><span class="pln">D</span><span class="pun">,</span><span class="pln"> E</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Cooldown</span><span class="pun">(</span><span class="lit">3</span><span class="pun">));</span></pre>

<p>
	ستكون هناك ثلاثة تراتيب طوبولوجية ممكنة بين <code>‎A‎</code> و<code>‎E‎</code>:
</p>

<ul>
<li>
		A -&gt; B -&gt; D -&gt; E
	</li>
	<li>
		A -&gt; C -&gt; D -&gt; E
	</li>
	<li>
		A -&gt; C -&gt; E
	</li>
</ul>
<h2 id="-depth-first-traversal">
	رصد الدورات في المخططات الموجهة باستخدام الاجتياز العميق أولا Depth First Traversal
</h2>

<p>
	إذا نتج عن الاجتياز العميق أولًا ضلعٌ خلفي back edge، فذلك يعني أنّ المخطط الموجّه يحتوي دورة cycle. والضلع الخلفي هو ضلع ينطلق من عقدة ويعود إليها أو إلى إحدى أسلافها في شجرة بحث عميق أولًا Depth-first search اختصارًا DFS.
</p>

<p>
	بالنسبة لمخطط غير متصل disconnected graph، سنحصل على <a data-ss1629880682="1" href="https://stackoverflow.com/questions/47396753/what-is-a-dfs-forest-component" rel="external nofollow">غابة بحث عميق أولا أو غابة DFS</a> وهي اختصار لـ DFS forest، لذلك سيكون عليك التكرار على جميع الحروف في المخطط لإيجاد أشجار البحث العميق أولًا والمنفصلة disjoint DFS trees.
</p>

<p>
	فيما يلي تنفيذ بلغة C++‎‎:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5727_30" style="">
<span class="com">#include</span><span class="pln"> </span><span class="str">&lt;iostream&gt;</span><span class="pln">
    </span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;list&gt;</span><span class="pln">

   </span><span class="kwd">using</span><span class="pln"> </span><span class="kwd">namespace</span><span class="pln"> std</span><span class="pun">;</span><span class="pln">

   </span><span class="com">#define</span><span class="pln"> NUM_V   </span><span class="lit">4</span><span class="pln">
   </span><span class="kwd">bool</span><span class="pln"> helper</span><span class="pun">(</span><span class="typ">list</span><span class="str">&lt;int&gt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">graph</span><span class="pun">,</span><span class="pln"> </span><span class="typ">int</span><span class="pln"> u</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">bool</span><span class="pun">*</span><span class="pln"> visited</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">bool</span><span class="pun">*</span><span class="pln"> recStack</span><span class="pun">)</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
       visited</span><span class="pun">[</span><span class="pln">u</span><span class="pun">]=</span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
       recStack</span><span class="pun">[</span><span class="pln">u</span><span class="pun">]=</span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
       </span><span class="typ">list</span><span class="str">&lt;int&gt;</span><span class="pun">::</span><span class="typ">iterator</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="pln"> graph</span><span class="pun">[</span><span class="pln">u</span><span class="pun">].</span><span class="pln">begin</span><span class="pun">();</span><span class="pln">i</span><span class="pun">!=</span><span class="pln">graph</span><span class="pun">[</span><span class="pln">u</span><span class="pun">].</span><span class="pln">end</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">if</span><span class="pun">(</span><span class="pln">recStack</span><span class="pun">[*</span><span class="pln">i</span><span class="pun">])</span></pre>

<p>
	شرح السطر السابق في الشيفرة: عند إيجاد حرف <code>v</code> في مكدس التكرارية الخاص باجتياز DFS، أعِد <code>true</code>، تابع المثال الآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5727_32" style="">
<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="kwd">else</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">u</span><span class="pun">)</span><span class="pln"> </span><span class="com">// في حال كان هناك ضلع من الحرف إلى نفسه</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="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pun">(!</span><span class="pln">visited</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">if</span><span class="pun">(</span><span class="pln">helper</span><span class="pun">(</span><span class="pln">graph</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"> visited</span><span class="pun">,</span><span class="pln"> recStack</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="pun">}</span><span class="pln">
       recStack</span><span class="pun">[</span><span class="pln">u</span><span class="pun">]=</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">false</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span></pre>

<p>
	هنا تستدعي دالة التغليف الدالةَ <code>helper</code> على كل حرف لم يُزَر بعد، وتعيد دالة helper القيمة <code>true</code> عند رصد ضلع خلفي في الشجيرة، وإلا فإنها تعيد <code>false</code>، تابع المثال الآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5727_34" style="">
<span class="pln">bool isCyclic</span><span class="pun">(</span><span class="pln">list</span><span class="pun">&lt;</span><span class="kwd">int</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln">graph</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> V</span><span class="pun">)</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
     bool visited</span><span class="pun">[</span><span class="pln">V</span><span class="pun">];</span><span class="pln">  </span><span class="com">// مصفوفة لتتبع الأحرف المُزارة سلفا</span><span class="pln">
     bool recStack</span><span class="pun">[</span><span class="pln">V</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="kwd">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">V</span><span class="pun">;</span><span class="pln">i</span><span class="pun">++)</span><span class="pln">
      visited</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]=</span><span class="kwd">false</span><span class="pun">,</span><span class="pln"> recStack</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]=</span><span class="kwd">false</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="kwd">int</span><span class="pln"> u </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> u </span><span class="pun">&lt;</span><span class="pln"> V</span><span class="pun">;</span><span class="pln"> u</span><span class="pun">++)</span><span class="pln"> </span><span class="com">// التحقق اليدوي مما إذا كانت كل الأحرف مُزارة</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">visited</span><span class="pun">[</span><span class="pln">u</span><span class="pun">]==</span><span class="kwd">false</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">helper</span><span class="pun">(</span><span class="pln">graph</span><span class="pun">,</span><span class="pln"> u</span><span class="pun">,</span><span class="pln"> visited</span><span class="pun">,</span><span class="pln"> recStack</span><span class="pun">))</span><span class="pln"> 
</span><span class="com">// التحقق مما إذا كانت شجرةُ “بحثٍ عميقٍ أولًا” تحتوي دورة.</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="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">
   </span><span class="com">/*
   Driver function
   */</span><span class="pln">
   </span><span class="kwd">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
       list</span><span class="pun">&lt;</span><span class="kwd">int</span><span class="pun">&gt;*</span><span class="pln"> graph </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> list</span><span class="pun">&lt;</span><span class="kwd">int</span><span class="pun">&gt;[</span><span class="pln">NUM_V</span><span class="pun">];</span><span class="pln">
       graph</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">push_back</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
       graph</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">push_back</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
       graph</span><span class="pun">[</span><span class="lit">1</span><span class="pun">].</span><span class="pln">push_back</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
       graph</span><span class="pun">[</span><span class="lit">2</span><span class="pun">].</span><span class="pln">push_back</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln">
       graph</span><span class="pun">[</span><span class="lit">2</span><span class="pun">].</span><span class="pln">push_back</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span><span class="pln">
       graph</span><span class="pun">[</span><span class="lit">3</span><span class="pun">].</span><span class="pln">push_back</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span><span class="pln">
       bool res </span><span class="pun">=</span><span class="pln"> isCyclic</span><span class="pun">(</span><span class="pln">graph</span><span class="pun">,</span><span class="pln"> NUM_V</span><span class="pun">);</span><span class="pln">
       cout</span><span class="pun">&lt;&lt;</span><span class="pln">res</span><span class="pun">&lt;&lt;</span><span class="pln">endl</span><span class="pun">;</span><span class="pln">
   </span><span class="pun">}</span></pre>

<p>
	تكون النتيجة كما هو موضّح أدناه، أن هناك ثلاثة أضلاع خلفية في المخططات، واحد بين الحرفين 0 و 2؛ وآخر بين الحروف 0 و1 و2؛ والحرف 3. والتعقيد الزمني للبحث يساوي O (V + E)‎‎، حيث يمثّل V عدد الحروف، وE يمثّل عدد الأضلاع.
</p>

<p style="text-align: center;">
	<img alt="UHwvp.png" class="ipsImage ipsImage_thumbnailed" data-fileid="75409" data-unique="cjxz2zra5" src="https://academy.hsoub.com/uploads/monthly_2021_08/UHwvp.png.7eaf1769030b3fc8c87888420d887e39.png" style=""></p>

<h2 id="-thorup">
	خوارزمية Thorup
</h2>

<p id="-">
	كيف يمكن العثور على أقصر مسار من حرف (مصدر) معيّن إلى أيّ حرف آخر في مخطط غير موجّهة؟ قدّم "ميكيل توغوب Mikkel Thorup" -نُطْقُ اسمه من الدانماركية- أول خوارزمية تحل هذه المشكلة. يساوي التعقيد الزمني لهذه الخوارزمية O (m)‎‎.
</p>

<p>
	وفيما يلي الأفكارُ الأساسية التي تعتمد عليها الخوارزمية:
</p>

<ul>
<li>
		هناك عدّة طرق للعثور على <a data-ss1629880682="1" href="https://ar.wikipedia.org/wiki/%D8%B4%D8%AC%D8%B1%D8%A9_%D9%85%D8%AA%D9%81%D8%B1%D8%B9%D8%A9" rel="external nofollow">الشجرة المتفرّعة</a> spanning tree في مدة O (m)‎‎ (لن نذكر هذه الطرق هنا)، سيكون عليك إنشاء الشجرة المتفرّعة من الضلع الأقصر إلى الأطول، وسينتج عن ذلك <a data-ss1629880682="1" href="https://www.quora.com/What-is-the-difference-between-a-tree-and-a-forest-in-graph-theory" rel="external nofollow">غابة</a> (مجموعة من الأشجار غير المتصلة بالضرورة) تحتوي العديد من المكوّنات المتصلة قبل أن تنمو كاملةً.
	</li>
	<li>
		اختر عددًا صحيحًا b ‏(b&gt; = 2)، ولا تأخذ بالحسبان إلّا الغابات المتفرّعة ذات الطول الأقصى b ^ k، ثم ادمج المكونات المتشابهة في كل شيء ولكن تختلف في قيمة k. سنسمّى أصغر قيم k مستوى المكوّن level of the component. ثم ضع المكوّنات بعد هذا في الشجرة في المكان المناسب بحيث يكون الحرف u أبًا للحرف v إذ كان u هي أصغر مكوّن مختلف عن v ويحتوي v بشكل كامل. الجذر سيكون المخطط بأكمله، أمّا الأوراق فهي الحروف المفردة single vertices في المخطط الأصلي (مستواها يساوي سالب ما لا نهاية). ستحتوي الشجرة على O (n)‎‎ عقدة.
	</li>
	<li>
		حافظ على المسافة بين كل مكوّن وبين المصدر كما هو الحال في خوارزمية Dijkstra، تساوي مسافة مكوّن يحتوي أكثر من حرفٍ المسافةَ الأقل بين أبنائها غير الموسَّعين unexpanded children. اجعل مسافة الحرف الأصلي source vertex على 0، ثمّ حدّث الأسلاف وفقًا لذلك.
	</li>
	<li>
		احسب المسافات <a data-ss1629880682="1" href="https://ar.wikipedia.org/wiki/%D8%A3%D8%B3%D8%A7%D8%B3_(%D8%B1%D9%8A%D8%A7%D8%B6%D9%8A%D8%A7%D8%AA" rel="external nofollow">بنظام العدّ من الأساس b</a> أو base b، وعند زيارة عقدة في المستوى k للمرة الأولى، ضع أبناءها في مجموعات أو سلَّات buckets مشتركة بين جميع العقد من المستوى k. وخذ بالحسبان أوّل b سلّة وحسب في كل مرة تزور فيها عقدة، وَزُر كلّ واحدة منها ثمّ أزلها، ثمّ حدّث مسافة العقدة الحالية، وَأعِد ربط العقدة الحالية بأصلها باستخدام المسافة الجديدة وانتظر الزيارة القادمة للسلات التالية.
	</li>
	<li>
		عند زيارة ورقة leaf، تكون المسافة الحالية هي المسافة النهائية للحرف. وسِّع جميع الأضلاع المنطلقة منه في المخطط الأصلي، ثمّ حدّث المسافات وفقًا لذلك.
	</li>
	<li>
		زر العقدة الجذرية (المخطط كاملًا) بشكل متكرر إلى أن تصل إلى الوجهة المقصودة.
	</li>
</ul>
<p>
	تعتمد هذه الخوارزمية على حقيقة أنّه لا يمكن أن يوجد ضلع ذا طول أقل من l بين مكوّنيْن متصليْن في غابة متفرّعة ذات حدّ طولي يساوي ‏‎‎length limitation، لذلك، يمكنك حصر تركيزك على مكوّن واحد متصل بدءًا من مسافة x إلى أن تصل إلى المسافة x + l. ستزور بعض الحروف في الطريق قبل زيارة جميع الحروف ذات المسافة الأقصر، لكن ذلك لا يهم بما أنّنا نعلم أنّه لن يكون هناك مسار أقصر إلى هنا من تلك الحروف.
</p>

<h2 id="-graph-traversals">
	اجتياز المخططات Graph Traversals
</h2>

<p>
	هناك العديد من الخوارزميات للبحث في المخططات، سنستعرض إحداها فيما يلي، وهي خوارزمية <a data-ss1629880682="1" href="https://ar.wikipedia.org/wiki/%D8%A7%D9%84%D8%A8%D8%AD%D8%AB_%D8%A7%D9%84%D9%85%D8%AA%D8%B9%D9%85%D9%82_%D8%A7%D9%84%D8%A3%D9%88%D9%84" rel="external nofollow">البحث العميق أولا</a>.
</p>

<p>
	تنفذ الشيفرة التالية هذه الخوارزمية، إذ تنشئ دالة تأخذ فهرس العقدة الحالي كوسيط، وقائمة التجاور (مخزّنة في متجهة من المتجهات)، ومتجهة منطقية vector of boolean لتعقّب العقدة التي تمت زيارتها، انظر:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_2389_9" style="">
<span class="kwd">void</span><span class="pln"> dfs</span><span class="pun">(</span><span class="typ">int</span><span class="pln"> node</span><span class="pun">,</span><span class="pln"> </span><span class="typ">vector</span><span class="pun">&lt;</span><span class="typ">vector</span><span class="str">&lt;int&gt;</span><span class="pun">&gt;*</span><span class="pln"> graph</span><span class="pun">,</span><span class="pln"> </span><span class="typ">vector</span><span class="str">&lt;bool&gt;</span><span class="pun">*</span><span class="pln"> visited</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">if</span><span class="pun">((*</span><span class="pln">visited</span><span class="pun">)[</span><span class="pln">node</span><span class="pun">])</span><span class="pln">
       </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="com">// set as visited to avoid visiting the same node twice</span><span class="pln">
    </span><span class="pun">(*</span><span class="pln">visited</span><span class="pun">)[</span><span class="pln">node</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
    </span><span class="com">// نفّذ بعض الإجراءات هنا</span><span class="pln">
    cout </span><span class="pun">&lt;&lt;</span><span class="pln"> node</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="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"> </span><span class="pun">(*</span><span class="pln">graph</span><span class="pun">)[</span><span class="pln">node</span><span class="pun">].</span><span class="pln">size</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">
        dfs</span><span class="pun">((*</span><span class="pln">graph</span><span class="pun">)[</span><span class="pln">node</span><span class="pun">][</span><span class="pln">i</span><span class="pun">],</span><span class="pln"> graph</span><span class="pun">,</span><span class="pln"> visited</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ترجمة -بتصرّف- للفصلين 9 و10 من كتاب <a data-ss1629880682="1" href="https://goalkicker.com/AlgorithmsBook/" rel="external nofollow">Algorithms Notes for Professionals</a>
</p>

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

<ul>
<li>
		المقالة السابقة: <a data-ss1629880682="1" href="https://academy.hsoub.com/programming/advance/%D8%A7%D9%84%D8%A3%D8%B4%D8%AC%D8%A7%D8%B1-trees-%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-r1292/" rel="">الأشجار Trees في الخوازرميات</a>
	</li>
	<li>
		<a data-ss1629880682="1" href="https://academy.hsoub.com/programming/advance/%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>
	</li>
	<li>
		<a data-ss1629880682="1" href="https://academy.hsoub.com/programming/advance/%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>
	<li>
		النسخة الكاملة من كتاب <a data-ss1629880682="1" href="https://academy.hsoub.com/files/17-%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%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-%D9%88%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A2%D9%84%D8%A9/" rel="">مدخل إلى الذكاء الاصطناعي وتعلم الآلة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1293</guid><pubDate>Mon, 23 Aug 2021 15:07:00 +0000</pubDate></item><item><title>&#x645;&#x641;&#x647;&#x648;&#x645; &#x627;&#x644;&#x623;&#x634;&#x62C;&#x627;&#x631; Trees &#x641;&#x64A; &#x627;&#x644;&#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x627;&#x62A;</title><link>https://academy.hsoub.com/programming/advanced/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A3%D8%B4%D8%AC%D8%A7%D8%B1-trees-%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-r1292/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/61177534a58d3_4(1).png.21e980e7365805c52d0c8a59ad7dac74.png" /></p>
<p>
	تمثل الأشجار نوعًا فرعيًا لهيكل أعم من هياكل البياناتٍ التخطيطية التفرعية Node-Edge Graph Data Structure كالآتي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74495" href="https://academy.hsoub.com/uploads/monthly_2021_08/BmT3t.png.5f95f78cd9fab8067701799e52dc9e5b.png" rel="" data-fileext="png"><img alt="BmT3t.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74495" data-unique="71s3zi15a" src="https://academy.hsoub.com/uploads/monthly_2021_08/BmT3t.png.5f95f78cd9fab8067701799e52dc9e5b.png"></a>
</p>

<p>
	والشجرة هي مخطط يحقق الشرطين التاليين:
</p>

<ul>
	<li>
		<strong>غير حلقي acyclic</strong>: أي لا يحتوي أيّ دورات cycles أو حلقات loops.
	</li>
	<li>
		<strong>متصل</strong>: أي يمكن الوصول إلى كل عقدة من عقد المخطط عبر مسار معيّن.
	</li>
</ul>

<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>، إذ يُستخدم لنمذجة العديد من هياكل البيانات الخوارزمية المختلفة، مثل الأشجار الثنائية العادية والأشجار الحمراء-السوداء red-black trees، وأشجار B وأشجار AB وأشجار 23 وأشجار الكومة Heap وكذلك أشجار Trie.
</p>

<p>
	وتكون الشجرة جِذريةً ‎Rooted Tree‎ في حال:
</p>

<ul>
	<li>
		اختيار خلية واحدة لتكون جذرًا للشجرة.
	</li>
	<li>
		صباغة Painting الجذر في أعلى الشجرة.
	</li>
	<li>
		إنشاء طبقة سفلية lower layer لكل خلية في المخطط تبعًا للمسافة بينها وبين الجذر، فكلما كانت المسافة أكبر، كانت الخلية أسفل كما في الصورة التوضيحية أعلاه. ويرمز عادةً للأشجار بالرمز <code>‎T‎</code>.
	</li>
</ul>

<h3>
	الأشجار اللانهائية anary tree
</h3>

<p>
	نُمثّل الأشجار اللانهائية -أي الأشجار التي يمكن أن يتفرّع عدد لانهائي من الفروع من عقدها- على هيئة أشجار ثنائية binary tree، وهي أشجار يتفرع عن كل عقدة منها فرعان فقط، ويُعَد الفرع الثاني منهما شقيقًا Sibling، لاحظ أنه إذا كانت الشجرة ثنائيةً، فسينشئ التمثيل عُقدًا إضافية.
</p>

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

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

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_11" style=""><span class="pln">struct node
 </span><span class="pun">{</span><span class="pln">
    struct node </span><span class="pun">*</span><span class="pln">next</span><span class="pun">;</span><span class="pln">
    struct node </span><span class="pun">*</span><span class="pln">child</span><span class="pun">;</span><span class="pln">
    std</span><span class="pun">::</span><span class="pln">string data</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
 void printtree_r</span><span class="pun">(</span><span class="pln">struct node </span><span class="pun">*</span><span class="pln">node</span><span class="pun">,</span><span class="pln"> int depth</span><span class="pun">)</span><span class="pln">
 </span><span class="pun">{</span><span class="pln">
    int i</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">node</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">node</span><span class="pun">-&gt;</span><span class="pln">child</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">depth</span><span class="pun">*</span><span class="lit">3</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">" "</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">
           printtree_r</span><span class="pun">(</span><span class="pln">node</span><span class="pun">-&gt;</span><span class="pln">child</span><span class="pun">,</span><span class="pln"> depth </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="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">depth</span><span class="pun">*</span><span class="lit">3</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">" "</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="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">depth</span><span class="pun">*</span><span class="lit">3</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">" "</span><span class="pun">);</span><span class="pln">
            printf</span><span class="pun">(</span><span class="str">"%s\n"</span><span class="pun">,</span><span class="pln"> node</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">.</span><span class="pln">c_str</span><span class="pun">());</span><span class="pln">
            node </span><span class="pun">=</span><span class="pln"> node</span><span class="pun">-&gt;</span><span class="pln">next</span><span class="pun">;</span><span class="pln">
         </span><span class="pun">}</span><span class="pln">
     </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
 void printtree</span><span class="pun">(</span><span class="pln">node </span><span class="pun">*</span><span class="pln">root</span><span class="pun">)</span><span class="pln">
 </span><span class="pun">{</span><span class="pln">
    printree_r</span><span class="pun">(</span><span class="pln">root</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>

<h3>
	التحقق من تساوي شجرتين ثنائيتين
</h3>

<p>
	انظر الشجرتين التاليتين:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74500" href="https://academy.hsoub.com/uploads/monthly_2021_08/Gzckc.png.2e227e67cbc6e8b8c7f4f7eb83ad68c0.png" rel="" data-fileext="png"><img alt="Gzckc.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74500" data-unique="qb3ainc9d" src="https://academy.hsoub.com/uploads/monthly_2021_08/Gzckc.png.2e227e67cbc6e8b8c7f4f7eb83ad68c0.png"></a>
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74505" href="https://academy.hsoub.com/uploads/monthly_2021_08/y2dy0.png.748af0cd213ab153fe9e0701bcb5a928.png" rel="" data-fileext="png"><img alt="y2dy0.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74505" data-unique="11ix0afbd" src="https://academy.hsoub.com/uploads/monthly_2021_08/y2dy0.png.748af0cd213ab153fe9e0701bcb5a928.png"></a>
</p>

<p>
	هاتان الشجرتين متساويتان، على خلاف الشجرتين التاليتين:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74497" href="https://academy.hsoub.com/uploads/monthly_2021_08/C8jj7.png.536ef3632542761d4f4578d0eb84536f.png" rel="" data-fileext="png"><img alt="C8jj7.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74497" data-unique="3xvu8vk3c" src="https://academy.hsoub.com/uploads/monthly_2021_08/C8jj7.png.536ef3632542761d4f4578d0eb84536f.png"></a>
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74494" href="https://academy.hsoub.com/uploads/monthly_2021_08/BBfnO.png.c4198022f389a4b843e1290940eca90a.png" rel="" data-fileext="png"><img alt="BBfnO.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74494" data-unique="249o3v2aw" src="https://academy.hsoub.com/uploads/monthly_2021_08/BBfnO.png.c4198022f389a4b843e1290940eca90a.png"></a>
</p>

<p>
	وفيما يلي شيفرة عامة زائفة pseudo code للتحقق من تساوي شجرتين:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8133_14" style=""><span class="pln">boolean sameTree</span><span class="pun">(</span><span class="pln">node root1</span><span class="pun">,</span><span class="pln"> node root2</span><span class="pun">){</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">root1 </span><span class="pun">==</span><span class="pln"> NULL </span><span class="pun">&amp;&amp;</span><span class="pln"> root2 </span><span class="pun">==</span><span class="pln"> NULL</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="kwd">if</span><span class="pun">(</span><span class="pln">root1 </span><span class="pun">==</span><span class="pln"> NULL </span><span class="pun">||</span><span class="pln"> root2 </span><span class="pun">==</span><span class="pln"> NULL</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">if</span><span class="pun">(</span><span class="pln">root1</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">==</span><span class="pln"> root2</span><span class="pun">-&gt;</span><span class="pln">data
    </span><span class="pun">&amp;&amp;</span><span class="pln"> sameTree</span><span class="pun">(</span><span class="pln">root1</span><span class="pun">-&gt;</span><span class="pln">left</span><span class="pun">,</span><span class="pln">root2</span><span class="pun">-&gt;</span><span class="pln">left</span><span class="pun">)</span><span class="pln">
       </span><span class="pun">&amp;&amp;</span><span class="pln"> sameTree</span><span class="pun">(</span><span class="pln">root1</span><span class="pun">-&gt;</span><span class="pln">right</span><span class="pun">,</span><span class="pln"> root2</span><span class="pun">-&gt;</span><span class="pln">right</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></pre>

<h2>
	أشجار البحث الثنائية Binary Search Trees
</h2>

<p>
	الأشجار الثنائية هي الأشجار التي يتفرّع عن كلّ عقدة منها ابنان على الأكثر، وشجرة البحث الثنائية Binary search tree أو BST اختصارًا هي شجرة ثنائية عناصرها مُرتّبة ترتيبًا خاصًا، إذ تكون جميع القيم الموجودة في الشجيرة أو الفرع sub tree الأيسر أصغر من القيم في الشجَيرة اليمنى.
</p>

<h3>
	إدراج عنصر في شجرة بحث ثنائية Python
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74490" href="https://academy.hsoub.com/uploads/monthly_2021_08/3NG0e.gif.99cd6f731c6008d0cca4aedd03e36e01.gif" rel="" data-fileext="gif"><img alt="3NG0e.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="74490" data-unique="voqgirs2b" src="https://academy.hsoub.com/uploads/monthly_2021_08/3NG0e.gif.99cd6f731c6008d0cca4aedd03e36e01.gif"></a>
</p>

<p>
	رسم يوضح كيفية إدراج عنصر في الشجرة
</p>

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

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_16" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">Node</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> val</span><span class="pun">):</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">l_child </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">None</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">r_child </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">None</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> val</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74498" href="https://academy.hsoub.com/uploads/monthly_2021_08/GlqkB.png.4d93ae682ee322486533c89e7e8b2485.png" rel="" data-fileext="png"><img alt="GlqkB.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74498" data-unique="wu7nlfni4" src="https://academy.hsoub.com/uploads/monthly_2021_08/GlqkB.png.4d93ae682ee322486533c89e7e8b2485.png"></a>
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_18" style=""><span class="kwd">def</span><span class="pln"> insert</span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> node</span><span class="pun">):</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> root </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">None</span><span class="pun">:</span><span class="pln">
        root </span><span class="pun">=</span><span class="pln"> node
 </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">data </span><span class="pun">&gt;</span><span class="pln"> node</span><span class="pun">.</span><span class="pln">data</span><span class="pun">:</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">l_child </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">None</span><span class="pun">:</span><span class="pln">
                root</span><span class="pun">.</span><span class="pln">l_child </span><span class="pun">=</span><span class="pln"> node
 </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
                insert</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">l_child</span><span class="pun">,</span><span class="pln"> node</span><span class="pun">)</span><span class="pln">
 </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">r_child </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">None</span><span class="pun">:</span><span class="pln">
                root</span><span class="pun">.</span><span class="pln">r_child </span><span class="pun">=</span><span class="pln"> node
 </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
                insert</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">r_child</span><span class="pun">,</span><span class="pln"> node</span><span class="pun">)</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74506" href="https://academy.hsoub.com/uploads/monthly_2021_08/zwGtx.png.017621f93fa4b471c4b6f7a377957c4e.png" rel="" data-fileext="png"><img alt="zwGtx.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74506" data-unique="7ll7hxa0h" src="https://academy.hsoub.com/uploads/monthly_2021_08/zwGtx.png.017621f93fa4b471c4b6f7a377957c4e.png"></a>
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_21" style=""><span class="kwd">def</span><span class="pln"> in_order_print</span><span class="pun">(</span><span class="pln">root</span><span class="pun">):</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> root</span><span class="pun">:</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln">
    in_order_print</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">l_child</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">print</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">data
    in_order_print</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">r_child</span><span class="pun">)</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74492" href="https://academy.hsoub.com/uploads/monthly_2021_08/5fGHu.png.a01ad7bafbd842974d8afb6f1a063957.png" rel="" data-fileext="png"><img alt="5fGHu.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74492" data-unique="1t6djvf3q" src="https://academy.hsoub.com/uploads/monthly_2021_08/5fGHu.png.a01ad7bafbd842974d8afb6f1a063957.png"></a>
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_23" style=""><span class="kwd">def</span><span class="pln"> pre_order_print</span><span class="pun">(</span><span class="pln">root</span><span class="pun">):</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">not</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="kwd">print</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">data
    pre_order_print</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">l_child</span><span class="pun">)</span><span class="pln">
    pre_order_print</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">r_child</span><span class="pun">)</span><span class="pln"> </span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74501" href="https://academy.hsoub.com/uploads/monthly_2021_08/lOXwz.png.697110f56debcb26ed7fed4ebc6ffe00.png" rel="" data-fileext="png"><img alt="lOXwz.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74501" data-unique="c9eb0ycx0" src="https://academy.hsoub.com/uploads/monthly_2021_08/lOXwz.png.697110f56debcb26ed7fed4ebc6ffe00.png"></a>
</p>

<h3>
	حذف عنصر من شجرة بحث ثنائية C++‎
</h3>

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

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74503" href="https://academy.hsoub.com/uploads/monthly_2021_08/TTM4d.png.367355f83654b4f8929f9963b4c4152f.png" rel="" data-fileext="png"><img alt="TTM4d.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74503" data-unique="438kdsjo2" src="https://academy.hsoub.com/uploads/monthly_2021_08/TTM4d.png.367355f83654b4f8929f9963b4c4152f.png"></a>
</p>

<p>
	هناك ثلاث حالات يجب مراعاتها عند حذف العقدة، هي الآتية:
</p>

<ul>
	<li>
		الحالة 1: العقدة المراد حذفها هي ورقة أو عقدة طرفية leaf node، مثل العقدة ذات القيمة 22.
	</li>
	<li>
		الحالة 2: العقدة المراد حذفها لها ابن واحد، مثل العقدة ذات القيمة 26.
	</li>
	<li>
		الحالة 3: العقدة المراد حذفها لها ابنان، مثل العقدة ذات القيمة 49.
	</li>
</ul>

<h4>
	شرح الحالات
</h4>

<ul>
	<li>
		عندما تكون العقدة المراد حذفها ورقةً، فما عليك سوى حذف العقدة وتعيين المؤشر الفارغ <code>‎nullptr‎</code> إلى العقدة الأصلية.
	</li>
	<li>
		عندما تحتوي العقدة المراد حذفها على ابن واحد فقط، انسخ قيمة الابن إلى قيمة العقدة ثمّ احذف الابن (ستُحوّل إلى الحالة 1).
	</li>
	<li>
		عندما يكون للعقدة المراد حذفها ابنان، فيمكن نسخ القيمة الأصغر من شجيرتها اليمنى إلى العقدة، بعدها يمكن حذف القيمة الدنيا من الشجيرة اليمنى للعقدة، حيث ستُحوَّل إلى الحالة 2.
	</li>
</ul>

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

	<p data-gramm="false">
		<strong>ملاحظة</strong>: يمكن أن يحتوي الحد الأدنى في الشجيرة اليمنى على ابن واحد على الأكثر، والذي سيكون الابن الأيمن، إذ أنّه في حال كان لها ابن آخر أيسر، فذلك يعني أنّها ليست القيمة الأدنى أو لا تستوفي خاصية أشجار البحث الثنائية BST.
	</p>
</blockquote>

<p>
	انظر المثال التالي على حذف عنصر من شجرة بحث ثنائية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_25" style=""><span class="pln">struct node
</span><span class="pun">{</span><span class="pln">
   int data</span><span class="pun">;</span><span class="pln">
   node </span><span class="pun">*</span><span class="pln">left</span><span class="pun">,</span><span class="pln"> </span><span class="pun">*</span><span class="pln">right</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
node</span><span class="pun">*</span><span class="pln"> delete_node</span><span class="pun">(</span><span class="pln">node </span><span class="pun">*</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> int data</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">root </span><span class="pun">==</span><span class="pln"> nullptr</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="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">data </span><span class="pun">&lt;</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">left  </span><span class="pun">=</span><span class="pln"> delete_node</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">left</span><span class="pun">,</span><span class="pln"> data</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="pun">(</span><span class="pln">data </span><span class="pun">&gt;</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">right </span><span class="pun">=</span><span class="pln"> delete_node</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">right</span><span class="pun">,</span><span class="pln"> data</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">if</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">left </span><span class="pun">==</span><span class="pln"> nullptr </span><span class="pun">&amp;&amp;</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">right </span><span class="pun">==</span><span class="pln"> nullptr</span><span class="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">
     free</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"> nullptr</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="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">left </span><span class="pun">==</span><span class="pln"> nullptr</span><span class="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">
      node</span><span class="pun">*</span><span class="pln"> temp </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"> root</span><span class="pun">-&gt;</span><span class="pln">right</span><span class="pun">;</span><span class="pln">
      free</span><span class="pun">(</span><span class="pln">temp</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="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">right </span><span class="pun">==</span><span class="pln"> nullptr</span><span class="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">
      node</span><span class="pun">*</span><span class="pln"> temp </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"> root</span><span class="pun">-&gt;</span><span class="pln">left</span><span class="pun">;</span><span class="pln">
      free</span><span class="pun">(</span><span class="pln">temp</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"> </span><span class="lit">3</span><span class="pln">
   </span><span class="pun">{</span><span class="pln">
      node</span><span class="pun">*</span><span class="pln"> temp </span><span class="pun">=</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">right</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">while</span><span class="pun">(</span><span class="pln">temp</span><span class="pun">-&gt;</span><span class="pln">left </span><span class="pun">!=</span><span class="pln"> nullptr</span><span class="pun">)</span><span class="pln"> temp </span><span class="pun">=</span><span class="pln"> temp</span><span class="pun">-&gt;</span><span class="pln">left</span><span class="pun">;</span><span class="pln">
      root</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> temp</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">;</span><span class="pln">
      root</span><span class="pun">-&gt;</span><span class="pln">right </span><span class="pun">=</span><span class="pln"> delete_node</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">right</span><span class="pun">,</span><span class="pln"> temp</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="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"> root</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	التعقيد الزمني للشيفرة أعلاه هو O(h)‎‎، حيث تمثّل h ارتفاع الشجرة.
</p>

<h3>
	أدنى سلف مشترك في شجرة بحث ثنائية
</h3>

<p>
	انظر شجرة البحث الثنائية التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74504" href="https://academy.hsoub.com/uploads/monthly_2021_08/Y1QA4.png.7e39a223ae115bbff3c924360d47fc6b.png" rel="" data-fileext="png"><img alt="Y1QA4.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74504" data-unique="v6yybybje" src="https://academy.hsoub.com/uploads/monthly_2021_08/Y1QA4.png.7e39a223ae115bbff3c924360d47fc6b.png"></a>
</p>

<ul>
	<li>
		أدنى سلف مشترك Lowest common ancestor لـ 22 و26 هو 24.
	</li>
	<li>
		أدنى سلف مشترك لـ 26 و49 هو 46.
	</li>
	<li>
		أدنى سلف مشترك لـ 22 و24 هو 24.
	</li>
</ul>

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

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_27" style=""><span class="pln">lowestCommonAncestor</span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln">node1</span><span class="pun">,</span><span class="pln"> node2</span><span class="pun">){</span><span class="pln">
</span><span class="kwd">if</span><span class="pun">(</span><span class="pln">root </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">)</span><span class="pln">   
</span><span class="kwd">return</span><span class="pln"> NULL</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="pun">(</span><span class="pln">node1</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">==</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">||</span><span class="pln"> node2</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">==</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">data</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="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pun">((</span><span class="pln">node1</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">&lt;=</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">&amp;&amp;</span><span class="pln"> node2</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">&gt;</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
             </span><span class="pun">||</span><span class="pln"> </span><span class="pun">(</span><span class="pln">node2</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">&lt;=</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">&amp;&amp;</span><span class="pln"> node1</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">&gt;</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">data</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="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">&gt;</span><span class="pln"> max</span><span class="pun">(</span><span class="pln">node1</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">,</span><span class="pln">node2</span><span class="pun">-&gt;</span><span class="pln">data</span><span class="pun">)){</span><span class="pln">
           </span><span class="kwd">return</span><span class="pln"> lowestCommonAncestor</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">left</span><span class="pun">,</span><span class="pln"> node1</span><span class="pun">,</span><span class="pln"> node2</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"> lowestCommonAncestor</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">right</span><span class="pun">,</span><span class="pln"> node1</span><span class="pun">,</span><span class="pln"> node2</span><span class="pun">);</span><span class="pln">
     </span><span class="pun">}</span><span class="pln">
       </span><span class="pun">}</span></pre>

<h3>
	شجرة البحث الثنائية Python
</h3>

<p>
	انظر إلى شيفرة البايثون التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_29" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">Node</span><span class="pun">(</span><span class="pln">object</span><span class="pun">):</span><span class="pln">
   </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> val</span><span class="pun">):</span><span class="pln">
       self</span><span class="pun">.</span><span class="pln">l_child </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">None</span><span class="pln">
       self</span><span class="pun">.</span><span class="pln">r_child </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">None</span><span class="pln">
       self</span><span class="pun">.</span><span class="pln">val </span><span class="pun">=</span><span class="pln"> val
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">BinarySearchTree</span><span class="pun">(</span><span class="pln">object</span><span class="pun">):</span><span class="pln">
   </span><span class="kwd">def</span><span class="pln"> insert</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> root</span><span class="pun">,</span><span class="pln"> node</span><span class="pun">):</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> root </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">None</span><span class="pun">:</span><span class="pln">
           </span><span class="kwd">return</span><span class="pln"> node
       </span><span class="kwd">if</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">val </span><span class="pun">&lt;</span><span class="pln"> node</span><span class="pun">.</span><span class="pln">val</span><span class="pun">:</span><span class="pln">
           root</span><span class="pun">.</span><span class="pln">r_child </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">insert</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">r_child</span><span class="pun">,</span><span class="pln"> node</span><span class="pun">)</span><span class="pln">
       </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
           root</span><span class="pun">.</span><span class="pln">l_child </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">insert</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">l_child</span><span class="pun">,</span><span class="pln"> node</span><span class="pun">)</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> root
   </span><span class="kwd">def</span><span class="pln"> in_order_place</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> root</span><span class="pun">):</span><span class="pln">
       </span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">not</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="kwd">None</span><span class="pln">
       </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
           self</span><span class="pun">.</span><span class="pln">in_order_place</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">l_child</span><span class="pun">)</span><span class="pln">
           </span><span class="kwd">print</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">val
           self</span><span class="pun">.</span><span class="pln">in_order_place</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">r_child</span><span class="pun">)</span><span class="pln">
   </span><span class="kwd">def</span><span class="pln"> pre_order_place</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> root</span><span class="pun">):</span><span class="pln">
       </span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">not</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="kwd">None</span><span class="pln">
       </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
           </span><span class="kwd">print</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">val
           self</span><span class="pun">.</span><span class="pln">pre_order_place</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">l_child</span><span class="pun">)</span><span class="pln">
           self</span><span class="pun">.</span><span class="pln">pre_order_place</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">r_child</span><span class="pun">)</span><span class="pln">
   </span><span class="kwd">def</span><span class="pln"> post_order_place</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> root</span><span class="pun">):</span><span class="pln">
       </span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">not</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="kwd">None</span><span class="pln">
       </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
           self</span><span class="pun">.</span><span class="pln">post_order_place</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">l_child</span><span class="pun">)</span><span class="pln">
           self</span><span class="pun">.</span><span class="pln">post_order_place</span><span class="pun">(</span><span class="pln">root</span><span class="pun">.</span><span class="pln">r_child</span><span class="pun">)</span><span class="pln">
           </span><span class="kwd">print</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">val</span></pre>

<p>
	هذه شيفرة لإنشاء عقدة جديدة وإدراج البيانات فيها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_31" style=""><span class="pln">r </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Node</span><span class="pun">(</span><span class="lit">3</span><span class="pun">)</span><span class="pln">
node </span><span class="pun">=</span><span class="pln"> </span><span class="typ">BinarySearchTree</span><span class="pun">()</span><span class="pln">
nodeList </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">8</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">12</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">6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">15</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">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">]</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> nd </span><span class="kwd">in</span><span class="pln"> nodeList</span><span class="pun">:</span><span class="pln">
    node</span><span class="pun">.</span><span class="pln">insert</span><span class="pun">(</span><span class="pln">r</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Node</span><span class="pun">(</span><span class="pln">nd</span><span class="pun">))</span><span class="pln">
</span><span class="kwd">print</span><span class="pln"> </span><span class="str">"------In order ---------"</span><span class="pln">
</span><span class="kwd">print</span><span class="pln"> </span><span class="pun">(</span><span class="pln">node</span><span class="pun">.</span><span class="pln">in_order_place</span><span class="pun">(</span><span class="pln">r</span><span class="pun">))</span><span class="pln">
</span><span class="kwd">print</span><span class="pln"> </span><span class="str">"------Pre order ---------"</span><span class="pln">
</span><span class="kwd">print</span><span class="pln"> </span><span class="pun">(</span><span class="pln">node</span><span class="pun">.</span><span class="pln">pre_order_place</span><span class="pun">(</span><span class="pln">r</span><span class="pun">))</span><span class="pln">
</span><span class="kwd">print</span><span class="pln"> </span><span class="str">"------Post order ---------"</span><span class="pln">
</span><span class="kwd">print</span><span class="pln"> </span><span class="pun">(</span><span class="pln">node</span><span class="pun">.</span><span class="pln">post_order_place</span><span class="pun">(</span><span class="pln">r</span><span class="pun">))</span></pre>

<h2>
	التحقق مما إذا كانت الشجرة شجرة بحث ثنائية أم لا
</h2>

<p>
	تكون شجرةٌ ثنائيةٌ ما "شجرةَ بحث ثنائية" إذا كانت تستوفي أيًّا من الشروط التالية:
</p>

<ul>
	<li>
		إن كانت فارغة.
	</li>
	<li>
		لا تتفرع منها أيّ شجيرات.
	</li>
	<li>
		لكلّ عقدة x في الشجرة، يجب أن تكون جميع المفاتيح (إن وجدت) في الشجيرة اليسرى أصغر من مفتاح x، أي ‏key(x)‎‎، ويتعيّن أن تكون جميع المفاتيح (إذا وجدت) في الشجيرة اليمنى أكبر من key(x)‎‎.
	</li>
</ul>

<p>
	الخوارزمية التكرارية التالية تتحقق من الشروط أعلاه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8133_33" style=""><span class="pln">is_BST</span><span class="pun">(</span><span class="pln">root</span><span class="pun">):</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> root </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">:</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span></pre>

<p>
	تحقق من القيم في الشجيرة اليسرى:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8133_35" style=""><span class="pln"> </span><span class="kwd">if</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">left </span><span class="pun">!=</span><span class="pln"> NULL</span><span class="pun">:</span><span class="pln">
   max_key_in_left </span><span class="pun">=</span><span class="pln"> find_max_key</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">left</span><span class="pun">)</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> max_key_in_left </span><span class="pun">&gt;</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">key</span><span class="pun">:</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span></pre>

<p>
	تحقق من القيم في الشجيرة اليمنى:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_37" style=""><span class="pln"> </span><span class="kwd">if</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">right </span><span class="pun">!=</span><span class="pln"> NULL</span><span class="pun">:</span><span class="pln">
   min_key_in_right </span><span class="pun">=</span><span class="pln"> find_min_key</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">right</span><span class="pun">)</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> min_key_in_right </span><span class="pun">&lt;</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">key</span><span class="pun">:</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> false
 </span><span class="kwd">return</span><span class="pln"> is_BST</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">left</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> is_BST</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">right</span><span class="pun">)</span></pre>

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

<p>
	 
</p>

<p>
	سنستخدم هذه الفكرة لتطوير خوازمية أكثر فعالية.
</p>

<p>
	ولفعل هذا، نرمز للقيمة الدنيا الممكنة لأيّ مفتاح <code>‎K_MIN‎</code>، والقيمة القصوى بالرمز <code>K_MAX</code>. فإن بدأتَ من جذر الشجرة يكون نطاق قيم الشجرة هو <code>‎[ K_MIN ،K_MAX</code>‎]‎. وإذا كان x‎ هو مفتاح عقدة الجذر فسيكون نطاق القيم في الشجيرة اليسرى هو <code><code>‎[K_MIN,x)‎</code></code>، ونطاق القيم في الشجيرة اليمنى هو <code><code>(x,K_MAX].</code></code>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8133_39" style=""><code>
<span class="pln">s_BST</span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> min</span><span class="pun">,</span><span class="pln"> max</span><span class="pun">):</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> root </span><span class="pun">==</span><span class="pln"> NULL</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="pln">
   </span><span class="com">// هل مفتاح العقدة الحالية خارج النطاق؟</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">key </span><span class="pun">&lt;</span><span class="pln"> min </span><span class="pun">||</span><span class="pln"> root</span><span class="pun">-&gt;</span><span class="pln">key </span><span class="pun">&gt;</span><span class="pln"> max</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="pln">
   </span><span class="com">// التحقق مما إذا كانت الشجيرتان اليسرى واليمنى أشجارَ بحث ثنائية</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> is_BST</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">left</span><span class="pun">,</span><span class="pln">min</span><span class="pun">,</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">key</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> is_BST</span><span class="pun">(</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">right</span><span class="pun">,</span><span class="pln">root</span><span class="pun">-&gt;</span><span class="pln">key</span><span class="pun">+</span><span class="lit">1</span><span class="pun">,</span><span class="pln">max</span><span class="pun">)</span></code></pre>

<p>
	وستُستَدعى في البداية على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_41" style=""><code>
<span class="pln">is_BST</span><span class="pun">(</span><span class="pln">my_tree_root</span><span class="pun">,</span><span class="pln">KEY_MIN</span><span class="pun">,</span><span class="pln">KEY_MAX</span><span class="pun">)</span></code></pre>

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

<h3>
	النظر في ما إن كانت شجرة ما تحقق شرط أشجار البحث الثنائية
</h3>

<p>
	انظر المثال التالي: إن كانت المدخلات كما يلي:
</p>

<p style="text-align: center;">
	<code><a class="ipsAttachLink ipsAttachLink_image" data-fileid="74502" href="https://academy.hsoub.com/uploads/monthly_2021_08/sd2Zq.png.cd7915ea50cc136d7ce6cd3cead07284.png" rel="" data-fileext="png"><img alt="sd2Zq.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74502" data-unique="iljszcopd" src="https://academy.hsoub.com/uploads/monthly_2021_08/sd2Zq.png.cd7915ea50cc136d7ce6cd3cead07284.png"></a> </code>
</p>

<p>
	فإن الخرج سيكون خاطئًا false، وعليه لا تكون هذه شجرة بحث ثنائية، ذلك أنّ 4 في الشجيرة اليسرى أكبر من قيمة الجذر 3. انظر الآن مثالًا على شجرة أخرى:
</p>

<p style="text-align: center;">
	<code><a class="ipsAttachLink ipsAttachLink_image" data-fileid="74499" href="https://academy.hsoub.com/uploads/monthly_2021_08/GR41M.png.8787952ae1acb716d3537872330265f0.png" rel="" data-fileext="png"><img alt="GR41M.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74499" data-unique="y06kmj1wc" src="https://academy.hsoub.com/uploads/monthly_2021_08/GR41M.png.8787952ae1acb716d3537872330265f0.png"></a> </code>
</p>

<p>
	النتيجة ستكون صحيحة وتكون شجرة بحث ثنائية.
</p>

<h2>
	اجتيازات الأشجار الثنائية Binary Tree traversals
</h2>

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

<h3>
	الاجتياز بالمستويات: التطبيق
</h3>

<p>
	انظر الشجرة التالية:
</p>

<p style="text-align: center;">
	<code><a class="ipsAttachLink ipsAttachLink_image" data-fileid="74493" href="https://academy.hsoub.com/uploads/monthly_2021_08/7Kz71.png.39944ebeb26d04a2c89a68d52c2c3653.png" rel="" data-fileext="png"><img alt="7Kz71.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74493" data-unique="48aoqh7xe" src="https://academy.hsoub.com/uploads/monthly_2021_08/7Kz71.png.39944ebeb26d04a2c89a68d52c2c3653.png"></a> </code>
</p>

<p>
	يكون الاجتياز بالمستويات على الترتيب التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_43" style=""><code>
<span class="pun">‪‎</span><span class="lit">1</span><span class="pun">‎‎‎‎‪</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="lit">7</span><span class="pun">‪</span><span class="pln"> </span><span class="pun">‎</span></code></pre>

<p>
	مع طبع بيانات العُقَد مستوىً بمستوى، انظر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_45" style=""><code>
<span class="pln">include</span><span class="pun">&lt;</span><span class="pln">iostream</span><span class="pun">&gt;</span><span class="pln">
</span><span class="com">#include&lt;queue&gt;</span><span class="pln">
</span><span class="com">#include&lt;malloc.h&gt;</span><span class="pln">
using namespace std</span><span class="pun">;</span><span class="pln">
struct node</span><span class="pun">{</span><span class="pln">

   int data</span><span class="pun">;</span><span class="pln">
   node </span><span class="pun">*</span><span class="pln">left</span><span class="pun">;</span><span class="pln">
   node </span><span class="pun">*</span><span class="pln">right</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
void levelOrder</span><span class="pun">(</span><span class="pln">struct node </span><span class="pun">*</span><span class="pln">root</span><span class="pun">){</span><span class="pln">

       </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">root </span><span class="pun">==</span><span class="pln"> NULL</span><span class="pun">)</span><span class="pln">    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">

       queue</span><span class="pun">&lt;</span><span class="pln">node </span><span class="pun">*&gt;</span><span class="pln"> Q</span><span class="pun">;</span><span class="pln">
       Q</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">root</span><span class="pun">);</span><span class="pln">

       </span><span class="kwd">while</span><span class="pun">(!</span><span class="pln">Q</span><span class="pun">.</span><span class="pln">empty</span><span class="pun">()){</span><span class="pln">
       struct    node</span><span class="pun">*</span><span class="pln"> curr </span><span class="pun">=</span><span class="pln"> Q</span><span class="pun">.</span><span class="pln">front</span><span class="pun">();</span><span class="pln">
           cout</span><span class="pun">&lt;&lt;</span><span class="pln"> curr</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">&lt;&lt;</span><span class="str">" "</span><span class="pun">;</span><span class="pln">
           </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">curr</span><span class="pun">-&gt;</span><span class="pln">left </span><span class="pun">!=</span><span class="pln"> NULL</span><span class="pun">)</span><span class="pln"> Q</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">curr</span><span class="pun">-&gt;</span><span class="pln"> left</span><span class="pun">);</span><span class="pln">
               </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">curr</span><span class="pun">-&gt;</span><span class="pln">right </span><span class="pun">!=</span><span class="pln"> NULL</span><span class="pun">)</span><span class="pln"> Q</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">curr</span><span class="pun">-&gt;</span><span class="pln"> right</span><span class="pun">);</span><span class="pln">

               Q</span><span class="pun">.</span><span class="pln">pop</span><span class="pun">();</span><span class="pln">


       </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
struct node</span><span class="pun">*</span><span class="pln"> newNode</span><span class="pun">(</span><span class="pln">int data</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
   struct node</span><span class="pun">*</span><span class="pln"> node </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">struct node</span><span class="pun">*)</span><span class="pln">
                       malloc</span><span class="pun">(</span><span class="pln">sizeof</span><span class="pun">(</span><span class="pln">struct node</span><span class="pun">));</span><span class="pln">
   node</span><span class="pun">-&gt;</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> data</span><span class="pun">;</span><span class="pln">
   node</span><span class="pun">-&gt;</span><span class="pln">left </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span><span class="pln">
   node</span><span class="pun">-&gt;</span><span class="pln">right </span><span class="pun">=</span><span class="pln"> NULL</span><span class="pun">;</span><span class="pln">
   </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">node</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
int main</span><span class="pun">(){</span><span class="pln">

   struct node </span><span class="pun">*</span><span class="pln">root </span><span class="pun">=</span><span class="pln"> newNode</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
   root</span><span class="pun">-&gt;</span><span class="pln">left        </span><span class="pun">=</span><span class="pln"> newNode</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
   root</span><span class="pun">-&gt;</span><span class="pln">right       </span><span class="pun">=</span><span class="pln"> newNode</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span><span class="pln">
   root</span><span class="pun">-&gt;</span><span class="pln">left</span><span class="pun">-&gt;</span><span class="pln">left  </span><span class="pun">=</span><span class="pln"> newNode</span><span class="pun">(</span><span class="lit">4</span><span class="pun">);</span><span class="pln">
   root</span><span class="pun">-&gt;</span><span class="pln">left</span><span class="pun">-&gt;</span><span class="pln">right </span><span class="pun">=</span><span class="pln"> newNode</span><span class="pun">(</span><span class="lit">5</span><span class="pun">);</span><span class="pln">
   root</span><span class="pun">-&gt;</span><span class="pln">right</span><span class="pun">-&gt;</span><span class="pln">left  </span><span class="pun">=</span><span class="pln"> newNode</span><span class="pun">(</span><span class="lit">6</span><span class="pun">);</span><span class="pln">
   root</span><span class="pun">-&gt;</span><span class="pln">right</span><span class="pun">-&gt;</span><span class="pln">right </span><span class="pun">=</span><span class="pln"> newNode</span><span class="pun">(</span><span class="lit">7</span><span class="pun">);</span><span class="pln">
   printf</span><span class="pun">(</span><span class="str">"Level Order traversal of binary tree is \n"</span><span class="pun">);</span><span class="pln">
   levelOrder</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></code></pre>

<p>
	تُستخدم الصفوف Queues -وهو نوع من البيانات- لتحقيق الهدف أعلاه.
</p>

<h3>
	الاجتياز التنازلي والتصاعدي والمرتب
</h3>

<p>
	انظر الشجرة الثنائية التالية:
</p>

<p style="text-align: center;">
	<code><a class="ipsAttachLink ipsAttachLink_image" data-fileid="74491" href="https://academy.hsoub.com/uploads/monthly_2021_08/4oxnI.png.5f08e1e2b229a928199074d270dc9de1.png" rel="" data-fileext="png"><img alt="4oxnI.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74491" data-unique="jj8efmyur" src="https://academy.hsoub.com/uploads/monthly_2021_08/4oxnI.png.5f08e1e2b229a928199074d270dc9de1.png"></a> </code>
</p>

<ul>
	<li>
		<strong>الاجتياز التنازلي Pre-order traversal:</strong> يبدأ هذا النوع باجتياز العقدة، ثم الشجيرة اليسرى للعقدة، ثمّ الشجيرة اليمنى لها. ويكون الاجتياز التنازلي بالترتيب التالي:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_47" style=""><code>
<span class="lit">1</span><span class="pln"> </span><span class="lit">2</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">3</span><span class="pln"> </span><span class="lit">6</span><span class="pln"> </span><span class="lit">7</span></code></pre>

<ul>
	<li>
		<strong>الاجتياز المُرتّب In-order traversal</strong>: هو اجتياز الشجيرة اليسرى للعقدة، ثمّ العقدة نفسها، ثم الشجيرة اليمنى للعقدة. يكون الاجتياز المُرتّب بالترتيب التالي:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_49" style=""><code>
<span class="pln"> </span><span class="lit">4</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">6</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="lit">7</span><span class="pln">  </span></code></pre>

<ul>
	<li>
		<strong>الاجتياز التصاعدي Post-order traversal</strong>: هو اجتياز الشجيرة اليسرى للعقدة، ثم الشجيرة اليمنى، ثمّ العقدة، ويكون الاجتياز التصاعدي بالترتيب التالي:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8133_51" style=""><code>
<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">2</span><span class="pln"> </span><span class="lit">6</span><span class="pln"> </span><span class="lit">7</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="lit">1</span></code></pre>

<h2>
	العثور على أدنَى سلف مشترك لشجرة ثنائية
</h2>

<p>
	السلف المشترك الأدنى للعقدتين n1 وn2 هو أدنى عقدة في الشجرة يكون كلّ من n1 وn2 أحفادًا لها. انظر الشجرة التالية:
</p>

<p style="text-align: center;">
	<code><a class="ipsAttachLink ipsAttachLink_image" data-fileid="74496" href="https://academy.hsoub.com/uploads/monthly_2021_08/C4UqM.png.a6199c3d7b3c3e46064094b5ff0de875.png" rel="" data-fileext="png"><img alt="C4UqM.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74496" data-unique="fhp4v97d7" src="https://academy.hsoub.com/uploads/monthly_2021_08/C4UqM.png.a6199c3d7b3c3e46064094b5ff0de875.png"></a> </code>
</p>

<ul>
	<li>
		أدنى سلف مشترك للعقدتين ذواتي القيمتين 1 و4 هو 2.
	</li>
	<li>
		أدنى سلف مشترك للعقدتين ذواتي القيمتين 1 و5 هو 3.
	</li>
	<li>
		أدنى سلف مشترك للعقدتين ذواتي القيمتين 2 و4 هو 4.
	</li>
	<li>
		أدنى سلف مشترك للعقدتين ذواتي القيمتين 1 و2 هو 2.
	</li>
</ul>

<p>
	ترجمة -بتصرّف- للفصول من 4 إلى 8 من كتاب <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/advance/%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>
	</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/advance/%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/advance/%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>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advance/%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>
	<li>
		النسخة الكاملة من كتاب<a href="https://academy.hsoub.com/files/17-%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%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-%D9%88%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A2%D9%84%D8%A9/" rel=""> مدخل إلى الذكاء الاصطناعي وتعلم الآلة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1292</guid><pubDate>Sun, 15 Aug 2021 15:01:00 +0000</pubDate></item><item><title>&#x62A;&#x631;&#x645;&#x64A;&#x632; Big-O &#x641;&#x64A; &#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%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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/6112de4d161f3_3(2).png.2f79fb79a8286f96f5ac77cfadf23d4c.png" /></p>

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

<p>
	إذا كانت <code>‎n -&gt; f(n)‎</code> و<code>n -&gt; g(n)‎</code> دالتين مُعرّفتين على الأعداد الطبيعية، فسنقول أنّ <code>‎f = O(g)‎</code> فقط إذا كانت <code>‎f(n)/g(n)‎</code> محصورة bounded عندما يؤول n إلى اللا نهاية. أي أن <code>‎f = O(g)‎</code> فقط إذا كان هناك ثابت A، بحيث يكون<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3436_7" 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">g</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"> A</span><span class="pun">‎</span><span class="pln">
</span></pre>

<p>
	لكل n.
</p>

<p>
	وفي الواقع فإن نطاق استخدام ترميز Big-O أوسع قليلاً في الرياضيات، لكن سنضيّقه في النطاق المُستخدَم في تحليل الخوارزميات للتبسيط، أي الدوال المُعرَّفة على الأعداد الطبيعية والتي ليست لها قيم صفرية أو جذور zero values عندما يؤول n إلى اللانهاية.
</p>

<h2>
	شرح
</h2>

<p>
	لنأخذ حالة هاتين الدالتين:<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3436_9" 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"> </span><span class="lit">100n</span><span class="pun">^</span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">10n</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="lit">7</span><span class="pln">
</span></pre>

<p>
	والدالة<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3436_11" style="">
<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"> n</span><span class="pun">^</span><span class="lit">2</span><span class="pun">‎</span><span class="pln">
</span></pre>

<p>
	من الواضح تمامًا أنهما تؤولان إلى اللانهاية عندما يؤول n إلى اللانهاية. لكن قد لا يكفي أحيانًا أن نعرف النهاية limit، فقد نرغب أيضًا في معرفة السرعة التي تقترب بها الدوال من نهايتها، وهنا يأتي دور ترميز Big-O إذ يساعد على تصنيف الدوال بحسَب سرعة تقاربها.
</p>

<p>
	فعندئذ نطبّق التعريف للتحقق ممّا إذا كانت <code>‎f = O(g)‎</code> حيث لدينا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3436_13" 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">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">100</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">10</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">n</span><span class="pun">^</span><span class="lit">2</span><span class="pun">‎</span><span class="pln">
</span></pre>

<p>
	 وبما أن <code>‎10/n‎</code> يساوي القيمة 10 عندما يكون n=1 ويتناقص مع تزايد قيمة n، وبما أنّ <code>‎1/n^2‎</code> تساوي 1 عندما يساوي n القيمة 1 وهو أيضًا يتناقص مع تزايد قيمة n، فنحصل على المتراجحة
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3436_15" style="">
<span class="pln"> f</span><span class="pun">(</span><span class="pln">n</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">&lt;=</span><span class="pln"> </span><span class="lit">100</span><span class="pln"> </span><span class="pun">+</span><span class="lit">10</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">111</span><span class="pln">
</span></pre>

<p>
	وقد تحقّق شرط التعريف هنا لأنّنا وجدنا حدًّا bound للتعبير<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3436_17" 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">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></pre>

<p>
	وهو 111-، ونكون بهذا قد أثبتنا أنّ<code>‎f = O(g)‎‎</code>، ونقول أنّ f هي Big-O لـ <code>‎n^2‎</code>.
</p>

<p>
	هذا يعني أنّ f تؤول إلى اللانهاية بنفس سرعة g تقريبًا. قد يبدو هذا غريبًا في البداية لأنّنا وجدنا أنّ f أكبر بـ 111 مرة من g، أو بعبارة أخرى، عندما تنمو g بمقدار 1، فإن f تنمو بمقدار 111 على أقصى حد. والحقيقة أنّ ترميز Big-O ليس دقيقًا في تصنيف سرعات تقارب الدوال، لهذا نَستخدم <a href="https://ar.wikipedia.org/wiki/%D8%B9%D9%84%D8%A7%D9%82%D8%A9_%D8%AA%D9%83%D8%A7%D9%81%D8%A4" rel="external nofollow">علاقة التكافؤ</a> equivalence relationship في الرياضيات عندما نريد تقديرًا دقيقًا للسرعة.
</p>

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

<p>
	على سبيل المثال، في حالة<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3436_19" style="">
<span class="pun">‎</span><span class="pln">h</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"> n</span><span class="pun">^</span><span class="lit">2</span><span class="pun">*</span><span class="pln">log</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)‎</span><span class="pln">
</span></pre>

<p>
	 نلاحظ أنّ <code>‎h(n)/g(n) = log(n)‎</code> تؤول إلى ما لانهاية عندما يؤول n إلى ما لا نهاية، لذا فإنّ h ليست من الصنف O (n ^ 2)<code>‎‎</code>، لأنّ h تنمو لانهائيًا أسرع من <code>n ^ 2</code>.
</p>

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

	<p>
		<strong>ملاحظة جانبية</strong>: إذا كانت <code>‎f = O(g)‎</code> و <code>‎g = O(h)‎</code>؛ فسيكون <code>‎f = O(h)‎</code>، فمثلًا في حالتنا هذه سيكون لدينا <code>‎f = O(n^3)‎</code> و <code>‎f = O(n^4)‎</code> …
	</p>
</blockquote>

<p>
	وفي مجال تحليل تعقيد الخوارزميات، نكتب <code>‎f =‎ O(g)‎</code> للدلالة على أنّ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3436_21" style="">
<span class="pln">f </span><span class="pun">=</span><span class="pln"> O</span><span class="pun">(</span><span class="pln">g</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"> O</span><span class="pun">(</span><span class="pln">f</span><span class="pun">)‎</span><span class="pln">
</span></pre>

<p>
	 والتي يمكن تأويلها على أنّ g هي أصغر دالة من الصنف Big-O لــ f؛ أما في الرياضيات فنقول أنّ هاتين الدالتين Big-Theta لبعضها البعض.
</p>

<h2>
	كيفية الاستخدام
</h2>

<p>
	أول شيء عليك حسابه عند موازنة أداء الخوارزميات هو عدد العمليات التي تجريها الخوارزمية، وهو ما يُسمّى وقت التعقيد time complexity.
</p>

<p>
	ونفترض في هذا النموذج أنّ كل عملية أساسية (الجمع والضرب والمقارنة والتعيين وما إلى ذلك) تستغرق مقدارًا ثابتًا من الوقت، ونحسب عدد هذه العمليات. ونعبر في الغالب عن هذا العدد كدالة لحجم الدخل -الذي نصطلح على تسميته n-، ويؤول هذا العدد (الدالة) في الغالب إلى ما لا نهاية عندما يؤول n إلى ما لا نهاية، أما خلاف ذلك، فنقول أنّ الخوارزمية من النوع O (1)‎‎.
</p>

<p>
	ونحن نصنّف الخوارزميات إلى أصناف بحسب سرعات الدوال ونمثّلها بالترميز Big-O: فمثلًا إن قلنا أنّ خوارزميةً ما من النوع الآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3436_23" style="">
<span class="pln"> O </span><span class="pun">(</span><span class="pln">n </span><span class="pun">^</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)‎‎</span><span class="pln">
</span></pre>

<p>
	 فإنّنا نقصد أنّ عدد العمليات التي تنفّذها الخوارزمية -معبَّرًا عنها كدالة لـ n- هو O (n ^ 2)<code>‎‎</code>. وهو ما يعني أنّ سرعة الخوارزمية تقارب سرعة خوارزمية تجري عددًا من العمليات يساوي مرّبع حجم الدخل أو أسرع. لاحظ لفظة <strong>أو أسرع</strong>، لقد وضعناها لأنّنا استخدمنا Big-O بدلاً عن Big-Theta، ذلك أنّه من الشائع أن ترى الناس تكتب Big-O، رغم أنّهم يعنون Big-Theta.
</p>

<p>
	ونحن نأخذ الحالة الأسوأ في حسابنا عادة عند عدّ العمليات، فإن كانت حلقة تكرارية تُنفَّذ n مرّةً على الأكثر وكانت تحتوي على 5 عمليات، فإنّ عدد العمليات المُقدَّر سيكون 5n. كذلك من الممكن مراعاة متوسط تعقيد الحالة.
</p>

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

	<p>
		<strong>ملاحظة سريعة</strong>: الخوارزمية السريعة هي تلك التي تنفذ عمليات قليلة، فكلما اقترب عدد العمليات المُنفّذة إلى اللانهاية بسرعة أكبر كانت الخوارزمية أبطأ، مثلًا: O (n)‎‎ أسرع من O (n ^ 2)<code>‎‎</code> لأنّ الدالة n تؤول إلى اللانهاية أبطأ من <code>n^2</code>.
	</p>
</blockquote>

<p>
	يمكن أن نأخذ مساحة التخزين بالحسبان كذلك، وهو ما يُسمّى تعقيد المساحة space complexity للخوارزمية، ذلك أن الوقت ليس المورد الوحيد المهم، وفي هذه الحالة نحسُب عدد البايتات التي تشغلها الخوارزمية في الذاكرة كدالة لحجم الدخل، ونستخدم Big-O كما في حالة تعقيد الوقت.
</p>

<h2>
	مثال عن حلقة بسيطة Simple Loop
</h2>

<p>
	الدالة التالية تبحث عن أكبر عنصر في المصفوفة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4922_7" style="">
<span class="kwd">int</span><span class="pln"> find_max</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">array</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> len</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="kwd">int</span><span class="pln"> max </span><span class="pun">=</span><span class="pln"> INT_MIN</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">size_t</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"> len</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">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">max </span><span class="pun">&lt;</span><span class="pln"> array</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"> array</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"> max</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	حجم الدخل هو حجم المصفوفة، والذي سمّيناه <code>‎len‎</code> في الشيفرة أعلاه. دعنا الآن نَعُد العمليات.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4922_9" style="">
<span class="kwd">int</span><span class="pln"> max </span><span class="pun">=</span><span class="pln"> INT_MIN</span><span class="pun">;</span><span class="pln">
</span><span class="typ">size_t</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></pre>

<p>
	تُنفَّذ هاتان العمليتان مرةً واحدة، لذا فلدينا عمليتان هنا. والآن نعدّ عمليات الحلقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4922_13" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">max </span><span class="pun">&lt;</span><span class="pln"> array</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">
max </span><span class="pun">=</span><span class="pln"> array</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span></pre>

<p>
	لمّا كانت هناك 3 عمليات في الحلقة، وكانت الحلقة تُنفَّذ n مرة، فسنضيف <code>‎3n‎</code> إلى 2 (العمليتان اللتان حسبناهما من قبل)، ليبلغ الإجمالي <code>‎3n + 2‎</code>.
</p>

<p>
	تجري دالتنا إذن عددًا من العمليات مقداره <code>‎3n + 2‎</code> عملية للعثور على أكبر عنصر في المصفوفة (تعقيدها يساوي <code>‎3n + 2‎</code>). وهذا يعرف بتعددية الحدود polynomial، وأسرع عواملها نموّا هو العامل n، لذا يساوي تعقيدها O (n)‎‎.
</p>

<p>
	لعلّك لاحظت أنّ طريقة عدّ العمليات ليست دقيقة، فقد قلنا مثلًا أنّ <code> (max &lt; array)‎</code> هي عملية واحدة، ولكن اعتمادًا على هندسة الحاسوب فقد تنطوي هذه العبارة على تعليمتين مثلاً، الأولى لقراءة الذاكرة والثانية للمقارنة. وقد اعتبرنا كذلك أنّ جميع العمليات متشابهة رغم أنّ العمليات التي تخصّ الذاكرة على سبيل المثال تكون أبطأ من العمليات الأخرى، كما يختلف أداؤها اختلافًا كبيرًا بسبب تأثيرات التخزين المؤقت cache.
</p>

<p>
	كذلك تجاهلنا أيضًا تعليمة الإعادة <code>return</code> وحقيقةَ إنشاء إطار frame خاص بالدالة، وما إلى ذلك من الأمور الجانبية. لكنّ هذا لن يؤثّر في النهاية على تحليل التعقيد، ذلك أنّه مهما كانت طريقة عدّ العمليات، فلن يؤثّر إلا على معامل العامل n وكذلك لن يؤثر على الثابت، لذا ستظل النتيجة O (n)‎‎. ويُظهر التعقيد كيف تتطوّر الخوارزمية مع تطوّر حجم الدخل، بيْد أنّها ليست الجانب الوحيد الذي يمثّل الأداء.
</p>

<h2>
	مثال عن الحلقات المتشعبة Nested Loops
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4922_15" style="">
<span class="typ">_Bool</span><span class="pln"> contains_duplicates</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">array</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> len</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="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> len </span><span class="pun">-</span><span class="pln"> </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"> </span><span class="pun">(</span><span class="kwd">int</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"> len</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">if</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">&amp;&amp;</span><span class="pln"> array</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"> array</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">return</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="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="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تنفّذ الحلقة الداخلية عند كل تكرار عددًا ثابتًا من العمليات (بغضّ النظر عن قيمة <code>‎n‎</code>)، كما تنفّذ الحلقة الخارجية أيضًا بعض العمليات الثابتة علاوة على تنفيذ الحلقة الداخلية عدد <code>‎n‎</code> مرّة.
</p>

<p>
	أيضًا، تنفَّذ الحلقةُ الخارجية نفسها <code>‎n‎</code> مرّة، وعليه تُنفّذ العمليات داخل الحلقة الداخلية <code>‎n^2‎</code> مرّة بينما تُنفّذ عمليات الحلقة الخارجية <code>‎n‎</code> مرّة، أمّا عملية التعيين إلى <code>‎i‎</code> فتُتفّذ مرّة واحدة. وهكذا يمكن التعبير عن التعقيد بمعادلة مثل <code>‎an^2 + bn + c‎</code>، ولمّا كان المعامل الأعلى هو <code>‎n^2‎</code>، فإنّ ترميز O سيساوي <code>‎O(n^2)‎</code>.
</p>

<p>
	لعلك لاحظت أن هناك فرصة لتحسين الخوارزمية أكثر، إذ يمكن مثلًا تجنّب إجراء نفس المقارنات أكثرة من مرّة. يمكننا أن نبدأ من <code>‎i + 1‎</code> في الحلقة الداخلية نظرًا لأنّ جميع العناصر التي تسبقه قد تم التحقق منها سلفًا وقورنت مع جميع عناصر المصفوفة، بما في ذلك العنصر الموجود عند الفهرس <code>‎i + 1‎</code>. هذا يسمح لنا بحذف الاختبار <code>‎i == j‎</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4922_17" style="">
<span class="typ">_Bool</span><span class="pln"> faster_contains_duplicates</span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> </span><span class="pun">*</span><span class="pln">array</span><span class="pun">,</span><span class="pln"> </span><span class="typ">size_t</span><span class="pln"> len</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="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> len </span><span class="pun">-</span><span class="pln"> </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"> </span><span class="pun">(</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="lit">1</span><span class="pun">;</span><span class="pln"> j </span><span class="pun">&lt;</span><span class="pln"> len</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">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">array</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"> array</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">return</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="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="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كما ترى فإنّ هذا الإصدار أفضل لأنّه يُجري عمليات أقل، لكن كيف نترجم هذا في ترميز Big-O؟ حسنًا، في الإصدار الجديد، يُنفّذ مَتن الحلقة الداخلية المرات
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3436_25" style="">
<span class="pln"> </span><span class="pun">‎</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"> 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"> n</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="lit">2</span><span class="pun">‎</span><span class="pln">
</span></pre>

<p>
	لا يزال هذا التعبير كثير الحدود من الدرجة الثانية، لذا سيظلّ التعقيد مساويا لـ <code>‎O(n^2)‎</code>.
</p>

<p>
	وقد قلّلنا التعقيد إذ خفّضنا عدد العمليات إلى النصف تقريبًا، بيْد أنّنا ما زلنا في نفس صنف التعقيد من Big-O. ونحتاج إلى تقسيم عدد العمليات على مقدار يؤول إلى اللا نهاية مع <code>‎n‎</code> من أجل تقليل التعقيد إلى صنف أدنى.
</p>

<h3>
	الخوارزميات اللوغاريتمية‎‎‎‎
</h3>

<p>
	لنفترض أنّ لدينا مشكلة ذات حجم n، ولنفترض أنّ حجم مشكلتنا الأصلية ينخفض إلى النصف <code>(n / 2)</code> بعد كل خطوة من خطوات الخوارزمية. أي أنّه في كل خطوة جديدة يصير حجم المشكلة نصف ما كانت عليه في الخطوة التي قبلها:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th>
				الخطوة
			</th>
			<th>
				المشكلة
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				1
			</td>
			<td>
				n/2
			</td>
		</tr>
<tr>
<td>
				2
			</td>
			<td>
				n/4
			</td>
		</tr>
<tr>
<td>
				3
			</td>
			<td>
				n/8
			</td>
		</tr>
<tr>
<td>
				4
			</td>
			<td>
				n/16
			</td>
		</tr>
</tbody>
</table>
<p>
	تكون المشكلة محلولة حين يتعذر تقليل حجمها أكثر بعد الخروج من شرط التحقق، أي عندما تكون n مساوية للقيمة 1.
</p>

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

<ol>
<li>
		عند الخطوة k، سيساوي حجم المشكلة 1 (أي أنّ المشكلة ستكون قد حُلَّت).
	</li>
	<li>
		من جهة أخرى، نعلم أنّه عند الخطوة k، ينبغي أن يساوي حجم المشكلة العدد <code>(n/2^k)</code>.
	</li>
	<li>
		من 1، 2، إذًا n = 2k.
	</li>
	<li>
		طبّق دالة اللوغاريتم على كلا الجانبين:
	</li>
</ol>
<pre class="ipsCode">
log n = k * loge_2  →   k = loge n / loge 2
</pre>

<ol start="5">
<li>
		باستخدام المعادلة التالية:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3436_27" style="">
<span class="pln">logx </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"> logx </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"> logn </span><span class="pun">(</span><span class="pln">m</span><span class="pun">)‎</span><span class="pln">
</span></pre>

<p>
	حيث يرمز التعبير log<strong>t</strong>‎‎ إلى اللوغاريتم ذي الأساس <strong>t،</strong> وتصبح النتيجة k = log2 (n)‎‎ أو k = log n ببساطة.
</p>

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

	<p>
		نعلم الآن أنّ خوارزميتنا يمكن أن تُنفّذ log n على أقصى حدّ، وعليه يساوي تعقيد الوقت O (log n)‎‎
	</p>
</blockquote>

<p>
	انظر المثال التوضيحي التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4922_19" style="">
<span class="kwd">for</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">n</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="lit">2</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="pun">}</span></pre>

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

<p>
	سنرمز للعدد الذي نبحث عنه بالرمز k، ونحسبه على النحو الآتي:
</p>

<ul>
<li>
		k = log2 (256)‎‎.
	</li>
	<li>
		k = log2 (2^8)‎‎ ( نعلم أنّ log(a^a) = 1<code>‎</code> لكل عدد a‎).
	</li>
	<li>
		k = 8.
	</li>
</ul>
<p>
	هذا مثال آخر لتوضيح هذا النوع من الخوارزميات. وهي خوارزمية البحث الثنائي Binary Search Algorithm.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4922_21" style="">
<span class="kwd">int</span><span class="pln"> bSearch</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> arr</span><span class="pun">[],</span><span class="kwd">int</span><span class="pln"> size</span><span class="pun">,</span><span class="kwd">int</span><span class="pln"> item</span><span class="pun">){</span><span class="pln">
</span><span class="kwd">int</span><span class="pln"> low</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">int</span><span class="pln"> high</span><span class="pun">=</span><span class="pln">size</span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">while</span><span class="pun">(</span><span class="pln">low</span><span class="pun">&lt;=</span><span class="pln">high</span><span class="pun">){</span><span class="pln">               
    mid</span><span class="pun">=</span><span class="pln">low</span><span class="pun">+(</span><span class="pln">high</span><span class="pun">-</span><span class="pln">low</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">arr</span><span class="pun">[</span><span class="pln">mid</span><span class="pun">]==</span><span class="pln">item</span><span class="pun">)</span><span class="pln">                       
        </span><span class="kwd">return</span><span class="pln"> mid</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="pun">(</span><span class="pln">arr</span><span class="pun">[</span><span class="pln">mid</span><span class="pun">]&lt;</span><span class="pln">item</span><span class="pun">)</span><span class="pln">                       
        low</span><span class="pun">=</span><span class="pln">mid</span><span class="pun">+</span><span class="lit">1</span><span class="pun">;</span><span class="pln">               
    </span><span class="kwd">else</span><span class="pln">  high</span><span class="pun">=</span><span class="pln">mid</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">return</span><span class="pln"> </span><span class="pun">–</span><span class="lit">1</span><span class="pun">;</span><span class="com">// لا يوجد</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	مثال على خوارزمية من الصنف O (log n)‎‎
</h2>

<p>
	انظر المشكلة التالية:
</p>

<p>
	L قائمة مرتّبة تحتوي n عددًا صحيحًا نسبيًا (n كبيرة جدا)، على سبيل المثال ‎[-5, -2, -1, 0، 1، 2، 4]‎‎ (هنا، n تساوي 7). إذا كانت <code>‎L‎</code> تحتوي العدد الصحيح 0، فكيف يمكنك العثور على فهرس 0؟
</p>

<h3>
	المقاربة البسيطة
</h3>

<p>
	أول ما يتبادر إلى الذهن هو قراءة كل فهرس إلى حين العثور على 0، وفي أسوأ الحالات سيكون علينا إجراء <code>‎n‎</code> عملية، وعليه فإنّ التعقيد سيساوي O (n)‎‎.
</p>

<p>
	لا مشكلة في هذا مع القيم الصغيرة لـ <code>‎n‎</code>، ولكن هل هناك طريقة أفضل؟
</p>

<h4>
	مبدأ الحصار Dichotomy
</h4>

<p>
	انظر الخوارزمية التالية Python3:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4922_23" style="">
<span class="pln">a </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
b </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="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">
 h </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"> </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"> L</span><span class="pun">[</span><span class="pln">h</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="kwd">return</span><span class="pln"> h
 </span><span class="kwd">elif</span><span class="pln"> L</span><span class="pun">[</span><span class="pln">h</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">
   b </span><span class="pun">=</span><span class="pln"> h
 </span><span class="kwd">elif</span><span class="pln"> L</span><span class="pun">[</span><span class="pln">h</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">
   a </span><span class="pun">=</span><span class="pln"> h</span></pre>

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

	<p>
		(*) تمثل <code>//</code> عملية القسمة الصحيحة integer division، لذا ستكون h عددًا صحيحًا. يحصركل من a و b فهرس العدد 0، فلا بدّ لفهرس 0 أن يكون محصورًا بينهما، وكل مرة ندخل إلى الحلقة، نختار فهرسًا بين <code>a</code>و<code>‎b‎</code> ونستخدمه لتضييق المنطقة المراد البحث عنها.
	</p>
</blockquote>

<p>
	وفي أسوأ الحالات، علينا الانتظار حتى يتساوى <code>‎a‎</code> و<code>‎b‎</code>، لكن كم عدد العمليات التي يتطلبها ذلك؟ قطعًا ليس n لأننا نقسم المسافة بين <code>‎a‎</code> و<code>‎b‎</code> على 2 في كل مرة ندخل إلى الحلقة، لذا فالتعقيد يساوي O (log n)‎‎.
</p>

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

	<p>
		<strong>تنبيه</strong>: حين نكتب log فإننا نشير إلى اللوغاريتم الثنائي binary logarithm أو اللوغاريتم ذي الأساس 2، والذي يُكتب عادةً <code>log_2</code> أو log2، لذا فإنّ O (log_2 n)<code>‎‎</code> و O (log)‎‎ متكافئتان.
	</p>
</blockquote>

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

<p>
	ترجمة -بتصرّف- للفصل الثالث من كتاب <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/advance/%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="">تعقيد الخوارزميات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advance/%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>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advance/%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>
	<li>
		<a href="https://academy.hsoub.com/files/17-%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%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-%D9%88%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A2%D9%84%D8%A9/" rel="">مدخل إلى الذكاء الاصطناعي وتعلم الآلة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1290</guid><pubDate>Tue, 10 Aug 2021 15:00:00 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x642;&#x64A;&#x62F; &#x627;&#x644;&#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x627;&#x62A; Algorithms Complexity</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/610ba471e56d6_2(2).png.e952a3b3fe7eb2058bb2ae1fd279bebc.png" /></p>

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

<h2>
	ترميز Big-Omega
</h2>

<p>
	تُستخدم ترميز Ω -notation لتمثيل المقارِب الأدنى asymptotic lower bound.
</p>

<h3>
	التعريف الرسمي
</h3>

<p>
	لتكن <code>‎fn)‎</code> و <code>‎g(n)‎</code> دالتين مُعرّفتين على مجموعة الأعداد الحقيقية الموجبة، فنقول أنّ <code>‎f(n) = Ω(g(n))‎</code> في حال وُجِدت ثابتان موجبان <code>‎c‎</code> و <code>‎n0‎</code> يحققان الآتي لكل n أكبر من أو تساوي n0:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_792_7" style="">
<span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">≤</span><span class="pln"> c 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"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)‎‎</span></pre>

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

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			<strong>ملاحظة</strong>: المعادلة <code>f(n) = Ω(g(n))‎</code> تعني أنّ <code>f(‎n‎)‎</code> لا يمكن أن تنمو تقاربيًا asymptotically بشكل أبطأ من <code>‎g(n)‎</code>، كذلك يمكن كتابة <code>‎Ω(‎ g(‎ n ‎))‎</code> عندما لا يؤكّد تحليل الخوارزمية أنّ <code>‎Θ(g(n))‎</code> أو / <code>‎O(g(n))‎</code>.
		</p>
	</div>
</blockquote>

<h3>
	نظرية
</h3>

<p>
	بالنسبة لدالتين <code>‎f(n)‎</code> و <code>‎g(n)‎</code>، نقول أنّ <code>‎f(n) = Ө(g(n))‎</code> فقط إذا كان الآتي محققًا:
</p>

<ul>
<li>
		<code>f(n) = O(g(n))‎‎.</code>
	</li>
	<li>
		<code>f(n) = Ω(g(n))‎</code>.
	</li>
</ul>
<p>
	يمكن تمثيل ترميز <code>‎Ω‎</code> بيانيًا على النحو التالي:
</p>

<p style="text-align: center;">
	<img alt="5qDtj.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74367" data-unique="559qhse2g" src="https://academy.hsoub.com/uploads/monthly_2021_08/5qDtj.png.c52b81915d41b7a5b199c3f2ae4d97dc.png" style=""></p>

<p>
	على سبيل المثال، إذا كانت <code>‎</code><br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_792_9" 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="lit">3n</span><span class="pun">^</span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">5n</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">4</span><span class="pun">‎</span><span class="pln">
</span></pre>

<p>
	فستكون <code>‎f(n) = Ω(n^2)‎</code>، كما ستكون <code>‎f(n) = Ω(n)‎</code> أو حتى ‎‎<code>f(n) = Ω(1)‎‎‎</code>‎ صحيحة.
</p>

<p>
	لدينا مثال آخر يحل خوارزمية التطابق التام matching algorithm، حيث إذا كان عدد الرؤوس vertices فرديًا، فسنحصل على الخرج "No Matching Matching"، وإلا فينبغي تجربة جميع التطابقات الممكنة.
</p>

<p>
	وكنا نودّ القول أنّ الخوارزمية تتطلب وقتًا أسيًا exponential time، لكن الواقع أنه لا يمكنك إثبات وجود حدّ أدنى <code>‎Ω(n^2)‎</code> باستخدام التعريف المعتاد لـ <code>‎Ω‎</code> نظرًا لأنّ الخوارزمية تُجرى في وقت خطي linear time لقيم n الفردية.
</p>

<p>
	وبدلًا من هذا فيجب علينا تعريف <code>‎f(n)=Ω(g(n))‎</code> على نحو أنه بالنسبة لثابت <code>‎‎</code>c قيمته أكبر من الصفر، فهناك عدد لا نهائي من قيم <code>‎n‎</code> التي تحقّق الآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_792_11" 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"> c g</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span></pre>

<p>
	<code>‎</code>
</p>

<p>
	يوفّق هذا التعريف بين الحدّين الأعلى والأدنى إذ يحقّق التكافؤ الآتي:<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_792_13" 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">g</span><span class="pun">(</span><span class="pln">n</span><span class="pun">))‎</span><span class="pln">
</span></pre>

<p>
	فقط إن كان
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_792_15" 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"> o</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></pre>

<h2>
	ترميز Big-Theta
</h2>

<p>
	على خلاف ترميز Big-O الذي يمثل الحد الأعلى upper bound فقط من وقت تشغيل الخوارزميات، فإنّ ترميز Big-Theta هو رابط محصور Tight bound يشمل الحدّ العلوي والسفلي معًا، وهو أكثر دقة لكنه صعب الحساب.
</p>

<p>
	وترميز Big-Theta متماثلة، أي تحقق المعادلة:<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2848_7" style="">
<span class="pun">‎</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">g</span><span class="pun">(</span><span class="pln">x</span><span class="pun">))</span><span class="pln"> </span><span class="pun">&lt;=&gt;</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"> </span><span class="pun">Ө(</span><span class="pln">f</span><span class="pun">(</span><span class="pln">x</span><span class="pun">))‎</span><span class="pln">
</span></pre>

<p>
	 ولتفهم ذلك بسهولة، اعلم أن المعادلة الآتية<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2848_9" style="">
<span class="pun">‎</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">g</span><span class="pun">(</span><span class="pln">x</span><span class="pun">))</span><span class="pln"> </span><span class="pun">&lt;=&gt;</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"> </span><span class="pun">Ө(</span><span class="pln">f</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">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">g</span><span class="pun">(</span><span class="pln">x</span><span class="pun">))‎</span><span class="pln">
</span></pre>

<p>
	تعني أنّ الرسمين البيانيين للدالتين f و g ينمُوان بنفس المعدّل، أو أنّ الرسمين البيانيين "يتصرّفان" بشكل متماثل عند قيم x الكبيرة.
</p>

<p>
	والتعبير الرياضي الكامل لترميز Big-Theta هو كما يلي:
</p>

<pre class="ipsCode">
Ө(f(x)) = {g: N0 -&gt; R and c1, c2, n0 &gt; 0}
</pre>

<p>
	حيث تكون
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_792_17" style="">
<span class="pln">c1 </span><span class="pun">&lt;</span><span class="pln"> abs</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"> f</span><span class="pun">(</span><span class="pln">n</span><span class="pun">))‎‎</span><span class="pln">
</span></pre>

<p>
	لكل n أكبر من n0، وتمثل abs القيمة المطلقة.
</p>

<p>
	<strong>ﻣﺜــــﺎل</strong>
</p>

<p>
	إذا كانت الخوارزمية تأخذ العدد الآتي عمليةً للانتهاء مقابل مُدخل <code>‎n‎</code>، فنقول أنّ تعقيدها يساوي <code>‎O(n^2)‎</code> ، كما يساوي أيضًا <code>‎O(n^3)‎</code> و<code>O(n^100)‎.</code> <br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_792_19" style="">
<span class="pun">‎</span><span class="lit">42n</span><span class="pun">^</span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">25n</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">4</span><span class="pun">‎</span><span class="pln">
</span></pre>

<p>
	أمّا في حال ترميز Big-Theta، فنقول أنّ التعقيد يساوي <code>‎Ө(n^2)‎</code>، لكن لا يساوي <code>‎Ө(n^3)‎</code> أو <code>‎Ө(n^4)‎</code>، إلخ… . كذلك، فإن كانت الخوارزمية من نوع <code>‎Ө(f(n))‎</code>، فستكون أيضًا من النوع <code>‎O(f(n))‎</code>، ولكن ليس العكس.
</p>

<h3>
	التعريف الرياضي
</h3>

<p>
	تُعرّف <code> Ө(g(x))‎‎</code> على أنها مجموعة من الدوال، أما تعريفها الرياضي الرسمي Formal mathematical definition فهو:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_792_23" style="">
<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"> f</span><span class="pun">(</span><span class="pln">x</span><span class="pun">)}</span><span class="pln">
</span></pre>

<p>
	وإذا كانت هناك ثوابت موجبة N وc1 و c2، بحيث يكون
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2848_11" style="">
<span class="lit">0</span><span class="pun">‎‎</span><span class="pln"> </span><span class="pun">&lt;=</span><span class="pln"> c1</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="pln"> f</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="pln"> c2g</span><span class="pun">*(</span><span class="pln">x</span><span class="pun">)‎‎</span><span class="pln">
</span></pre>

<p>
	 لكل x أكبر من N}.
</p>

<p>
	وهذا يعني أنّ <code>Ө(g(x))‎‎</code> هي مجموعةٌ تحتوي كلّ دالة f مُحاصَرة من قبل الدالة g، بمعنى أنّه توجد 3 ثوابت موجبة هي c1 و c2 وN، بحيث يكون لدينا: <code>0‎‎ &lt;= c1*g(x) &lt;= f(x) &lt;= c2*g(x)‎‎</code> لكل عدد x أكبر من N.
</p>

<p>
	وبما أن <code>‎Ө(g(x))‎</code> مجموعةً فيمكننا كتابة الآتي <code>‎‎</code> للإشارة إلى أنّ <code>‎f(x)‎</code> تنتمي إلى <code>‎Ө(g(x))‎.</code> 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_792_25" style="">
<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">g</span><span class="pun">(</span><span class="pln">x</span><span class="pun">))</span><span class="pln">
</span></pre>

<p>
	غير أن الشائع هو كتابة الآتي للتعبير عن نفس الترميز:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_792_27" style="">
<span class="pln"> </span><span class="pun">‎</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">g</span><span class="pun">(</span><span class="pln">x</span><span class="pun">))‎</span><span class="pln">
</span></pre>

<p>
	ويمكن تفسير <code>‎Ө(g(x))‎</code> عندما تظهر في ترميز ما على أنّها كناية عن دالة مجهولة لا تهمّنا تسميتها، فمثلًا، المعادلة الآتية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2848_13" style="">
<span class="pun">‎</span><span class="pln">T</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"> T</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">n</span><span class="pun">)‎</span><span class="pln">
</span></pre>

<p>
	 تكافئ<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2848_15" style="">
<span class="pun">‎</span><span class="pln">T</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"> T</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="pln">
</span></pre>

<p>
	 حيث تمثّل <code>‎f(n)‎</code> دالةً ما من المجموعة <code>‎Ө(n)‎ا</code>
</p>

<p>
	وإن كانت <code>‎f‎</code> و <code>‎g‎</code> دالتين مُعرَّفتين على نفس النطاق من مجموعة الأعداد الحقيقية real numbers، فإننا نكتب  الآتي:<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_792_29" style="">
<span class="pun">‎</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">g</span><span class="pun">(</span><span class="pln">x</span><span class="pun">))‎</span><span class="pln">
</span></pre>

<p>
	عندما تؤول x إلى ما لا نهاية x-&gt;infinity، وذلك فقط في حالة وجود ثابتْين موجبيْن <code>K</code> و <code>L</code> وعدد حقيقي <code>x0</code> يحققون<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2848_17" style="">
<span class="pln">K</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="pln"> f</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="pln"> L</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></pre>

<p>
	لكل x أكبر من أو تساوي x0.
</p>

<p>
	يكون التعريف مكافئًا للعبارة التالية:
</p>

<pre class="ipsCode" id="ips_uid_2848_21">
f(x) = O(g(x))‎‎
</pre>

<p>
	والعبارة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2848_23" style="">
<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">g</span><span class="pun">(</span><span class="pln">x</span><span class="pun">))‎‎</span><span class="pln"> 
</span></pre>

<h3>
	استخدام مفهوم النهايات limits
</h3>

<p>
	إذا كان لدينا<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2848_19" style="">
<span class="pln">limit</span><span class="pun">(</span><span class="pln">x</span><span class="pun">-&gt;</span><span class="pln">infinity</span><span class="pun">)</span><span class="pln"> f</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">=</span><span class="pln"> c </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></pre>

<p>
	 أي أنّ النهاية موجودة وموجبة، أي:<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_792_31" style="">
<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">g</span><span class="pun">(</span><span class="pln">x</span><span class="pun">))</span><span class="pln">
</span></pre>

<p>
	<code>‎</code> وفيما يلي بعض أصناف التعقيد الشائعة:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th>
				الاسم
			</th>
			<th>
				الترميز
			</th>
			<th>
				n = 10
			</th>
			<th>
				n = 100
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				Constant - ثابت
			</td>
			<td>
				Ө(1)
			</td>
			<td>
				1
			</td>
			<td>
				1
			</td>
		</tr>
<tr>
<td>
				Logarithmic - لوغارتمي
			</td>
			<td>
				Ө(log(n))
			</td>
			<td>
				3
			</td>
			<td>
				7
			</td>
		</tr>
<tr>
<td>
				Linear - خطي
			</td>
			<td>
				Ө(n)
			</td>
			<td>
				10
			</td>
			<td>
				100
			</td>
		</tr>
<tr>
<td>
				Linearithmic - لوغارتمي-خطي
			</td>
			<td>
				Ө(n*log(n))
			</td>
			<td>
				30
			</td>
			<td>
				700
			</td>
		</tr>
<tr>
<td>
				Quadratic - تربيعي
			</td>
			<td>
				Ө(n^2)
			</td>
			<td>
				100
			</td>
			<td>
				10000
			</td>
		</tr>
<tr>
<td>
				Exponential - أسّي
			</td>
			<td>
				Ө(2^n)
			</td>
			<td>
				1024
			</td>
			<td>
				1.267650e+ 30
			</td>
		</tr>
<tr>
<td>
				Factorial - مُعاملي
			</td>
			<td>
				Ө(n!)
			</td>
			<td>
				3 628 800
			</td>
			<td>
				9.332622e+157
			</td>
		</tr>
</tbody>
</table>
<h2>
	موازنة الصيغ المقاربة asymptotic notations
</h2>

<p>
	لتكن <code>‎f(n)‎</code> و <code>‎g(n)‎</code> دالتين مُعرّفتين على مجموعة الأعداد الحقيقية الموجبة، ولتكن <code>‎c, c1, c2, n0‎</code> ثوابت حقيقية موجبة.
</p>

<p>
	يوضّح الجدول التالي الفروق بين مختلف الصيغ:
</p>

<table>
<thead><tr>
<th>
				الترميز
			</th>
			<th>
				f(n) = O(g(n))
			</th>
			<th>
				f(n) = Ω(g(n))
			</th>
			<th>
				f(n) = Θ(g(n))
			</th>
			<th>
				f(n) = o(g(n))
			</th>
			<th>
				f(n) = ω(g(n))
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				التعريف الرسمي
			</td>
			<td>
				∃ c &gt; 0, ∃ n0 &gt; 0 : ∀ n ≥ n0, 0 ≤ f(n) ≤ c g(n)
			</td>
			<td>
				∃ c &gt; 0, ∃ n0 &gt; 0 : ∀ n ≥ n0, 0 ≤ c g(n) ≤ f(n)
			</td>
			<td>
				∃ c1, c2 &gt; 0, ∃ n0 &gt; 0 : ∀ n ≥ n0, 0 ≤ c1 g(n) ≤ f(n) ≤ c2 g(n)
			</td>
			<td>
				∀ c &gt; 0, ∃ n0 &gt; 0 : ∀ n ≥ n0, 0 ≤ f(n) &lt; c g(n)
			</td>
			<td>
				∀ c &gt; 0, ∃ n0 &gt; 0 : ∀ n ≥ n0, 0 ≤ c g(n) &lt; f(n)
			</td>
		</tr>
<tr>
<td>
				التشابه مع الموازنة بين عددين a وb
			</td>
			<td>
				a ≤ b
			</td>
			<td>
				a ≥ b
			</td>
			<td>
				a = b
			</td>
			<td>
				a &lt; b
			</td>
			<td>
				a &gt; b
			</td>
		</tr>
<tr>
<td>
				أمثلة
			</td>
			<td>
				7n + 10 = O(n^2 + n - 9)
			</td>
			<td>
				n^3 - 34 = Ω(10n^2 - 7n + 1)
			</td>
			<td>
				1/2 n^2 - 7n = Θ(n^2)
			</td>
			<td>
				5n^2 = o(n^3)
			</td>
			<td>
				7n^2 = ω(n)
			</td>
		</tr>
<tr>
<td>
				<table><tbody><tr>
<td>
								الرسوم البيانية
							</td>
							<td>
								 
							</td>
						</tr></tbody></table>
</td>
			<td>
				<table><tbody><tr>
<td>
								<img alt="AkEKr.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74364" data-unique="y8rp00f9a" src="https://academy.hsoub.com/uploads/monthly_2021_08/AkEKr.png.a32a246a9fbb40b8f86e240bf92d7bab.png" style="">
</td>
							<td>
								 
							</td>
						</tr></tbody></table>
</td>
			<td>
				<table><tbody><tr>
<td>
								<img alt="5qDtj.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74365" data-unique="ok7ht4zp8" src="https://academy.hsoub.com/uploads/monthly_2021_08/5qDtj.png.b186d4fa5252c945306eaa30983b1081.png" style="">
</td>
							<td>
								 
							</td>
						</tr></tbody></table>
</td>
			<td>
				<table><tbody><tr>
<td>
								<img alt="RPdzC.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74366" data-unique="9gryi6m6u" src="https://academy.hsoub.com/uploads/monthly_2021_08/RPdzC.png.9f8e91164f99d575cbe23e6f0d66f109.png" style="">
</td>
							<td>
								 
							</td>
						</tr></tbody></table>
</td>
			<td>
				 
			</td>
			<td>
				 
			</td>
		</tr>
</tbody>
</table>
<p>
	يمكن تمثيل الصيغ المُقارِبة بواسطة مخطّط فِن كما يلي:
</p>

<p style="text-align: center;">
	<img alt="v2eH3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74363" data-unique="m8nkphdvq" src="https://academy.hsoub.com/uploads/monthly_2021_08/v2eH3.png.f7e968380c61c78fc7d7c5cd1fb4316d.png" style=""></p>

<p>
	ترجمة -بتصرّف- للفصل الثاني من كتاب <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/advance/%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>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advance/%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>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%B5%D9%86%D9%81-%D8%A8%D8%A7%D9%84%D8%A7%D8%B9%D8%AA%D9%85%D8%A7%D8%AF-%D8%B9%D9%84%D9%89-%D8%B7%D8%B1%D9%82-%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A2%D9%84%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-scikit-learn-r1266/" rel="">بناء مصنف بالاعتماد على طرق تعلم الآلة بلغة بايثون باستخدام مكتبة Scikit-Learn</a>
	</li>
	<li>
		النسخة الكامة من كتاب <a href="https://academy.hsoub.com/files/17-%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%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-%D9%88%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A2%D9%84%D8%A9/" rel="">مدخل إلى الذكاء الاصطناعي وتعلم الآلة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1284</guid><pubDate>Thu, 05 Aug 2021 15:03:00 +0000</pubDate></item><item><title>&#x645;&#x62F;&#x62E;&#x644; &#x625;&#x644;&#x649; &#x627;&#x644;&#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x627;&#x62A;</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/61065fbd7bf00_1(1).png.80bf825a3bffc971a8f3e3a1aadcf68e.png" /></p>
<p>
	نتعرف في مقال اليوم على مفهوم الخوارزميات Algorithms التي تعد واحدة من المفاهيم الأساسية المستخدمة في مجال الرياضيات والبرمجة وعلوم الحاسوب والعديد من المجالات المختلفة الأخرى كما يمكننا تطبيقها في حل مشكلات حياتنا اليومية، ونكتشف أهم خصائصها وطرق كتابتها والتعبير عنها وطريقة تحويلها إلى كود برمجي. كما نوفر في ختام المقال أهم النصائح التي تساعدك في تعلم الخوارزميات بالطريقة الصحيحة.
</p>

<h2>
	ما هي الخوارزميات Algorithms
</h2>

<p style="text-align: center;">
	<img alt="ما-هي-الخوارزمية.png" class="ipsImage ipsImage_thumbnailed" data-fileid="135823" data-ratio="65.20" data-unique="8ldzlhswj" style="width: 500px; height: auto;" width="500" src="https://academy.hsoub.com/uploads/monthly_2023_09/--.thumb.png.641cb6a0df0ea6545ad23ae4a36b5ac1.png">
</p>

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

<p>
	فهناك خوارزميات تستخدم في مجال الرياضيات مثل خوارزمية خوارزمية إقليدس <a href="https://ar.wikipedia.org/wiki/%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A9_%D8%A3%D9%82%D9%84%D9%8A%D8%AF%D8%B3" rel="external nofollow">Euclidean algorithm</a> وخوارزمية غربال إراتوستينس <a href="https://ar.wikipedia.org/wiki/%D8%BA%D8%B1%D8%A8%D8%A7%D9%84_%D8%A5%D8%B1%D8%A7%D8%AA%D9%88%D8%B3%D8%AA%D9%8A%D9%86%D8%B3" rel="external nofollow">Sieve of Eratosthenes</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%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/" rel="">Sorting algorithms</a> التي تساعدك على ترتيب مجموعة من العناصر وفق ترتيب تصاعدي، ويفيدنا الترتيب في حل الكثير من المشكلات الحاسوبية بسهولة أكبر على سبيل المثال البحث عن اسم ما في سلسلة عناصر مرتبة وفق التسلسل الأبجدي أسرع وأسهل من البحث في سلسلة عناصر غير مرتبة لذا من الأفضل أن ترتب العناصر قبل البحث فيها.
</p>

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

<ul>
	<li>
		املأ الركوة أو دلة القهوة بالماء.
	</li>
	<li>
		سخّن الماء.
	</li>
	<li>
		انتظر حتى يغلي الماء.
	</li>
	<li>
		أضف القهوة وحركها.
	</li>
	<li>
		اسكب القهوة في الكوب.
	</li>
</ul>

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

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

<h2>
	خصائص الخوارزميات
</h2>

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

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

<h2>
	طرق تمثيل الخوارزمية
</h2>

<p>
	يمكن تمثيل الخوارزمية بطرق مختلفة تصف من خلالها كيفية حل المشكلة، وفيما يلي نوضح عدة طرق ممكنة لتمثيل الخوارزمية:
</p>

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

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

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

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

<p style="text-align: center;">
	<img alt="مخطط-تدفقي-لخوارزمية-صنع-كوب-قهوة.png" class="ipsImage ipsImage_thumbnailed" data-fileid="135824" data-unique="uhat0fzt8" src="https://academy.hsoub.com/uploads/monthly_2023_09/-----.thumb.png.354fbef5dc0bd9a594365fe5d5e01469.png">
</p>

<h2>
	خطوات كتابة الخوارزمية
</h2>

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

<p>
	على سبيل المثال تُعرَّف مشكلة خوارزمية الفرز أو الترتيب sorting على النحو التالي:
</p>

<ul>
	<li>
		<strong>المشكلة</strong>: الترتيب.
	</li>
	<li>
		<strong>المدخلات</strong>: تسلسل من عدد n من المفاتيح <code>‎a_1, a_2, ..., a_n‎</code>
	</li>
	<li>
		<strong>الخرج</strong>: إعادة ترتيب تسلسل المدخلات بحيث يكون لدينا:
	</li>
</ul>

<pre class="ipsCode">b_1, b_2,&lt;= ... &lt;= b_{n-1}, b_n
</pre>

<p>
	يمكن أن تعمل خوارزمية الفرز على عدة حالات instances، على سبيل المثال يمكن أن يكون دخل هذه الخوارزمية عبارة عن مصفوفة من السلاسل النصية التي نحتاج لترتيبها أبجديًا مثل <code>{Haskell, Emacs}</code> أو يكون عبارة عن تسلسل من الأرقام التي نريد ترتيبها تصاعديًا مثل <code>{154، 245، 1337}</code>.
</p>

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

<h2>
	مثال على كتابة خوارزمية Fizz Buzz
</h2>

<p>
	سنوضح في هذه الفقرة كيفية حل مسألة Fizz Buzz وهي خوارزمية معروفة تحل مشكلة بسيطة ويطلب منك حلها في معظم المقابلات البرمجية والهدف منها طباعة مجموعة من الأعداد الصحيحة من واحد إلى N ولكنك ستطبع كلمة Fizz إذا كان العدد قابلاً للقسمة على 3، و كلمة Buzz إذا كان العدد قابلاً للقسمة على 5، وكلمة Fizzbuzz إذا كان العدد قابلاً للقسمة على العددين 3 و 5 بذات الوقت.
</p>

<p>
	يشير المعنى الحرفي لهاتين الكلمتين إلى الصوت التي تحدثه كل كلمة منهما، ويرجع أصل استخدامها إلى لعبة أطفال شهيرة تُسمّى <a href="https://en.wikipedia.org/wiki/Fizz_buzz" rel="external nofollow">ﬁzz buzz</a> تقوم على نفس المبدأ وتستخدم لتعلم الأطفال عملية القسمة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="135826" href="https://academy.hsoub.com/uploads/monthly_2023_09/-FIZZBUZZ.png.c1a2c758af1e54cc4bb362812550c671.png" rel=""><img alt="خوارزمية-FIZZBUZZ.png" class="ipsImage ipsImage_thumbnailed" data-fileid="135826" data-ratio="65.00" data-unique="got1zt75r" style="width: 500px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2023_09/-FIZZBUZZ.thumb.png.6abaf8e4983863066baafcc09f477387.png"></a>
</p>

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

<p>
	بفرض أن دخل الخوارزمية هو سلسلة الأرقام التالية:
</p>

<pre class="ipsCode">1 2 3 4 5 6 7 8 9 10
</pre>

<p>
	كما وضحنا سابقًا، يشير المصطلحَان Fizz و Buzz إلى أيّ عدد مضاعف للعدد 3 أو 5 على الترتيب، أي إذا كان العدد يقبل القسمة على 3 فيمكن استبداله بالكلمة Fizz، وإذا كان قابلاً للقسمة على 5 فيمكن استبداله بكلمة Buzz. أما إذا كان من مضاعفات 3 و 5 بذات الوقت فيُستبدَل بكلمة FzzBuzz أي سيكون الخرج كما هو مبين في الصورة التالية:
</p>

<p>
	يمكن التعبير عن الخوارزمية بشكل كود زائف كما يلي:
</p>

<ul>
	<li>
		ابدأ.
	</li>
	<li>
		قم بتكرار الأعداد من 1 إلى 10.
	</li>
	<li>
		تحقق مما إذا كان العدد الحالي قابلاً للقسمة على 3.
	</li>
	<li>
		إذا كان الجواب صحيح اطبع الكلمة ﬁzz.
	</li>
	<li>
		إذا لم يكن الجواب صحيح، انتقل إلى الخطوة التالية.
	</li>
	<li>
		تحقق فيما إذا كان العدد الحالي قابلاً للقسمة على 5.
	</li>
	<li>
		إذا كان الجواب صحيح اطبع الكلمة buzz.
	</li>
	<li>
		إذا لم يكن الجواب صحيح، اعرض العدد الحالي.
	</li>
	<li>
		كرر الحلقة حتى تصل إلى الرقم 10.
	</li>
	<li>
		النهاية.
	</li>
</ul>

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

<h2>
	تنفيذ خوارزمية Fizz Buzz من خلال لغة البرمجة Swift
</h2>

<p>
	بعد أن فهمت الخوارزمية ستتمكن من تنفيذ هذه <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> بسهولة وتحويلها لبرنامج حاسوبي، افتح محرر الأكواد البرمجية Xcode أو VS code أو أي محرر آخر تفضله لكتابة برنامج جديد، يبدأ البرنامج بتعلمية تهيئة مصفوفة من 1 إلى 10
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6902_11" style=""><span class="kwd">let</span><span class="pln"> number  </span><span class="pun">=</span><span class="pln"> </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="lit">4</span><span class="pun">,</span><span class="lit">5</span><span class="pun">,</span><span class="lit">6</span><span class="pun">,</span><span class="lit">7</span><span class="pun">,</span><span class="lit">8</span><span class="pun">,</span><span class="lit">9</span><span class="pun">,</span><span class="lit">10</span><span class="pun">]</span></pre>

<p>
	نريد أن نعالج مصفوفة الدخل والحصول على الخرج المطلوب، أي نريد أن نستبدل 3 بالكلمة fizz هنا و 5 بالكلمة buzz كما شرحنا سابقًا. ولتحقيق ذلك نمرّ على جميع عناصر المصفوفة والتحقق من كل عنصر من عناصرها ولذا سننشئ حلقة for تمرّ عبر هذه العناصر كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6902_15" style=""><span class="kwd">for</span><span class="pln"> num in number </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// الحسابات هنا</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بعد هذا، سنستخدم العبارة الشرطية if else، وعامل باقي القسمة module operator في لغة swift أي <code>%</code> لتحديد مواقع ﬁzz و buzz كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6902_17" style=""><span class="kwd">for</span><span class="pln"> num in number </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> num </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">0</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    print</span><span class="pun">(</span><span class="str">"\(num) fizz"</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">
    print</span><span class="pun">(</span><span class="pln">num</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">1
2
3 fizz
4
5
6 fizz
7
8
9 fizz
10
</pre>

<p>
	سنضيف الآن الجزء المتعلق بالكلمة Buzz مستخدمين نفس الآلية التي اتبعناها في كتابة الكود السابق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7008_6" style=""><span class="kwd">for</span><span class="pln"> num in number </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> num </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">0</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    print</span><span class="pun">(</span><span class="str">"\(num) fizz"</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"> num </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="lit">0</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    print</span><span class="pun">(</span><span class="str">"\(num) buzz"</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">
    print</span><span class="pun">(</span><span class="pln">num</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>

<pre class="ipsCode" id="ips_uid_6902_19">1
2
3 fizz
4
5 buzz
6 fizz
7
8
9 fizz
10
</pre>

<p>
	سنزيد عناصر المصفوفة إلى 1-15. لاحظ أنّه بما أن 15 مضاعف لكلّ من 3 و 5، فينبغي استبدالها بـ ﬁzz buzz:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7008_8" style=""><span class="kwd">for</span><span class="pln"> num in number </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> num </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">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> num </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="lit">0</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    print</span><span class="pun">(</span><span class="str">"\(num) fizz buzz"</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"> num </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">0</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    print</span><span class="pun">(</span><span class="str">"\(num) fizz"</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"> num </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="lit">0</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    print</span><span class="pun">(</span><span class="str">"\(num) buzz"</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">
    print</span><span class="pun">(</span><span class="pln">num</span><span class="pun">)</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لا تزال لدينا مشكلة قائمة في الكود أعلاه، فالغرض الأساسي من الخوارزمية هو ترشيد وقت التنفيذ. تخيّل لو زدنا نطاق المصفوفة من 1-15 إلى 1-100، سيكون على <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="">مُصرِّف اللغة</a> فحص كل عدد على حدة لتحديد ما إذا كان قابلاً للقسمة على 3 أو 5، ثمّ سيمرّ على الأعداد ثانيةً للتحقق ممّا إذا كانت قابلة للقسمة على 3 و 5 (معًا)، كما سيتعيّن على الشيفرة أن تمرّ على كلّ عدد في المصفوفة مرّتين لأنّها ستتحقق من قابلية قسمة العدد على 3 أولًا، ثمّ على 5.
</p>

<p>
	ولتسريع هذه العملية، يمكن أن نأمر المصرّف بقسمة الأعداد على 15 مباشرة. انظر الشيفرة النهائية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6902_21" style=""><span class="kwd">for</span><span class="pln"> num in number </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> num </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">0</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    print</span><span class="pun">(</span><span class="str">"\(num) fizz buzz"</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"> num </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">0</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    print</span><span class="pun">(</span><span class="str">"\(num) fizz"</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"> num </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="lit">0</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    print</span><span class="pun">(</span><span class="str">"\(num) buzz"</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">
    print</span><span class="pun">(</span><span class="pln">num</span><span class="pun">)</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<h2>
	تعلم الخوارزميات
</h2>

<p>
	إن تعلم الخوارزميات خطوة مهمة لأي شخص مهتم بالبرمجة والتطوير فهي تساعده على فهم البرمجة بشكل أفضل وتكسبه مهارة فهم المشكلات البرمجية المعقدة واختيار الطرق الأنسب والأكثر كفاءة لحلها.
</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>

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

<ol>
	<li>
		<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> فهي مهارة أساسية يحتاجها أي مبرمج.
		</p>
	</li>
	<li>
		<p>
			درب نفسك على التفكير الخوارزمي أي التفكير في أي مشكلة تواجهك بطريقة تشبه طريقة تفكير الحاسوب من خلال تحديد الخطوات المفصلة للحل أو تقسيم المشكلة إلى مشكلات أصغر وحل كل جزء على حدا.
		</p>
	</li>
	<li>
		<p>
			تعلم أهم أنواع الخوارزميات المعروفة التي يحتاجها معظم المطورين والطرق المختلفة لتنفيذها وكفاءة كل طريقة منها.
		</p>
	</li>
	<li>
		<p>
			تعلم كيف تحلل أي مشكلة برمجية وتفهمها جيدًا وتخطط لها على الورق وتمثلها بالطرق الخوارزمية وتأكد من أنك فهمتها بشكل كامل قبل كتابة كودها البرمجي.
		</p>
	</li>
	<li>
		<p>
			تعرف على أهم هياكل البيانات في لغة البرمجة التي تستخدمها لكتابة برامجك وتطبيقاتك وفكر بالبنية الأفضل لحل مشكلتك.
		</p>
	</li>
	<li>
		<p>
			طبق <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> وتدرب على حل التحديات البرمجية Problem Solving وهي كثيرة عبر الإنترنت وتضم الكثير من المهتمين بالبرمجة والتطوير وقارن إجاباتك مع إجابات الآخرين وتناقش معهم حول حلولهم لتكسب المزيد من الخبرات.
		</p>
	</li>
	<li>
		<p>
			فكر في تنفيذ عدة طرق لحل المشكلات البرمجية التي تواجهك وقارن أيّ هذه الطرق أسرع وأكثر كفاءة وفعالية.
		</p>
	</li>
	<li>
		<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="">تعقيد الخوارزميات</a> الذي يعبر عن مقدار الوقت أو مساحة الذاكرة التي تستغرقها الخوارزمية للتنفيذ واستفذ منه في كتابة شيفرات برمجية فعالة.
		</p>
	</li>
	<li>
		<p>
			اعتمد مصادر تعلم موثوقة ومنهجية، ستجد في أكاديمية حسوب الكثير من<a href="https://academy.hsoub.com/programming/" rel=""> المقالات والدروس</a> المفيدة لتعلم البرمجة والخوارزميات، كما توفر <a href="https://wiki.hsoub.com/Algorithms" rel="external">موسوعة حسوب</a> توثيقًا باللغة العربية لأشهر الخوارزميات التي يحتاج أي مطور لمعرفتها، كما يمكنك الانضمام إلى <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> التي تضم العديد من المسارات المفيدة لتعلم أساسيات البرمجة وتتضمن عدة مسارات تشرح الخوارزميات وطريقة التفكير المنهجي في حل أي مشكلة وكتابة خوارزمياتها قبل البدء ببرمجتها مما يصقل مهاراتك التقنية لتفكر كمهندس برمجيات بدلاً من أن تكون مجرد مبرمج يكتب الأكواد وينفذها فحسب.
		</p>
	</li>
</ol>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="443" id="ips_uid_3043_6" src="https://academy.hsoub.com/applications/core/interface/index.html" title="دورة علوم الحاسوب - أكاديمية حسوب" width="787" data-embed-src="https://www.youtube.com/embed/EkvUFDevMlM"></iframe>
</p>

<h2>
	الخلاصة
</h2>

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

<p>
	ترجمة -بتصرّف- للفصل الأول من كتاب <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%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/advance/%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>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D8%B5%D9%86%D9%81-%D8%A8%D8%A7%D9%84%D8%A7%D8%B9%D8%AA%D9%85%D8%A7%D8%AF-%D8%B9%D9%84%D9%89-%D8%B7%D8%B1%D9%82-%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A2%D9%84%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-scikit-learn-r1266/" rel="">بناء مصنف بالاعتماد على طرق تعلم الآلة بلغة بايثون باستخدام مكتبة Scikit-Learn</a> - النسخة الكاملة لكتاب <a href="https://academy.hsoub.com/files/17-%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%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-%D9%88%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A2%D9%84%D8%A9/" rel="">مدخل إلى الذكاء الاصطناعي وتعلم الآلة</a>
	</li>
</ul>

<p>
	 
</p>
]]></description><guid isPermaLink="false">1282</guid><pubDate>Mon, 02 Aug 2021 15:02:00 +0000</pubDate></item><item><title>&#x62F;&#x644;&#x64A;&#x644; &#x634;&#x627;&#x645;&#x644; &#x639;&#x646; &#x62A;&#x62D;&#x644;&#x64A;&#x644; &#x62A;&#x639;&#x642;&#x64A;&#x62F; &#x627;&#x644;&#x62E;&#x648;&#x627;&#x631;&#x632;&#x645;&#x64A;&#x629;</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_06/60cc2d7a0a8c5_-----.png.86b371f96bc68390d43c9571a5d6e5e0.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/" rel="">علوم الحاسوب</a>، لكنهم لا يزالون مبرمجين رائعين ومبدعين ونقدِّر ما يبنونه، حيث تملك علوم الحاسوب النظرية استخداماتها وتطبيقاتها، ويمكن أن تكون عمليةً تمامًا.
</p>

<p>
	يستهدف هذا المقال المبرمجين الذين يعرفون عملهم جيدًا ولكنهم لا يملكون خلفيةً نظريةً في علوم الحاسوب، وذلك من خلال واحدة من أكثر أدوات علوم الحاسوب واقعيةً، وهي: صيغة O الكبير Big O notation، وتحليل تعقيد الخوارزمية algorithm complexity.
</p>

<p>
	تُعَدّ هذه الأداة واحدةً من الأدوات المفيدة عمليًا للأشخاص العاملين في المجال الأكاديمي لعلوم الحاسوب، وفي إنشاء برامج على مستوى الإنتاج في الصناعة، لذلك نأمل أن تتمكن من تطبيقها في الشيفرة الخاصة بك لتحسينها بعد قراءة هذا المقال، كما يُفتَرض أن تكون قادرًا على فهم جميع المصطلحات الشائعة التي يستخدمها المختصون في علوم الحاسوب، مثل مصطلحات: التعقيد "Big O"، و"السلوك المقارب asymptotic behavior"، و"تحليل الحالة الأسوأ worst-case analysis" بعد قراءة هذا المقال.
</p>

<p>
	يستهدف هذا المقال أيضًا طلاب المدارس الإعدادية والثانوية في أي مكان في العالم، والذين يتنافسون دوليًا في <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://en.wikipedia.org/wiki/International_Olympiad_in_Informatics" rel="external nofollow">الأولمبياد الدولي للمعلوماتية</a>، أو مسابقة الخوارزميات الطلابية، أو مسابقات أخرى مماثلة.
</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>

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

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

<p>
	ستجد خلال هذا المقال العديد من الروابط التي تنقلك للاطلاع على مواد مهمة خارج نطاق موضوع المقال، إذ يُحتمل درايتك بمعظم هذه المفاهيم إذا كنت مبرمجًا صناعيًا؛ أما إذا كنت طالبًا مشاركًا في المسابقات، فسيعطيك اتباع هذه الروابط أفكارًا حول <span ipsnoautolink="true">مجالات أخرى من علوم الحاسوب أو <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></span> التي ربما لم تستكشفها بعد، والتي يمكنك الاطلاع عليها لتوسيع اهتماماتك.
</p>

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

<p>
	يُعَدّ تعقيد الخوارزمية طريقةً لقياس سرعة تشغيل برنامج ما أو خوارزمية ما، لذلك يُعَد أمرًا عمليًا.
</p>

<h2>
	الدافع Motivation
</h2>

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

<p>
	عندما نريد مقارنة الخوارزميات من حيث ماهيتها -أي الأفكار التي تُعرّفنا كيفية حساب شيءٍ ما-، فلن يساعدنا العد بالميلي ثانية في ذلك، إذ يُحتمَل أن تعمل الخوارزمية السيئة والمكتوبة بلغة برمجة منخفضة المستوى مثل <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://en.wikipedia.org/wiki/Assembly_language" rel="external nofollow">لغة التجميع Assembly</a>، بصورة أسرع بكثير من خوارزمية جيدة مكتوبة بلغة برمجة عالية المستوى مثل <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://www.python.org/" rel="external nofollow">بايثون</a>، أو <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="http://www.ruby-lang.org/en/" rel="external nofollow">روبي</a>، لذلك يجب تحديد معنى "الخوارزمية الأفضل".
</p>

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

<p>
	تتضمن أمثلة العمليات الحسابية البحتة <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://en.wikipedia.org/wiki/Floating-point_arithmetic/" rel="external nofollow">العمليات العشرية العددية numerical floating-point operations</a>، مثل: الجمع والضرب، أو البحث داخل قاعدة بيانات متناسبة مع الذاكرة العشوائية RAM عن قيمة معينة، أو تحديد المسار الذي ستمر به شخصية ذكاء اصطناعي في لعبة فيديو، بحيث يتعين عليها فقط السير مسافةً قصيرةً داخل عالمها الافتراضي (كما في الشكل الآتي)، أو تشغيل تطابق نمط <a data-ss1624978547="1" data-ss1624978693="1" href="http://www.regular-expressions.info/" rel="external nofollow">تعبير نمطي regular expressions</a> مع سلسلة، فالعمليات الحسابية موجودة في كل مكان في البرامج الحاسوبية.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="69018" data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://academy.hsoub.com/uploads/monthly_2021_06/ArtificialIntelligenceCharactersInVideoGamesUseAlgorithmsToAvoidObstaclesWhenNavigatingInTheVirtualWorld.png.63417b23c946d9a15422bf889754512f.png" rel=""><img alt="ArtificialIntelligenceCharactersInVideoGamesUseAlgorithmsToAvoidObstaclesWhenNavigatingInTheVirtualWorld.png" class="ipsImage ipsImage_thumbnailed" data-fileid="69018" data-unique="yo4x3ysbo" src="https://academy.hsoub.com/uploads/monthly_2021_06/ArtificialIntelligenceCharactersInVideoGamesUseAlgorithmsToAvoidObstaclesWhenNavigatingInTheVirtualWorld.thumb.png.a74310a4b17057db9e796017cdafd9c2.png"></a>
</p>

<p style="text-align: center;">
	تستخدم شخصيات الذكاء الاصطناعي في ألعاب الفيديو الخوارزميات لتجنب العقبات عند تنقّلها في العالم الافتراضي
</p>

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

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

<p>
	لنبدأ بمثال بسيط هو إيجاد العنصر الأكبر في مصفوفة.
</p>

<h2>
	عد التعليمات
</h2>

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

<p>
	إذا كنت طالبًا منافسًا في مسابقات الخوارزميات، فيُرجَّح استخدمك لغة <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="http://www.cplusplus.com/doc/tutorial/" rel="external nofollow">C++</a>‎، لذلك لن تواجه مشكلة، وبالتالي نوصي في هذه الحالة التدرّب باستخدام لغة C++‎.
</p>

<p>
	يمكن البحث عن العنصر الأكبر في مصفوفة باستخدام شيفرة لغة <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://www.quirksmode.org/js/intro.html" rel="external nofollow">جافا سكريبت</a> التالية، بافتراض أن مصفوفة الدخل هي A، وحجمها n:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_6" style=""><span class="kwd">var</span><span class="pln"> M </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="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="kwd">var</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"> </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">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">&gt;=</span><span class="pln"> M </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"> 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="pun">}</span></pre>

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

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

<ul>
	<li>
		إسناد قيمة لمتغير.
	</li>
	<li>
		البحث عن قيمة عنصر معين في مصفوفة.
	</li>
	<li>
		مقارنة قيمتين.
	</li>
	<li>
		زيادة قيمة.
	</li>
	<li>
		العمليات الحسابية الأساسية، مثل: الجمع، والضرب.
	</li>
</ul>

<p>
	سنفترض أن التفرع -أي الاختيار بين جزئي <code>if</code> و<code>else</code> بعد تقييم شرط <code>if</code>- سيحدث على الفور ولن يحسب هذه التعليمات.
</p>

<p>
	السطر الأول من الشيفرة السابقة هو:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_8" style=""><span class="kwd">var</span><span class="pln"> M </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></pre>

<p>
	يتطلب هذا السطر تعليمتين: إحداهما للبحث عن العنصر A[ 0 ]، والأخرى لإسناد قيمته إلى M، حيث سنفترض أن قيمة n تساوي 1 على الأقل دائمًا، كما تتطلّب الخوارزمية هاتين التعليمتين دائمًا، بغض النظر عن قيمة n، ويجب أيضًا تشغيل شيفرة تهيئة حلقة <code>for</code> دائمًا، ويعطينا هذا تعليمتين أخريين، هما: الإسناد assignment، والمقارنة comparison:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_14" style=""><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></pre>

<p>
	ستشغَّل هاتان التعليمتان قبل أول تكرار من حلقة <code>for</code>، كما نحتاج إلى تعليمتين إضافيتين للتشغيل بعد كل تكرار لحلقة <code>for</code>، وهما: زيادة المتغير i، ومقارنة للتحقق من أننا ما زلنا ضمن الحلقة، أي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_12" style=""><span class="pun">++</span><span class="pln">i</span><span class="pun">;</span><span class="pln">
i </span><span class="pun">&lt;</span><span class="pln"> n</span><span class="pun">;</span></pre>

<p>
	إذا تجاهلنا جسم الحلقة، فسيكون عدد التعليمات التي تحتاجها هذه الخوارزمية هو 4 + 2n، أي 4 تعليمات في بداية حلقة <code>for</code>، وتعليمتين في نهاية كل تكرار من n تكرار. يمكننا الآن تحديد دالة رياضية f( n )، حيث تعطينا عدد التعليمات التي تحتاجها الخوارزمية عند إعطاء n، أي لدينا f( n ) = 4 + 2n عندما يكون جسم حلقة <code>for</code> فارغًا.
</p>

<h2>
	تحليل الحالة الأسوأ Worst-case analysis
</h2>

<p>
	لدينا بالنظر إلى جسم حلقة <code>for</code> عملية بحث في مصفوفة، وعملية مقارنة تحدث دائمًا، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_17" style=""><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">&gt;=</span><span class="pln"> M </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">…</span></pre>

<p>
	أي يوجد تعليمتان، ولكن اعتمادًا على قيم المصفوفة، فقد يعمل جسم تعليمة <code>if</code> وقد لا يعمل. فإذا تحقق <code>A[ i ] &gt;= M</code>، فسنشغّل هاتين التعليمتين الإضافيتين -أي البحث في مصفوفة والإسناد-، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_21" style=""><span class="pln">M </span><span class="pun">=</span><span class="pln"> A</span><span class="pun">[</span><span class="pln"> i </span><span class="pun">]</span></pre>

<p>
	لكن لا يمكننا الآن تحديد الدالة f( n ) بسهولة، وذلك لعدم اعتماد عدد التعليمات على n فقط، وإنما على الدخل أيضًا، فإذا كان الدخل A = [ 1, 2, 3, 4 ] مثلًا، فستحتاج الخوارزمية إلى تعليمات أكثر مما إذا كان الدخل A = [ 4, 3, 2, 1 ].
</p>

<p>
	نفكر عند تحليل الخوارزميات في أسوأ سيناريو غالبًا، فما هو أسوأ شيء يمكن حدوثه لخوارزميتنا؟ ومتى تحتاج الخوارزمية إلى معظم التعليمات لإكمالها؟ في هذه الحالة، يحدث ذلك عندما يكون لدينا مصفوفة بترتيب تصاعدي مثل A = [ 1, 2, 3, 4 ]، حيث يجب استبدال المتغير M في كل مرة، وبالتالي، سينتج عن ذلك تنفيذ معظم التعليمات، ويُطلَق على هذه الحالة اسم <strong>تحليل الحالة الأسوأ Worst-case analysis</strong>؛ وهذا ليس أكثر من مجرد التفكير في الحالة التي تحدث عندما نكون الأقل حظًا.
</p>

<p>
	لدينا في أسوأ الحالات 4 تعليمات للتشغيل داخل جسم حلقة <code>for</code>، لذلك لدينا f( n ) = 4 + 2n + 4n = 6n + 4، حيث تعطينا الدالة f عدد التعليمات التي قد تكون مطلوبةً في الحالة الأسوأ بالاعتماد على حجم المشكلة n.
</p>

<h2>
	السلوك المقارب Asymptotic behavior
</h2>

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

<p>
	يعتمد عدد تعليمات وحدة المعالجة المركزية الفعلية اللازمة لكل عبارة لغة برمجة، على مُصرِّف compiler لغة البرمجة، وكذا على مجموعة تعليمات وحدة المعالجة المركزية المتاحة، سواءً كان معالج AMD أو Intel Pentium على حاسوبك، أو كان معالج MIPS على Playstation 2 الخاصة بك، وسنتجاهل ذلك أيضًا كما ذكرنا سابقًا.
</p>

<p>
	سنشغّل الآن الدالة "f" الخاصة بنا من خلال "مرشِّح filter" ليساعدنا في التخلص من التفاصيل الصغيرة التي يفضّل المتخصصون في علوم الحاسوب تجاهلها.
</p>

<p>
	يوجد قسمان في الدالة f التي تساوي 6n + 4، وهما: 6n، و4، كما لا نهتم في تحليل التعقيد إلا بما يحدث لدالة عد التعليمات عندما يكبر دخل البرنامج (n)، ويتماشى هذا مع الأفكار السابقة لسلوك "السيناريو الأسوأ" الذي ينص على الاهتمام بكيفية تصرّف الخوارزمية عند معالجتها بصورة سيئة، أي عندما تفعل هذه الخوارزمية شيئًا صعبًا، وهذا مفيد حقًا عند مقارنة الخوارزميات.
</p>

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

<p>
	سنحذف الأقسام التي تنمو ببطء ونبقي فقط الأقسام التي تنمو بسرعة عندما تصبح n أكبر، ومن الواضح أن 4 ستبقى 4 لأن n تنمو بصورة أكبر؛ أما 6n فتنمو بصورةٍ أكبر وأكبر، لذلك تميل إلى كونها أهم بالنسبة للمشكلات الأكبر، وبالتالي، أول شيء سنفعله هو حذف 4 والاحتفاظ بالدالة على صورة f( n ) = 6n.
</p>

<p>
	يُعَدّ ما سبق منطقيًا، وذلك لأنّ الرقم 4 هو "ثابت التهيئة initialization constant" ببساطة، وقد تتطلب <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> المختلفة وقتًا مختلفًا لعملية الإعداد، فمثلًا، تحتاج لغة جافا إلى بعض الوقت لتهيئة <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" 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="">آلتها الافتراضية virtual machine</a>، وبما أننا نتجاهل الاختلافات في لغات البرمجة، فمن المنطقي تجاهل هذه القيمة فقط.
</p>

<p>
	الشيء الثاني الذي سنتجاهله هو معامل الضرب الثابت أمام n، وبالتالي ستصبح الدالة f( n ) = n، مما يجعل الأمور أكثر بساطةً.
</p>

<p>
	يُعَدّ إهمال معامل الضرب أمرًا منطقيًا إذا فكرنا في كيفية تصريف لغات البرمجة المختلفة، فقد تُصرَّف عبارة "البحث في المصفوفة" في إحدى لغات البرمجة إلى تعليمات مختلفة عن لغات برمجةٍ مختلفة، كما لا يتضمّن الإجراء A[ i ] التحقق من عدم تجاوز المتغير i لحجم المصفوفة المُصرَّح عنها في لغة سي C على سبيل المثال، ولكنه يتضمّن ذلك في لغة <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://en.wikipedia.org/wiki/Pascal_(programming_language)" rel="external nofollow">باسكال Pascal</a>؛ إذًا فالشيفرة التالية المكتوبة بلغة باسكال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_25" style=""><span class="pln">M </span><span class="pun">:=</span><span class="pln"> A</span><span class="pun">[</span><span class="pln"> i </span><span class="pun">]</span></pre>

<p>
	تكافئ الشيفرة التالية المكتوبة بلغة سي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_27" style=""><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> i </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> n </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"> A</span><span class="pun">[</span><span class="pln"> i </span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لذلك نتوقّع أن لغات البرمجة المختلفة ستنتج عوامِلًا مختلفةً عندما نعُد تعليماتها.
</p>

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

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

<p>
	يُسمَّى المرشِّح المتمثِّل بعمليتَي "إهمال جميع العوامل"، و"الإبقاء على القسم الذي يكبر أكثر" كما هو موصوف أعلاه <strong>بالسلوك المقارب asymptotic behavior</strong>، ولذلك نمثِّل السلوك المقارب للدالة f( n ) = 2n + 8 من خلال الدالة f( n ) = n.
</p>

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

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

<ol>
	<li>
		تعطي الدالة f( n ) = 5n + 12 الدالة f( n ) = n باستخدام الاستنتاج نفسه بالضبط كما هو مذكور أعلاه.
	</li>
	<li>
		تعطي الدالة f (n) = 109 الدالة f( n ) = 1، حيث سنهمل معامل الضرب 109 * 1، لكن لا يزال يتعين علينا وضع 1 هنا للإشارة إلى أنّ لهذه الدالة قيمة غير صفرية.
	</li>
	<li>
		تعطي الدالة f( n ) =n<sup>2</sup> + 3n + 112 الدالة f( n ) = n<sup>2</sup>، حيث تكبرn<sup>2</sup> أكثر من 3n بالنسبة لقيمة n كبيرة بدرجة كافية، لذلك نحتفظ بها.
	</li>
	<li>
		تعطي الدالة f( n ) = n<sup>3</sup> + 1999n + 1337 الدالة f( n ) = n<sup>3</sup>، فعلى الرغم من أنّ العامل الموجود أمام n كبير جدًا، لكن لا يزال بإمكاننا العثور على قيمة n كبيرة بما يكفي، بحيث يكون n<sup>3</sup> أكبر من 1999n، وبما أننا مهتمون بسلوك قيم n الكبيرة جدًا، فسنبقي على n<sup>3</sup> فقط، أي تصبح الدالة n<sup>3</sup> المرسومة باللون الأزرق في الشكل التالي أكبر من الدالة 1999n المرسومة باللون الأحمر بعد القيمة n = 45، كما تبقى هي الأكبر بعد هذه النقطة إلى الأبد.
	</li>
</ol>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="69020" data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://academy.hsoub.com/uploads/monthly_2021_06/Cubic-vs-Linear.png.a4aae0b532ef2af36e0da1cae32589f2.png" rel=""><img alt="Cubic-vs-Linear.png" class="ipsImage ipsImage_thumbnailed" data-fileid="69020" data-unique="0jhro0uep" src="https://academy.hsoub.com/uploads/monthly_2021_06/Cubic-vs-Linear.png.a4aae0b532ef2af36e0da1cae32589f2.png"></a>
</p>

<ol start="5">
	<li>
		تعطي الدالة f( n ) = n +√n الدالة f (n) = n، وذلك لأنّ n تنمو أسرع من ‎√n كلما زادتn ويمكنك تجربة الأمثلة الآتية بنفسك.
	</li>
</ol>

<p>
	<strong>تمرين 1</strong>
</p>

<ol>
	<li>
		f( n ) = n<sup>6</sup> + 3n
	</li>
	<li>
		f( n ) = 2<sup>n</sup> + 12
	</li>
	<li>
		f( n ) = 3<sup>n</sup> + 2<sup>n</sup>
	</li>
	<li>
		f( n ) = n<sup>n</sup> + n
	</li>
</ol>

<p>
	(اكتب نتائجك، وسترى الحل أدناه).
</p>

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

<h2>
	التعقيد Complexity
</h2>

<p>
	بما أنه يمكننا إهمال كل هذه الثوابت "الشكلية"، فمن السهل جدًا معرفة السلوك المقارب لدالة عدّ تعليمات البرنامج، حيث يملك البرنامج الذي لا يحتوي على حلقات، الدالة f( n ) = 1، لأن عدد التعليمات التي يحتاجها هو مجرد عددٍ ثابت -إلا في حالة استخدامه العودية كما سنرى لاحقًا-.
</p>

<p>
	يملك البرنامج الذي يحتوي حلقةً واحدةً تمتد من 1 إلى n، الدالة f( n ) = n، حيث سينفِّذ عددًا ثابتًا من التعليمات قبل الحلقة، وعددًا ثابتًا من التعليمات بعد الحلقة، وعددًا ثابتًا من التعليمات داخل الحلقة التي تُشغَّل جميعًا n مرة.
</p>

<p>
	يجب أن يكون هذا أكثر سهولةً وأقل مللًا من عدّ التعليمات الفردية، لذلك لنلقِ نظرةً على بعض الأمثلة لفهم ذلك أكثر. يتحقق البرنامج التالي المكتوب بلغة <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://www.php.net/" rel="external nofollow">PHP</a> من وجود قيمة معيَّنة داخل مصفوفة A لها الحجم n:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_29" style=""><span class="pun">&lt;?</span><span class="pln">php
   $exists </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">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"> </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">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">==</span><span class="pln"> $value </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
           $exists </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</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="pun">?&gt;</span></pre>

<p>
	تسمَّى هذه الطريقة للبحث عن قيمة داخل مصفوفة <strong>البحث الخطي linear search</strong>، وهذا اسم معقول لاحتواء هذا البرنامج على الدالة f( n ) = n، كما سنحدد بالضبط ما تعنيه كلمة "خطي" في القسم التالي.
</p>

<p>
	لاحظ وجود عبارة "break" هنا، والتي قد تؤدّي إلى إنهاء البرنامج في وقت قريب، وربما بعد تكرارٍ واحد؛ لكن تذكّر اهتمامنا بالسيناريو الأسوأ، وهو بالنسبة لهذا البرنامج عدم احتواء المصفوفة A على القيمة التي نبحث عنها، لذلك لا يزال لدينا الدالة f( n ) = n.
</p>

<p>
	<strong>تمرين 2</strong>
</p>

<p>
	حلّل عدد التعليمات التي يحتاجها برنامج PHP أعلاه بالنسبة إلى n في الحالة الأسوأ للعثور على الدالة ( f( n، وذلك على غرار الطريقة التي حلّلنا بها البرنامج الأول المكتوب بلغة جافا سكريبت، ثم تحقق من أنه لدينا f( n ) = n بصورةٍ مقاربة.
</p>

<p>
	لنلقِ نظرةً على البرنامج المكتوب بلغة بايثون Python، والذي يجمع عنصرين من مصفوفة معًا لينتج مجموع يُخزَّن في متغير آخر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_31" style=""><span class="pln">v </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"> a</span><span class="pun">[</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">]</span></pre>

<p>
	لدينا هنا عددٌ ثابت من التعليمات، لذلك f (n) = 1.
</p>

<p>
	يتحقق البرنامج التالي المكتوب بلغة C++‎ من احتواء متّجه -مصفوفة مختارة أو جزء من مصفوفة- يُسمَّى A، وذو حجم n على قيمتين متماثلتين في أي مكان ضمنه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_34" style=""><span class="pln">bool duplicate </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">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="kwd">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"> </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"> </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"> </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"> </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">if</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">&amp;&amp;</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"> A</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="pun">{</span><span class="pln">
           duplicate </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</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">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> duplicate </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">
</span><span class="pun">}</span></pre>

<p>
	بما أنه توجد هنا حلقتان متداخلتان داخل بعضهما البعض، فسيكون لدينا سلوكًا مقاربًا موصوفًا بالدالة f( n ) = n<sup>2</sup>.
</p>

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

	<p data-gramm="false">
		<strong>قاعدة عامة:</strong> يمكن تحليل البرامج البسيطة عن طريق عدّ الحلقات المتداخلة في البرنامج، بحيث تنتج حلقة مفردة مارة على n عنصر الدالة f( n ) = n، وتنتج حلقة داخل حلقة الدالةf( n ) = n<sup>2</sup>، كما تنتج حلقة داخل حلقة داخل حلقة الدالة f (n) = n<sup>3</sup>.
	</p>
</blockquote>

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

<p>
	لنلقِ نظرةً على المثال التالي المكتوب بلغة <span class="ipsEmoji">?</span>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_37" style=""><span class="kwd">int</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"> 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"> </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">
   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>
	إذا علمنا أن <code>f( n )</code> هي دالة تنفِّذ n تعليمة بالضبط، فيمكننا حينئذٍ معرفة أن عدد تعليمات البرنامج بأكمله هو n<sup>2</sup> بصورةٍ مقاربة، حيث تُستدعى الدالة n مرة تمامًا.
</p>

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

	<p data-gramm="false">
		<strong>قاعدة عامة:</strong> إذا كان لدينا سلسلة من حلقات for المتسلسلة، فستحدِّد أبطأ حلقة سلوك البرنامج المقارب، وإذا وُجِدت حلقتان متداخلتان متبوعتان بحلقةٍ واحدة، فهذا يكافئ وجود الحلقات المتداخلة وحدها فقط، وذلك لأنّ الحلقات المتداخلة ستطغى على الحلقة البسيطة.
	</p>
</blockquote>

<p>
	ننتقل الآن إلى الصيغة التخيُّلية التي يستخدمها علماء الحاسوب للسلوك المقارب، حيث سنقول أن برنامجنا هو Θ ( f( n )) عندما نحدِّد الدالة f بصورة مقاربة، فمثلًا، البرامج المذكورة أعلاه هي Θ (1) وΘ( n<sup>2</sup> ) وΘ( n<sup>2</sup>  ) على التوالي، حيث تُقرَأ Θ( n ) "ثيتا بالنسبة إلى n".
</p>

<p>
	نقول أحيانًا أنّ f( n ) -وهي الدالة الأصلية التي تحسب عدد التعليمات بما في ذلك الثوابت- هي شيء ما Θ، حيث نقول مثلًا أنّ f( n ) = 2n هي دالة Θ( n )، كما يمكننا كتابة 2n ∈ Θ (n)‎ أيضًا، والتي تُنطَق "2n هي ثيتا بالنسبة إلى n".
</p>

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

<p>
	فيما يلي بعض العبارات الرياضية الصحيحة باستخدام هذا الصيغة:
</p>

<ol>
	<li>
		n<sup>6</sup> + 3n ∈ Θ( n<sup>6</sup> )
	</li>
	<li>
		2<sup>n</sup> + 12 ∈ Θ( 2<sup>n</sup> )
	</li>
	<li>
		3<sup>n</sup> + 2<sup>n</sup> ∈ Θ( 3<sup>n</sup> )
	</li>
	<li>
		n<sup>n</sup> + n ∈ Θ( n<sup>n</sup> )
	</li>
</ol>

<p>
	<strong>بالمناسبة</strong>، إذا حللت التمرين 1 السابق، فهذه هي بالضبط الإجابات التي يجب أن تصل إليها.
</p>

<p>
	نسمّي ما نضعه ( هنا )Θ <strong>التعقيد الزمني time complexity</strong>، أو <strong>تعقيد complexity الخوارزمية</strong>، لذلك فللخوارزمية التي تحتوي على الصيغة Θ( n ) تعقيدٌ هو n.
</p>

<p>
	لدينا أيضًا أسماءً خاصةً للصيغ التالية: Θ( 1 )، وΘ( n )، وΘ( n<sup>2</sup> ) وΘ( log( n ) )، وذلك لكثرة ظهورها، حيث نقول أنّ خوارزمية Θ( 1 ) هي خوارزمية ذات وقت ثابت constant-time algorithm، والخوارزمية Θ( n ) خطية linear، وΘ( n<sup>2</sup> ) تربيعية quadratic؛ أما الخوارمية Θ( log( n ) )  فهي لوغاريتمية logarithmic. لا تقلق إن لم تعرف ما هي اللوغاريتمات حتى الآن، سنشرح ذلك لاحقًا.
</p>

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

	<p data-gramm="false">
		<strong>قاعدة عامة:</strong> تعمل البرامج ذات صيغة Θ الأكبر بصورةٍ أبطأ من البرامج ذات صيغة Θ الأصغر.
	</p>
</blockquote>

<h2>
	صيغة O الكبير Big-O notation
</h2>

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

<p>
	<strong>مشكلة الفرز (sorting problem)</strong> هي إحدى المشكلات الشهيرة التي يستخدمها علماء الحاسوب لتدريس الخوارزميات، حيث تُعطَى مصفوفة A بحجم n في مشكلة الفرز، ويُطلَب منا كتابة برنامج لفرز أو ترتيب هذه المصفوفة، وتُعَدّ هذه المشكلة مشكلةً مهمةً كونها مشكلةً واقعيةً في الأنظمة الحقيقية، إذ يحتاج مستكشف الملفات إلى فرز الملفات التي يعرضها حسب الاسم حتى يتمكن المستخدِم من التنقل بينها بسهولة، أو قد تحتاج لعبة فيديو إلى فرز الكائنات ثلاثية الأبعاد المعروضة في العالم بناءً على بعدها عن عين اللاعب داخل العالم الافتراضي من أجل تحديد ما هو مرئي وما هو غير مرئي، وهو ما يسمى <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://en.wikipedia.org/wiki/Hidden-surface_determination" rel="external nofollow">مشكلة الرؤية Visibility Problem</a>، فالكائنات التي تكون أقرب للاعب هي المرئية، في حين أنّ الكائنات البعيدة قد تخفيها الكائنات الموجودة أمامها، ويوضّح الشكل الآتي هذه المشكلة، إذ لن يرى اللاعب الموجود في النقطة الصفراء المناطق المظلَّلة، كما يُعَدّ تقسيم العالم إلى أجزاء صغيرة وفرزها حسب المسافة التي تفصلها عن اللاعب إحدى طرق حل مشكلة الرؤية.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="69022" data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://academy.hsoub.com/uploads/monthly_2021_06/HiddenSurface.png.ede0ef8d8e7f95047c17d30337a9d192.png" rel=""><img alt="HiddenSurface.png" class="ipsImage ipsImage_thumbnailed" data-fileid="69022" data-unique="tuclza91f" src="https://academy.hsoub.com/uploads/monthly_2021_06/HiddenSurface.thumb.png.1aa65984926a6173320c74d75ea07d8c.png"></a>
</p>

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

<p>
	الطريقة التالية هي طريقة غير فعالة لفرز مصفوفة في لغة <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://wiki.hsoub.com/Ruby" rel="external">روبي</a> Ruby، حيث تدعم لغة روبي فرز المصفوفات باستخدام دوال مبنيّة مسبقًا يجب استخدامها بدلًا من ذلك، وهي بالتأكيد أسرع مما سنراه هنا، ولكن ما سنستخدمه هنا هي شيفرة بغرض التوضيح فقط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_40" style=""><span class="pln">b </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
n</span><span class="pun">.</span><span class="pln">times </span><span class="kwd">do</span><span class="pln">
   m </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">
   mi </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
   a</span><span class="pun">.</span><span class="pln">each_with_index </span><span class="kwd">do</span><span class="pln"> </span><span class="pun">|</span><span class="pln">element</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="pln"> element </span><span class="pun">&lt;</span><span class="pln"> m
           m </span><span class="pun">=</span><span class="pln"> element
           mi </span><span class="pun">=</span><span class="pln"> i
       end
   end
   a</span><span class="pun">.</span><span class="pln">delete_at</span><span class="pun">(</span><span class="pln"> mi </span><span class="pun">)</span><span class="pln">
   b </span><span class="pun">&lt;&lt;</span><span class="pln"> m
end</span></pre>

<p>
	تسمى هذه الطريقة <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://en.wikipedia.org/wiki/Selection_sort" rel="external nofollow">الفرز الانتقائي Selection sort</a>، حيث تجد هذه الخوارزمية الحد الأدنى من المصفوفة -يُرمَز إلى المصفوفة بالمتغير a في الشيفرة السابقة، بينما يُرمَز إلى الحد الأدنى بالمتغير m، والمتغير mi هو دليله في المصفوفة-، وتضعه في نهاية مصفوفة جديدة -أي المصفوفة b في حالتنا-، ثم تزيله من المصفوفة الأصلية، وبعدها تجد الحد الأدنى بين القيم المتبقية للمصفوفة الأصلية، وتلحِقه بالمصفوفة الجديدة التي تحتوي على عنصرين الآن، ثم تزيله من المصفوفة الأصلية؛ وتستمر هذه العملية إلى حين إزالة جميع العناصر من المصفوفة الأصلية وإدخالها في المصفوفة الجديدة، مما يعني فرز المصفوفة.
</p>

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

<p>
	من الصعب قليلًا تقييم تعقيد هذا البرنامج، حيث يجب معرفة المجموع 1 + 2 + … +(n+(n-1، ولكن يمكننا بالتأكيد إيجاد "الحد الأعلى" لهذا المجموع، وهذا يعني أنه يمكننا تغيير برنامجنا - أي يمكنك فعل ذلك في عقلك، وليس في الشيفرة الفعلية- لجعله <strong>أسوأ</strong> مما هو عليه، ومن ثم إيجاد تعقيد هذا البرنامج الجديد، فإذا تمكّنا من العثور على تعقيد البرنامج الأسوأ الذي أنشأناه، فسنعلم أنّ برنامجنا الأصلي أسوأ أو ربما أفضل. بالتالي إذا أوجدنا تعقيدًا جيدًا لبرنامجنا المعدَّل الذي هو أسوأ من برنامجنا الأصلي، فيمكننا معرفة أنه سيكون لبرنامجنا الأصلي تعقيدًا جيدًا جدًا أيضًا، أي إما جيدًا بمستوى برنامجنا المعدَّل أو أفضل منه.
</p>

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

<p>
	يمكن تغيير الحلقة الداخلية للبرنامج بجعلها تتكرر n مرة دائمًا بدلًا من تكرارها عددًا متغيرًا من المرات، كما ستكون بعض هذه التكرارات عديمة الفائدة، لكنها ستساعدنا في تحليل تعقيد الخوارزمية الناتجة؛ وإذا أجرينا هذا التغيير البسيط، فمن الواضح أن الخوارزمية الجديدة التي أنشأناها هي Θ( n<sup>2</sup> ) وذلك لوجود حلقتين متداخلتين بحيث يتكرر كل منهما n مرة بالضبط، وبالتالي، يمكننا القول أنّ الخوارزمية الأصلية هي O( n<sup>2</sup> ).
</p>

<p>
	تُنطَق O( n<sup>2</sup> ) "أوه كبيرة لمربع n، أي big oh of n squared"، وهذا يقودنا للقول بأن برنامجنا ليس أسوأ من n<sup>2</sup> بصورةٍ مقاربة، فقد يكون أفضل من ذلك أو مثله.
</p>

<p>
	إذا كان برنامجنا هو بالفعل Θ( n<sup>2</sup> ) فلا يزال بإمكاننا القول أنه O( n<sup>2</sup> ) أي تخيل تغيير البرنامج الأصلي بطريقة لا تغيره كثيرًا، لكنها لا تزال تجعله أسوأ قليلًا مثل إضافة تعليمات لا معنى لها في بداية البرنامج، بحيث سيؤدي فعل ذلك إلى تغيير دالة عدّ التعليمات بواسطة ثابت بسيط، والذي سنتجاهله عندما يتعلق الأمر بالسلوك المقارب، وبالتالي فالبرنامج Θ( n<sup>2</sup> ) هو O( n<sup>2</sup> ) أيضًا.
</p>

<p>
	قد لايكون البرنامج O( n<sup>2</sup> ) هو Θ( n<sup>2</sup> ) أيضًا، فأيّ برنامج Θ( n ) مثلًا هو O( n<sup>2</sup> ) وO( n ) كذلك، وإذا تخيلنا أن برنامج Θ( n ) هو عبارة عن حلقة for بسيطة تتكرر n مرة، فيمكن جعلها أسوأ بتغليفها ضمن حلقة for أخرى تتكرر n مرة أيضًا، وبالتالي ينتج برنامج له دالة f( n ) = n<sup>2</sup>، كما يمكن تعميم ذلك بالقول أنّ أي برنامج Θ( a ) هو O( b ) عندما يكون b أسوأ من a.
</p>

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

<p>
	لذا يمكن القول بأن برنامجنا هو O( n<sup>2</sup> ) بطريقةٍ آمنة، وذلك لأننا حلّلنا خوارزميتنا، ووجدناها ليست أسوأ من n<sup>2</sup>، ولكنها في الواقع قد تساوي n<sup>2</sup>، وبالتالي يمكننا تقدير سرعة تشغيل برنامجنا.
</p>

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

<p>
	<strong>تمرين 3</strong>
</p>

<p>
	أيٌّ مما يلي صحيح؟
</p>

<ol>
	<li>
		خوارزمية Θ( n ) هي O( n )
	</li>
	<li>
		خوارزمية Θ( n ) هي O( n<sup>2</sup> )
	</li>
	<li>
		خوارزمية Θ( n<sup>2</sup> ) هي O( n<sup>3</sup> )
	</li>
	<li>
		خوارزمية Θ( n ) هي O( 1 )
	</li>
	<li>
		خوارزمية O( 1 ) هي Θ( 1 )
	</li>
	<li>
		خوارزمية O( n ) هي Θ( 1 )
	</li>
</ol>

<p>
	<strong>الحل</strong>
</p>

<ol>
	<li>
		هذا صحيح لأنّ برنامجنا الأصلي كان Θ( n )، ويمكننا تحقيق O( n ) دون تغيير برنامجنا على الإطلاق.
	</li>
	<li>
		هذا صحيح لأنّ n<sup>2</sup> أسوأ من n.
	</li>
	<li>
		هذا صحيح لأنّ n<sup>3</sup> أسوأ من n<sup>2</sup>.
	</li>
	<li>
		هذا خطأ لأنّ 1 ليس أسوأ من n، فإذا أخذ البرنامج n تعليمةً بصورةٍ مقاربة -أي عددًا خطيًا من التعليمات-، فلا يمكننا جعله أسوأ ولا يمكن جعله يأخذ تعليمةً واحدةً بصورةٍ مقاربة -أي عددًا ثابتًا من التعليمات-.
	</li>
	<li>
		هذا صحيح لأنّ التعقيدان متماثلان.
	</li>
	<li>
		قد يكون هذا صحيحًا أو غير صحيح وذلك اعتمادًا على الخوارزمية، لكنه خاطئ في الحالة العامة، فإذا كانت الخوارزمية Θ( 1 )، فمن المؤكد أنها O( n )؛ أما إذا كانت O( n ) فقد لا تكون Θ( 1 )، فمثلًا، خوارزمية Θ( n ) هي O ( n ) وليست Θ( 1 ).
	</li>
</ol>

<p>
	<strong>تمرين 4</strong>
</p>

<p>
	استخدم متتالية الجمع الحسابية arithmetic progression sum لإثبات أنّ البرنامج أعلاه ليس O( n<sup>2</sup> ) فقط، وإنما Θ( n<sup>2</sup> ) أيضًا، ويمكنك البحث عن معنى المتتالية الحسابية في <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://en.wikipedia.org/wiki/1_%2B_2_%2B_3_%2B_4_%2B_%E2%8B%AF" rel="external nofollow">ويكيبيديا</a> في حالة عدم معرفتك بها.
</p>

<p>
	يعطي التعقيد O-complexity لخوارزمية ما <strong>حدًا أعلى</strong> لتعقيد الخوارزمية الفعلي - أي الوقت الأكبر الذي قد تستغرقه الخوارزمية-، بينما تعطي الصيغة Θ تعقيد الخوارزمية الفعلي، حيث نقول أحيانًا أن الصيغة Θ تعطينا <strong>حدًا تامًا</strong>، وإذا علمت أننا وجدنا حد تعقيدٍ غير تام، فيمكنك استخدام الحرف الصغير o للإشارة إلى ذلك، فمثلًا، إذا كان للخوارزمية التعقيد Θ( n )، فسيكون تعقيدها التام n، وبالتالي سيكون لهذه الخوارزمية O( n ) و O( n<sup>2</sup> ) معًا.
</p>

<p>
	بما أن الخوارزمية هي Θ( n )، فسيكون حد O( n ) هو الحد التام؛ أما حد O( n<sup>2</sup> ) فليس تامًا، ولذلك يمكننا كتابة أن الخوارزمية هي o( n<sup>2</sup> ) والتي تُنطق "o الصغير بالنسبة إلى مربع n"، وذلك لتوضيح أن الحد ليس تامًا.
</p>

<p>
	كما يُفضَّل إيجاد حدود تامة للخوارزميات لأنها تعطينا مزيدًا من المعلومات حول سلوكها، ولكنه ليس أمرًا سهلًا دائمًا.
</p>

<p>
	<strong>تمرين 5</strong>
</p>

<p>
	حدّد أيًا من الحدود التالية هي حدودًا تامةً وأيها لا، ثم تحقق من صحتها أو خطئها، ويجب عليك استخدام الصيغة o لتحديد الحدود غير التامة:
</p>

<ol>
	<li>
		خوارزمية Θ (n) التي لها الحد الأعلى O (n).
	</li>
	<li>
		خوارزمية Θ (n<sup>2</sup>) التي لها الحد الأعلى O (n<sup>3</sup>).
	</li>
	<li>
		خوارزمية Θ(1) التي لها الحد الأعلى O (n).
	</li>
	<li>
		خوارزمية Θ (n) التي لها الحد الأعلى O (1).
	</li>
	<li>
		خوارزمية Θ (n) التي لها الحد الأعلى O (2n).
	</li>
</ol>

<p>
	<strong>الحل</strong>
</p>

<ol>
	<li>
		الحد تام لأنّ تعقيد Θ وتعقيد O متماثلان في هذه الحالة.
	</li>
	<li>
		الحد غير تام لأنّ تعقيد O أكبر من تعقيد Θ، وقد يكون حد O( n<sup>2</sup> ) تامًا، لذلك يمكننا كتابة أن الخوارزمية هي o( n<sup>3</sup>).
	</li>
	<li>
		الحد غير تام، لأنّ تعقيد O أكبر من تعقيد Θ، وقد يكون حد O( 1 ) تامًا، لذلك يمكننا الإشارة بأنّ الحد O( n ) ليس تامًا من خلال كتابته بالشكل o( n ).
	</li>
	<li>
		الحد خاطئ، فقد اُرتكِب خطأ في حسابه، فلا يمكن أن يكون لخوارزمية Θ( n ) حد أعلى من O( 1 )‎، وذلك لأنّ التعقيد n أكبر من التعقيد 1 -تذكر أنّ O تعطي حدًا أعلى.
	</li>
	<li>
		الحد تام، فقد يبدو مثل حد غير تام، لكن هذا ليس صحيحًا في الواقع، -تذكر أنّ سلوك 2n وn المقارب هو نفسه، وأنّ الصيغتَين O وΘ تهتمان بالسلوك المقارب؛ إذًا لدينا O( 2n ) = O( n )، وبالتالي، فهذا الحد تام لأن التعقيد هو نفس Θ.
	</li>
</ol>

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

	<p data-gramm="false">
		<strong>قاعدة عامة:</strong> يُعَدّ إيجاد تعقيد O أسهل من إيجاد تعقيد Θ لخوارزميةٍ ما.
	</p>
</blockquote>

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

<p>
	عدّلنا برنامجنا في المثال أعلاه ليصبح أسوأ -أي أخذ المزيد من التعليمات، وبالتالي المزيد من الوقت- وأنشأنا الصيغة O، حيث تُعَدّ الصيغة O ذا مغزىً لأنها تخبرنا بأن برنامجنا لن يكون أبدًا أبطأ من حدٍ معين، ولهذا فهي توفر معلومات قيّمةً تمكننا من القول بأن برنامجنا جيد بما فيه الكفاية، وإذا فعلنا العكس وعدّلنا برنامجنا ليصبح أفضل، ثم أوجدنا تعقيد البرنامج الناتج، فنستخدم الصيغة Ω التي تعطينا تعقيدًا يخبرنا بأن برنامجنا لن يكون أفضل، وهذا مفيد إذا أردنا إثبات أن البرنامج يعمل ببطء أو أن الخوارزمية سيئة، وقد يكون هذا مفيدًا للقول بأن الخوارزمية بطيئة جدًا عند استخدامها في حالة معينة، فمثلًا، خوارزمية Ω( n<sup>3</sup> ) ليست أفضل من n<sup>3</sup>.
</p>

<p>
	قد تكون الصيغة Θ( n<sup>3</sup> ) سيئًة مثل Θ (n<sup>4</sup>) أو أسوأ منها، لكننا نعلم أنها سيئة إلى حد ما على الأقل. إذًا تعطينا الصيغة Ω حدًا أدنى لتعقيد خوارزمية، كما يمكننا كتابة الصيغة ω على غرار الصيغة ο عندما يكون الحد ليس تامًا، فمثلًا، خوارزمية Θ ( n<sup>3 </sup>) هي ο( n<sup>4</sup> ) وω( n<sup>2</sup> ) وتُقرَأ Ω( n ) "أوميغا كبيرة بالنسبة إلى n"، بينما تُقرَأ ω( n ) "أوميغا صغيرة بالنسبة إلى n".
</p>

<p>
	<strong>تمرين 6</strong>
</p>

<p>
	اكتب حد O تام وآخر غير تام، وحد Ω تام وآخر غير تام من اختيارك للتعقيدات التالية، ولكن بشرط وجودهما طبعًا:
</p>

<ol>
	<li>
		Θ( 1 )
	</li>
	<li>
		Θ(√n)
	</li>
	<li>
		Θ( n )
	</li>
	<li>
		Θ( n<sup>2</sup> )
	</li>
	<li>
		Θ( n<sup>3</sup> )
	</li>
</ol>

<p>
	<strong>الحل</strong>
</p>

<p>
	هذا هو تطبيق مباشر للتعاريف أعلاه:
</p>

<ol>
	<li>
		الحدود التامة هي O( 1 ) وΩ( 1 )، كما يكون حد O غير التام هو O( n ) -تذكّر أن O تعطينا حدًا أعلى-، وبما أن n أكبر من 1، فسيمثِّل حدًا غير تام يمكننا كتابته بالشكل o( n ) أيضًا، كما لا يمكننا إيجاد حد غير تام للصيغة Ω لعدم تمكننا من الحصول على أقل من 1 لهذه الدوال، إذًا يجب تعاملنا مع الحد التام فقط.
	</li>
	<li>
		يجب أن تكون للحدود التامة تعقيد Θ نفسه، لذا فهي  O(√n) وΩ(√n) على التوالي؛ أما الحدود غير التامة فقد تكون O( n )، حيث تُعَدّ n أكبر من ‎√n وبالتالي فهي حد أعلى لها. وبما أننا نعلم أن هذا حدًا أعلى غير تام، فيمكننا أيضًا كتابته بالصورة o( n )؛ أما الحد الأدنى غير التام، فيمكننا ببساطة استخدام Ω( 1 )، وبما أننا نعلم أن هذا الحد ليس تامًا، فيمكننا كتابته بالصورة ω( 1. 3 ). الحدود التامة هي O( n ) وΩ( n ). قد يكون الحدان الغير تامين هما ω( 1 ) وo( n<sup>3</sup> ) وهي في الواقع حدودٌ سيئة للغاية، لأنها بعيدة كل البعد عن التعقيدات الأصلية، إلا أنها لا تزال صالحة باستخدام التعاريف.
	</li>
	<li>
		الحدود التامة هي O( n<sup>2</sup> ) وΩ( n<sup>2</sup> ) ويمكننا استخدام ω( 1 ) وo( n<sup>3</sup> ) بالنسبة للحدود غير التامة كما في المثال السابق.
	</li>
	<li>
		الحدود التامة هي O( n<sup>3</sup> ) وΩ( n<sup>3</sup> ) على التوالي، وقد يكون الحدان غير التامَين هما ω(‎n<sup>2 </sup>√n)‎ وω(n<sup>3 </sup>√n)، وعلى الرغم من أنّ هذه الحدود ليست تامة، إلا أنها أفضل من تلك الموجودة في جواب رقم 3 و4 من هذا التمرين أعلاه.
	</li>
</ol>

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

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

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

<p>
	يوضح الجدول التالي الرموز التي قدمناها للتو وتوافقاتها مع الرموز الرياضية المعتادة للمقارنات التي نستخدمها مع الأعداد، كما يعود السبب في عدم استخدامنا للرموز المعتادة هنا واستخدام الأحرف الإغريقية بدلًا منها، إلى الإشارة إلى إجراء مقارنة سلوك مقارب وليس مقارنة بسيطة:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
	<thead>
		<tr>
			<th>
				عامل المقارنة المقارب Asymptotic comparison operator
			</th>
			<th>
				عامل المقارنة العددي Numeric comparison operator
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				الخوارزمية هي ( شيء ما )o
			</td>
			<td>
				يوجد عدد &lt; هذا الشيء
			</td>
		</tr>
		<tr>
			<td>
				الخوارزمية هي ( شيء ما )O
			</td>
			<td>
				يوجد عدد ≤ هذا الشيء
			</td>
		</tr>
		<tr>
			<td>
				الخوارزمية هي ( شيء ما )Θ
			</td>
			<td>
				يوجد عدد = هذا الشيء
			</td>
		</tr>
		<tr>
			<td>
				الخوارزمية هي ( شيء ما )Ω
			</td>
			<td>
				يوجد عدد ≥ هذا الشيء
			</td>
		</tr>
		<tr>
			<td>
				الخوارزمية هي ( شيء ما )ω
			</td>
			<td>
				يوجد عدد &gt; هذا الشيء
			</td>
		</tr>
	</tbody>
</table>

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

	<p data-gramm="false">
		<strong>قاعدة عامة:</strong> تُعَدّ جميع الرموز O، وo، وΩ، وω، وΘ مفيدةً في بعض الأحيان، لكن الرمز O هو الرمز الأكثر استخدامًا، حيث يسهُل تحديده أكثر من الرمز Θ، كما يُعَدّ أكثر فائدةً من الرمز Ω عمليًا.
	</p>
</blockquote>

<h2>
	اللوغاريتمات Logarithms
</h2>

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

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

<p>
	يوضِّح الشكل الآتي مقارنةً بين الدوال n، و‎√n، وlog( n )، إذ تنمو الدالة n -أو كما تُسمَّى الدالة الخطية linear function- المرسومة باللون الأخضر في أعلى الشكل، أسرع بكثير من دالة الجذر التربيعي المرسومة باللون الأحمر في المنتصف، والتي بدورها تنمو أسرع بكثير من الدالة log( n ) المرسومة باللون الأزرق في الجزء السفلي من هذا الرسم البياني، ويكون الفرق واضحًا تمامًا حتى بالنسبة إلى n صغيرة مثل n = 100.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="69023" data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://academy.hsoub.com/uploads/monthly_2021_06/Log-vs-linear.png.e863cf4d353110881c7837240b101faa.png" rel=""><img alt="Log-vs-linear.png" class="ipsImage ipsImage_thumbnailed" data-fileid="69023" data-unique="j62pd5f6a" src="https://academy.hsoub.com/uploads/monthly_2021_06/Log-vs-linear.png.e863cf4d353110881c7837240b101faa.png"></a>
</p>

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

<p>
	2<sup>x</sup> = 1024
</p>

<p>
	نريد حل هذه المعادلة لإيجاد قيمة x، لذلك لنسأل أنفسنا: ما هو الرقم الذي يجب رفع الأساس 2 إليه حتى نحصل على 1024؟ هذا الرقم هو 10. بالفعل لدينا ‎2<sup>10</sup> = 1024، وهو أمر سهل التحقق منه؛ كما تساعدنا اللوغاريتمات من خلال الإشارة إلى هذه المشكلة باستخدام صيغة جديدة، فالرقم 10 في هذه الحالة هو لوغاريتم 1024 ونكتبه بالصورة ( log( 1024 ونقرأه "لوغاريتم 1024".
</p>

<p>
	بما أننا نستخدم العدد 2 أساسًا، فتسمى هذه اللوغاريتمات لوغاريتمات الأساس 2، كما توجد لوغاريتمات لها أساسات أخرى، ولكننا سنستخدم فقط لوغاريتمات الأساس 2 في هذا المقال، وإذا كنت طالبًا منافسًا في مسابقات دولية ولا تعرف شيئًا عن اللوغاريتمات، فنوصيك بشِدة <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://tutorial.math.lamar.edu/Classes/Alg/LogFunctions.aspx" rel="external nofollow">بالتدرّب على اللوغاريتمات</a> بعد الانتهاء من هذا المقال.
</p>

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

<p>
	<strong>تمرين 7</strong>
</p>

<p>
	حُلّ المعادلات أدناه، وأوجد اللوغاريتم في كل حالة باستخدام اللوغاريتمات ذات الأساس 2 فقط:
</p>

<ol>
	<li>
		2<sup>x</sup> = 64
	</li>
	<li>
		‎(2<sup>2</sup>)<sup>x</sup>= 64
	</li>
	<li>
		4<sup>x</sup> = 4
	</li>
	<li>
		2<sup>x</sup> = 1
	</li>
	<li>
		2<sup>x</sup> + 2<sup>x</sup> = 32
	</li>
	<li>
		‎(2<sup>x</sup>) * (2<sup>x</sup>) = 64
	</li>
</ol>

<p>
	<strong>الحل</strong>
</p>

<p>
	لا يتضمن الحل أكثر من تطبيق الأفكار المُعرَّفة أعلاه:
</p>

<ol>
	<li>
		يمكننا باستخدام طريقة التجربة والخطأ إيجاد أن x = 6، وبالتالي، log( 64 ) = 6.
	</li>
	<li>
		يمكن كتابة ‎(2<sup>2</sup>)<sup>x</sup> بالصورة 2<sup>2x</sup> من خلال تطبيق خصائص الأس، إذًا لدينا 2x = 6 لأن log( 64 ) = 6 من النتيجة السابقة، وبالتالي، x = 3.
	</li>
	<li>
		يمكننا كتابة 4 بالشكل 2<sup>2</sup> باستخدام معرفتنا من المعادلة السابقة، وبهذا تصبح المعادلة ‎(2<sup>2</sup>)<sup>x</sup>= 4 وهي 2<sup>2x</sup> = 4 نفسها، ونلاحظ أن log( 4 ) = 2 لأن ‎2<sup>2</sup> = 4، وبالتالي، لدينا أن 2x = 2، وعليه فـ x = 1؛ كما يمكن ملاحظة ذلك بسهولة من المعادلة الأصلية، حيث أن ناتج الرفع للأس 1 هو الأساس نفسه.
	</li>
	<li>
		تذكر أن ناتج الرفع للأس 0 هو 1، لذلك لدينا log( 1 ) = 0، بما أنّ ‎2<sup>0</sup>= 1، وبالتالي، x = 0.
	</li>
	<li>
		لا يمكننا أخذ اللوغاريتم مباشرةً بسبب وجود المجموع، ولكن يمكن ملاحظة أنّ 2<sup>x</sup>+ 2<sup>x</sup> هي ‎2 * (2<sup>x</sup>) نفسها، حيث ضربنا هنا بالعدد 2 أي لهما الأساس نفسه، وبالتالي، ينتج 2<sup>x + 1</sup>، والآن كل ما علينا فعله هو حل المعادلة 2<sup>x + 1</sup>= 32، حيث نجد أنّ log( 32 ) = 5، وهكذا x + 1 = 5، وبالتالي، x = 4.
	</li>
	<li>
		نضرب هنا قوتين للعدد 2 معًا، ولذلك يمكننا ضمهما من خلال ملاحظة أن (2<sup>x</sup>) * (2<sup>x</sup>) هي 2<sup>2x</sup> نفسها، وبالتالي كل ما علينا فعله هو حل المعادلة 2<sup>2x</sup>= 64 التي حللناها بالفعل أعلاه، حيث x = 3.
	</li>
</ol>

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

	<p data-gramm="false">
		<strong>قاعدة عامة:</strong> يمكنك الحصول على تقدير تقريبي لمدى سرعة تشغيل برنامجك بمجرد تحليل التعقيد في مسابقة الخوارزميات المطبَّقة باستخدام لغة ++ C، وذلك من خلال التوقّع بأن البرنامج ينفّذ حوالي 1,000,000 عملية في الثانية، بحيث تُعطَى العمليات التي تحسب عددها من خلال دالة السلوك المقارب التي تصف خوارزميتك، وبالتالي تستغرق خوارزمية ( Θ( n مثلًا، حوالي ثانية واحدة لمعالجة الدخل من أجل n = 1,000,000.
	</p>
</blockquote>

<h2>
	التعقيد العودي Recursive complexity
</h2>

<p>
	لنلقِ نظرةً على دالة عودية recursive function، فالدالة العودية هي دالة تستدعي نفسها. هل يمكننا تحليل تعقيدها؟ توجِد الدالة الآتية والمكتوبة بلغة بايثون، حيث تُقيِّم <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://ar.wikipedia.org/wiki/%D8%B9%D8%A7%D9%85%D9%84%D9%8A" rel="external nofollow">مضروب factorial</a> عددٍ معين، إذ يمكن إيجاد مضروب عدد صحيح موجب بضربه بجميع الأعداد الصحيحة السابقة معًا، فمثلًا، مضروب العدد 5 هو 5 * 4 * 3 * 2 * 1؛ كما نعبِّر عن ذلك بالصورة "!5" ونقرؤها "مضروب العدد خمسة five factorial"، ويفضِّل بعض الناس نُطقها بصوت عالٍ مثل "خمسة !!!".
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_42" style=""><span class="pln">def factorial</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"> 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="kwd">return</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> n </span><span class="pun">*</span><span class="pln"> factorial</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"> </span><span class="pun">)</span></pre>

<p>
	لنحلّل تعقيد هذه الدالة، فعلى الرغم من عدم احتواء هذه الدالة على أية حلقات، إلا أنّ تعقيدها ليس ثابتًا constant، حيث يجب علينا متابعة عد التعليمات مرةً أخرى لمعرفة تعقيدها، فإذا مرّرنا المتغير n إلى هذه الدالة، فستنفّذ n مرة؛ وإذا لم تكن متأكدًا من ذلك، فشغّل هذه الدالة "يدويًا" الآن من أجل n = 5 للتحقق منها. ستنفَّذ هذه الدالة مثلًا 5 مرات من أجل n = 5، بحيث ستستمر في إنقاص قيمة n بمقدار 1 في كل استدعاء، وبالتالي، يكون تعقيد هذه الدالة هو ( Θ( n.
</p>

<p>
	إذا لم تكن متأكدًا من هذه الحقيقة، فتذكر أنه يمكنك دائمًا العثور على التعقيد الدقيق عن طريق حساب عدد التعليمات، وإذا رغبت في ذلك، فيمكنك محاولة حساب عدد التعليمات الفعلية التي تطبّقها هذه الدالة لإيجاد الدالة f( n )، والوصول إلى نتيجة أنها خطية بالفعل، مع العلم أنّ خطية تعني Θ( n ).
</p>

<p>
	يحتوي الشكل التالي على رسم بياني لمساعدتك على فهم مفهوم العودية المُطبَّقة عند استدعاء الدالة factorial( 5 )، ويجب على هذا الشكل توضيح لماذا تعقيد هذه الدالة هو تعقيد خطي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="69021" data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://academy.hsoub.com/uploads/monthly_2021_06/FactorialRecursion.png.26b1cbf3f3af6c53cdf55faf88594d84.png" rel=""><img alt="FactorialRecursion.png" class="ipsImage ipsImage_thumbnailed" data-fileid="69021" data-unique="1ovb07ulf" src="https://academy.hsoub.com/uploads/monthly_2021_06/FactorialRecursion.png.26b1cbf3f3af6c53cdf55faf88594d84.png"></a>
</p>

<p style="text-align: center;">
	العودية (معاوة الاستدعاء) التي تطبّقها الدالة factorial
</p>

<h2>
	التعقيد اللوغاريتمي Logarithmic complexity
</h2>

<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> هي البحث عن قيمة داخل مصفوفة، ولقد حلّلنا هذه المشكلة سابقًا من خلال الحالة العامة، كما تصبح هذه المشكلة ممتعةً إذا كان لدينا مصفوفة مرتَّبة ونريد إيجاد قيمة معينة بداخلها، حيث تُسمَّى إحدى طرق القيام بذلك البحث الثنائي binary search.
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_45" style=""><span class="pln">def binarySearch</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"> value </span><span class="pun">):</span><span class="pln">
   </span><span class="kwd">if</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="kwd">if</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"> value</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="pln">
       </span><span class="kwd">else</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="pln">
   </span><span class="kwd">if</span><span class="pln"> value </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"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">]:</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> binarySearch</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"> n </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">1</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">/</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">1</span><span class="pun">,</span><span class="pln"> value </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"> value </span><span class="pun">&gt;</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="lit">2</span><span class="pln"> </span><span class="pun">]:</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> binarySearch</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"> n </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">1</span><span class="pln"> </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="lit">2</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"> value </span><span class="pun">)</span><span class="pln">
   </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span></pre>

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

<p>
	يجب تطبيق الدالة floor()‎ أو ceil()‎ بسبب وجود أخطاء بفارق الواحد off-by-one، وقد لا ينتج عن القسمة على العدد 2 قيمةً صحيحةً، فالجزء الصحيح أو المتمم الصحيح الأسفل floor لعدد حقيقي ما x، هو أكبر عدد صحيح ليس أكبر من x، فصحيح العدد 2.6 هو 2، أي أنّ أكبر عدد صحيح ليس أكبر من 2.6. بينما السقف أو المتمم الصحيح الأعلى ceil لعدد حقيقي x، فهو أصغر عدد صحيح ولكنه ليس أصغر من x، لأن سقف العدد 2.15 هو 3، أي أنّ أصغر عدد صحيح ليس أصغر من 2.15. لكن يمكننا افتراض أن هذه الطريقة ستنجح دائمًا، وسنفترض أنّ تطبيقنا الفعلي يهتم بأخطاء الفراق الواحد off-by-one، وذلك لأننا نريد تحليل تعقيد هذه الطريقة فقط. إذا لم تطبّق البحث الثنائي مطلقًا، فقد ترغب في فعل ذلك باستخدام لغة البرمجة المفضلة لديك.
</p>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="69019" data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://academy.hsoub.com/uploads/monthly_2021_06/BinarySearch.png.58e590a9e7dbda63521f966235d01680.png" rel=""><img alt="BinarySearch.png" class="ipsImage ipsImage_thumbnailed" data-fileid="69019" data-unique="5n9gwc4wu" src="https://academy.hsoub.com/uploads/monthly_2021_06/BinarySearch.png.58e590a9e7dbda63521f966235d01680.png"></a>
</p>

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

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

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_47" style=""><span class="lit">0<sup>th</sup> iteration</span><span class="pun">:</span><span class="pln"> n
</span><span class="lit">1<sup>st</sup> iteration: n /</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
</span><span class="lit">2<sup>nd</sup> iteration: n /</span><span class="pln"> </span><span class="lit">4</span><span class="pln">
</span><span class="lit">3<sup>rd</sup> iteration: n /</span><span class="pln"> </span><span class="lit">8</span><span class="pln">
</span><span class="pun">…</span><span class="pln">
i<sup>th</sup> iteration: n /</span><span class="pln"> </span><span class="lit">2<sup>i</sup><span class="pln">
last iteration</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span></span></pre>

<p>
	لاحظ احتواء المصفوفة على n / 2<sup>i</sup> عنصر في التكرار i بسبب تقسيم المصفوفة في كل تكرار إلى نصفين، مما يعني أننا نقسم عدد عناصرها على 2، أي نضرب المقام بـ 2؛ وإذا فعلنا ذلك i مرة، فسنحصل على n / 2<sup>i</sup>، إذ يستمر هذا الإجراء ونحصل على عدد أصغر من العناصر مع كل قيمة أكبر للمتغير i، حتى نصل إلى التكرار الأخير الذي يتبقى فيه عنصر واحد فقط، وإذا رغبنا في معرفة التكرار i الذي يتبقى فيه عنصرٌ واحد فقط، فعلينا حل المعادلة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_50" style=""><span class="lit">1</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">2<sup>i</sup></span></pre>

<p>
	سيكون هذا صحيحًا فقط عندما نصل إلى الاستدعاء النهائي للدالة ()binarySearch، وليس في الحالة العامة، لذلك فإيجاد قيمة i هنا سيساعدنا في العثور على التكرار الذي ستنتهي فيه العودية. وإذا ضربنا كلا الطرفين بـ 2<sup>i</sup> فسنحصل على:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_52" style=""><span class="lit">2</span><sup>i</sup><span class="pun">=</span><span class="pln"> n</span></pre>

<p>
	يجب أن تبدو هذه المعادلة مألوفة إذا قرأت قسم اللوغاريتمات أعلاه، وبالتالي ينتج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_54" style=""><span class="pln">i </span><span class="pun">=</span><span class="pln"> log</span><span class="pun">(</span><span class="pln"> n </span><span class="pun">)</span></pre>

<p>
	يخبرنا هذا أن عدد التكرارات المطلوبة لإجراء بحث ثنائي هو log( n )، حيث n هي عدد عناصر المصفوفة الأصلية، ويُعَدّ ذلك أمرًا منطقيًا.
</p>

<p>
	بافتراض أنّ n = 32 فسيكون لدينا مصفوفة مؤلفة من 32 عنصرًا، فكم مرةً يجب تقسيم هذه المصفوفة إلى نصفين للحصول على عنصر واحد فقط؟ سنحتاج إلى تقسيمها خمس مرات للحصول إلى عنصر واحد، أي بالترتيب التالي: 32 ← 16 ← 8 ← 4 ← 2 ← 1، والعدد 5 هو لوغاريتم 32، لذلك يكون تعقيد البحث الثنائي هو Θ( log( n ) ).
</p>

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

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

	<p data-gramm="false">
		<strong>قاعدة عامة:</strong> يؤدي تحسين وقت التشغيل المقارب لأحد البرامج، إلى تحسين أدائه بصورة أكبر من أي تحسينات "تقنية" أصغر مثل استخدام لغة برمجة أسرع.
	</p>
</blockquote>

<h2>
	الفرز الأمثل Optimal sorting
</h2>

<p>
	تهانينا! لقد بتّ الآن تعرف كلًا من تحليل تعقيد الخوارزميات، وسلوك الدوال المقارب، وصيغة big-O؛ بالإضافة إلى كيفية إيجاد تعقيد الخوارزمية ليكون O( 1 )، وO( log( n ) )، وO( n )، وO( n<sup>2</sup> ) وما إلى ذلك بديهيًا، كما أصبحت تعرف الرموز o، وO، وω، وΩ، وΘ، وماذا يعني تحليل الحالة الأسوأ أيضًا.
</p>

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

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

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_58" style=""><span class="pln">def merge</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">
   </span><span class="kwd">if</span><span class="pln"> empty</span><span class="pun">(</span><span class="pln"> A </span><span class="pun">):</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> B
   </span><span class="kwd">if</span><span class="pln"> empty</span><span class="pun">(</span><span class="pln"> B </span><span class="pun">):</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> A
   </span><span class="kwd">if</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">&lt;</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="kwd">return</span><span class="pln"> concat</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"> merge</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">A_n </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="pun">:</span><span class="pln">
       </span><span class="kwd">return</span><span class="pln"> concat</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"> merge</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"> </span><span class="lit">1.</span><span class="pun">..</span><span class="pln">B_n </span><span class="pun">]</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	تأخذ الدالة concat عنصرًا يُسمى "الرأس head" ومصفوفة تُسمى "الذيل tail"، ثم تبني وتعيد مصفوفةً جديدةً تحتوي على عنصر "الرأس" الذي يمثِّل العنصر الأول في المصفوفة الجديدة، وعلى عنصر "الذيل" الذي يمثِّل بقية العناصر الموجودة في المصفوفة، حيث تعيد الدالة concat( 3, [ 4, 5, 6 ] ) مثلًا، ما يلي: [ 3, 4, 5, 6 ].
</p>

<p>
	ونستخدم المتغيرين A<em>n وB</em>n للإشارة إلى أحجام المصفوفتين A وB على التوالي.
</p>

<p>
	<strong>تمرين 8</strong>
</p>

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

<p>
	يكشف تحليل هذه الخوارزمية أن وقت تشغيلها Θ( n )، حيث يمثِّل n طول المصفوفة الناتجة أي n = A_n + B_n.
</p>

<p>
	<strong>تمرين 9</strong>
</p>

<p>
	تحقق من أن وقت تشغيل الدالة <code>merge</code> هو Θ( n ).
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3904_62" style=""><span class="pln">def mergeSort</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="kwd">if</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="kwd">return</span><span class="pln"> A </span><span class="pun">#</span><span class="pln"> it is already sorted
   middle </span><span class="pun">=</span><span class="pln"> floor</span><span class="pun">(</span><span class="pln"> n </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">
   leftHalf </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">middle </span><span class="pun">]</span><span class="pln">
   rightHalf </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"> middle </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">n </span><span class="pun">]</span><span class="pln">
   </span><span class="kwd">return</span><span class="pln"> merge</span><span class="pun">(</span><span class="pln"> mergeSort</span><span class="pun">(</span><span class="pln"> leftHalf</span><span class="pun">,</span><span class="pln"> middle </span><span class="pun">),</span><span class="pln"> mergeSort</span><span class="pun">(</span><span class="pln"> rightHalf</span><span class="pun">,</span><span class="pln"> n </span><span class="pun">-</span><span class="pln"> middle </span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span></pre>

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

<p>
	<strong>تمرين 10</strong>
</p>

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

<p>
	لنحلل الآن تعقيد الدالة <code>mergeSort</code>، حيث سنقسم المصفوفة إلى نصفين متساويين في الحجم على غرار دالة البحث الثنائي <code>binarySearch</code> في كل خطوة من خطوات الدالة <code>mergeSort</code>، ولكننا سنحافظ في هذه الحالة على كلا النصفين طوال فترة التنفيذ، ثم نطبّق الخوارزمية عوديًا في كل نصف، كما نطبّق عملية الدمج <code>merge</code> على النتيجة التي تستغرق وقتًا مقداره Θ( n ) بعد أن تعيد الخوارزمية العودية.
</p>

<p>
	لبهذا نكون قد قسمنا المصفوفة الأصلية إلى مصفوفتين بحجم n / 2 لكل منهما، ثم دمجنا هذه المصفوفات، وهي عملية لدمج n عنصرًا وبالتالي تستغرق وقتًا (Θ (n، كما يوضح الشكل التالي هذه العملية العودية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="69024" data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://academy.hsoub.com/uploads/monthly_2021_06/MergesortRecursion.png.c9dbac787a4a6dcc3fd0f0a9947fd82b.png" rel=""><img alt="MergesortRecursion.png" class="ipsImage ipsImage_thumbnailed" data-fileid="69024" data-unique="z6bdc8dld" src="https://academy.hsoub.com/uploads/monthly_2021_06/MergesortRecursion.png.c9dbac787a4a6dcc3fd0f0a9947fd82b.png"></a>
</p>

<p style="text-align: center;">
	شجرة العودية لطريقة الفرز بالدمج merge sort
</p>

<p>
	تمثِّل كل دائرة استدعاءً للدالة <code>mergeSort</code> كما يشير الرقم المكتوب في الدائرة إلى حجم المصفوفة التي يجري فرزها، إذ تكون الدائرة الزرقاء العلوية الاستدعاء الأصلي للدالة <code>mergeSort</code>، بحيث نحصل على فرز مصفوفة بالحجم n، كما تشير الأسهم إلى الاستدعاءات المتكررة التي تجريها الدوال فيما بينها.
</p>

<p>
	يعمل الاستدعاء الأصلي للدالة <code>mergeSort</code> على استدعائها مرتين في مصفوفتين وحجم كل منهما n / 2، حيث يشار إلى ذلك بواسطة السهمين الموجودين في الأعلى، ثم يجري كل من هذين الاستدعائَين بدورهما استدعائين خاصين بهما لدمج مصفوفتين بحجم n / 4 لكل منهما، وهكذا دواليك حتى نصل إلى مصفوفات ذات حجم 1؛ ويسمَّى هذا الرسم البياني بالشجرة العودية recursion tree، لأنه يوضح كيفية حدوث العودية ويظهر مثل شجرة، لكن الجذر root في الأعلى والأوراق في الأسفل، لذلك يظهر مثل شجرة مقلوبة.
</p>

<p>
	لاحظ أن العدد الإجمالي للعناصر هو n في كل مستوى من الرسم البياني أعلاه، حيث يحتوي المستوى الأول على استدعاء واحد فقط للدالة <code>mergeSort</code> مع مصفوفة بحجم n أي العدد الإجمالي للعناصر هو n، كما يحتوي المستوى الثاني على استدعائين للدالة <code>mergeSort</code> وكل منهما بحجم n / 2. لكن n / 2 + n / 2 = n وهكذا يكون العدد الإجمالي للعناصر هو n في هذا المستوى، كما توجد 4 استدعاءات في المستوى الثالث، بحيث يُطبَّق كل استدعاء على مصفوفة بحجم n / 4، أي سيساوي عدد العناصر الإجمالي n / 4 + n / 4 + n / 4 + n / 4 = 4n / 4 = n، وبالتالي نحصل على n عنصر.
</p>

<p>
	يجب على المستدعي في كل مستوى من هذا الرسم البياني إجراء عملية دمج <code>merge</code> على العناصر التي يعيدها المستدعَى، إذ يجب على الدائرة المشار إليها باللون الأحمر مثلًا، ترتيب عدد n / 2 من العناصر، وذلك بتقسيم المصفوفة ذات الحجم n / 2 إلى مصفوفتين بحجم n / 4، ثم تُستدعَى الدالة <code>mergeSort</code> عوديًا لفرز تلك المصفوفة، ثم تُدمَجان معًا، ويُشار إلى هذه الاستدعاءات بالدوائر ذات اللون الأخضر.
</p>

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

<p>
	تعقيد كل مستوى هو Θ( n )، كما يكون عدد المستويات في هذا الرسم البياني، والذي يُطلق عليه أيضًا عمق depth شجرة العودية مساويًا لـ log( n )، وسبب ذلك هو السبب ذاته تمامًا الذي استخدمناه عند تحليل تعقيد البحث الثنائي.
</p>

<p>
	لدينا log( n ) مستوى وتعقيد كل مستوى هو Θ( n )، وبالتالي، يكون تعقيد الدالة <code>mergeSort</code> هو Θ(n * log( n ))، وهذا أفضل بكثير من Θ( n<sup>2</sup> ) الذي هو تعقيد خوارزمية الفرز الانتقائي -تذكر أنّ log( n ) أصغر بكثير من n، وبالتالي يكون n * log (n) أصغر بكثير من n * n = n<sup>2</sup>-، وإذا وجدت ذلك معقدًا، فلا تقلق لأن الأمر ليس سهلًا في المرة الأولى.
</p>

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

<p>
	تُستخدَم خوارزميات الفرز التي لها وقت تشغيل Θ( n * log( n ) )عمليًا، إذ تستخدم نواة نظام لينكس مثلًا، خوارزمية فرز تسمى <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://elixir.bootlin.com/linux/latest/source/lib/sort.c" rel="external nofollow">heapsort</a>، والتي لها وقت تشغيل مماثل لخوارزمية الفرز بالدمج mergesort الذي أوجدناه للتو، والذي هو Θ( n log( n ) )، لذلك فهو الأمثل.
</p>

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

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

<p>
	ترجمة -وبتصرف- للمقال <a data-ss1624526923="1" data-ss1624530426="1" data-ss1624978547="1" data-ss1624978693="1" href="https://discrete.gr/complexity/" rel="external nofollow">A Gentle Introduction to Algorithm Complexity Analysis</a> لصاحبه Dionysis "dionyziz" Zindros.
</p>

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

<ul>
	<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%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%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>
	</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%A9-%D8%AF%D9%8A%D9%83%D8%B3%D8%AA%D8%B1%D8%A7-dijkstra%E2%80%99s-algorithm-r1336/" rel="">خوارزمية ديكسترا Dijkstra’s Algorithm</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1247</guid><pubDate>Fri, 11 Jun 2021 15:09:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641; &#x62A;&#x62D;&#x636;&#x631; &#x644;&#x623;&#x633;&#x626;&#x644;&#x629; &#x645;&#x642;&#x627;&#x628;&#x644;&#x629; &#x639;&#x645;&#x644; &#x645;&#x647;&#x646;&#x62F;&#x633; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x627;&#x62A;</title><link>https://academy.hsoub.com/programming/advanced/%D9%83%D9%8A%D9%81-%D8%AA%D8%AD%D8%B6%D8%B1-%D9%84%D8%A3%D8%B3%D8%A6%D9%84%D8%A9-%D9%85%D9%82%D8%A7%D8%A8%D9%84%D8%A9-%D8%B9%D9%85%D9%84-%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-r751/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2019_10/5d9849e6ae284_.jpg.d8d12e70ce9857a4766e9220a0a8a1d4.jpg" /></p>
<p>
	يعدُّ التحضير لأسئلة المقابلة في مجال هندسة البرمجيات عملًا بدوام كامل تقريبًا، إذ هناك مصادر لا حصر لها على الإنترنت وغالبًا ما تكون هائلة عندما تبدأ العملية، ومن الطبيعي أن تكون متوترًا وخائفًا عند التقدم لمنصبٍ ما، لأنك تواجه خطر الحكم والرفض وهذا يكفي لإثارة قلق أي شخص.
</p>

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

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

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="32050" data-ss1627733072="1" href="https://academy.hsoub.com/uploads/monthly_2019_10/5d9849e69dc47_.jpg.7554520d2a2fdfc75c762fcb70c63fe1.jpg" rel=""><img alt="كيف تحضر للمقابلة.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="32050" data-unique="9r8mxdzct" src="https://academy.hsoub.com/uploads/monthly_2019_10/5d9849e6b355f_.thumb.jpg.2f3c41370499680b96f4cf620b99aa90.jpg"></a>
</p>

<h2>
	أسئلة حول المعلومات الأساسية الخاصة بك
</h2>

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

<ul>
	<li>
		أخبرني عن مشروع عملت عليه، وما الذي جعلك تستمتع أو لا تستمتع؟
	</li>
	<li>
		أخبرني ماذا تعلّمت من العمل على هذا المشروع
	</li>
	<li>
		هل يمكنك التحدّث عن بعض الصعوبات التي واجهتها؟
	</li>
	<li>
		كيف نسّقت المهام بين أعضاء الفريق؟
	</li>
	<li>
		إذا قمت بإعادة هذا المشروع ثانيةً، هل ستفعل شيء ما بشكلٍ مختلفٍ؟
	</li>
</ul>

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

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

	<p data-gramm="false">
		يبدأ المقابلون بصفحة فارغة بالكامل ولديهم فترة زمنية قصيرة
	</p>
</blockquote>

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

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

<h2>
	أسئلة حول الشركة
</h2>

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

<ul>
	<li>
		ما الذي يثير اهتمامك في صناعتهم؟
	</li>
	<li>
		ما الذي جعلك مهتمًا بالشركة؟
	</li>
	<li>
		ما الذي تتوقع تحقيقه من خلال العمل هناك؟
	</li>
	<li>
		كيف تتناسب معرفتك مع ما يقومون به؟
	</li>
	<li>
		ما الذي جعلك تعتقد أنك ستكون مناسبًا لثقافتهم؟
	</li>
</ul>

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

<p>
	لا تقلق بشأن عدم معرفة جميع التقنيات التي يستخدموها. الانفتاح حول ما لا تعرفه (حتى الآن) يبدو جيدًا لمعظم المقابِلين. إظهار الاهتمام بتعلّم مجالهم حتى قبل توفير الوظيفة يبدو أفضل.
</p>

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

<h2>
	أسئلة المقابلة العامة
</h2>

<p>
	بالنسبة للجانب التقني للأشياء فإنَّ إدراك مبادئ <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> مثل <a data-ss1627733072="1" href="https://ar.wikipedia.org/wiki/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9_%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9_%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87" rel="external nofollow">البرمجة كائنية التوجه</a> و<a data-ss1627733072="1" href="https://ar.wikipedia.org/wiki/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1_%D9%85%D9%88%D8%AC%D9%87_%D8%A8%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1" rel="external nofollow">التطوير الموجه بالاختبار</a> و<a data-ss1627733072="1" href="https://ar.wikipedia.org/wiki/%D8%AA%D8%B1%D9%83%D9%8A%D8%A8_%D9%85%D8%AA%D9%88%D8%A7%D8%B5%D9%84" rel="external nofollow">التركيب المتواصل</a> و<a data-ss1627733072="1" href="https://wiki.hsoub.com/Design_Patterns" rel="external">أنماط التصميم</a> و<a data-ss1627733072="1" href="https://academy.hsoub.com/programming/workflow/git/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D9%81%D9%8A-%D8%A7%D9%84%D9%86%D8%B3%D8%AE-git-r240/" rel="">التحكم بالنسخة</a> هو أمرٌ أساسيٌّ. قد تكون محتاج أيضًا لتعلّم أساسيّات الشبكات أو قواعد البيانات أو الأنظمة حسب طبيعة المنصب الذي تتقدم له.
</p>

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

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

	<p data-gramm="false">
		أخبرني مرشدي الأول أنّني يجب أن أتقدّم لمنصب ما بالاعتماد على ما الذي أريد أن أكون عليه، بدلًا من الذي أنا حاليًا عليه.
	</p>
</blockquote>

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

<h2>
	حل المشاكل
</h2>

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

<h3>
	خلال مرحلة التحضير
</h3>

<p>
	هناك مواد غير محدودة على الإنترنت تساعدك على التحضير. الكتاب التقليدي الذي يعطّل العملية ويوفر المشاكل والحلول هو كتاب <a data-ss1627733072="1" href="http://www.crackingthecodinginterview.com/" rel="external nofollow">Cracking the Coding Interview</a> لـ Gayle Laakmann McDowell. المصادر على الإنترنت مثل <a data-ss1627733072="1" href="https://leetcode.com/" rel="external nofollow">LeetCode</a> و<a data-ss1627733072="1" href="https://www.hackerrank.com/" rel="external nofollow">HackerRank</a> هي بدائل جيّدة.
</p>

<p>
	يجب عليك معرفة ما هي الأعمال الأفضل لك بينما أنت تدرس. شخصيًّا أفضّل حل المشاكل والتحديات الصغيرة، مثل <a data-ss1627733072="1" href="http://codekata.com/" rel="external nofollow">code katas</a> أو <a data-ss1627733072="1" href="https://adventofcode.com/" rel="external nofollow">advent of code</a>. ومع ذلك يمكن أن يكون مضيعة للوقت. لذا عندما أريد أن أتعلّم أسرع، أجد الحلول على الإنترنت وأطبّقها بنفسي لفهم الخوارزمية. هناك الكثير من مقاطع الفيديو على اليوتيوب تشرح كيفية عمل خوارزمية معينة يمكنك أن تستخدمها.
</p>

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

<h3>
	خلال المقابلة نفسها
</h3>

<ul>
	<li>
		تأكد من أنّك فهمت المشكلة بشكلٍ كامل. اطلب من الشخص الذي يقابلك توضيح الأسئلة للتأكد من أنّك تعرف المطلوب منك.
	</li>
	<li>
		لا تنتقل إلى كتابة الشيفرة بدون أن يكون لديك معلومات كافية. مثلًا، يجب أن تتأكد من أنّك تعرف من أين تقرأ الدخل، ما هو تنسيق الدخل وحجم البيانات.
	</li>
	<li>
		ابدأ مع حل "القوة العنيفة" (brute force) ولا تفكر في الأداء بعد. اتركه بسيطًا. اشرح للمقابِل طريقة تفكيرك واذكر أنّك ستجرب حل القوة العنيفة أولًا.
	</li>
	<li>
		لا تقلل من صعوبة المهمة. فحتى الاختبارات التي تبدو بسيطة مثل <a data-ss1627733072="1" href="https://en.wikipedia.org/wiki/Fizz_buzz" rel="external nofollow">اختبار FizzBuzz</a>) يمكن أن تعطي انطباعًا سريعًا عن كيفية تحليل المشكلة وفهمها. يبدأ عادةً الاختبار يبدأ بسيطًا ويتقدم تدريجيًا.
	</li>
	<li>
		اذكر أيّة افتراضات تقوم بها. إذا كنت تستخدم توابع مكتبة للغةٍ ما من اختيارك، اسأل إذا كان يجب عليك تنفيذها. مثلًا الترتيب متوفر في معظم لغات البرمجة.
	</li>
	<li>
		بينما تتابع حل المشكلة، اشرح كيف تنقل بياناتك وأين تخزّنها وكيف تعالجها. يوضح هذا أنّك تفهم جيّدًا البيانات التي تتعامل معها ولا تحاول فقط تجربة الأشياء التي رأيتها حولك.
	</li>
	<li>
		يمكنك تقسيم مشكلتك إلى دوال أصغر بأسماء واضحة ثم المضي قدمًا والبدء بتنفيذ كلّ منها.
	</li>
</ul>

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

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

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

<h3>
	ماذا لو لم تحلّ المشكلة؟ هل يعني هذا أنّك فشلت في المقابلة؟
</h3>

<p>
	ليس بالضرورة. المقابِل مهتم أكثر بطريقة تفكيرك. من تجربتي يركز المقابِلون على:
</p>

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

<h2>
	أسئلة حول المقابِلين
</h2>

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

<ul>
	<li>
		كيف تُنظَّم فرق الهندسة؟ كيف توزَّع المهام على الفريق؟
	</li>
	<li>
		كيف يمكنك اختبار منتجاتك؟
	</li>
	<li>
		ما الفريق الذي يحتمل أن أعمل معه؟
	</li>
	<li>
		كيف تدعمون الأشخاص أثناء التدريب؟ كم يستغرق عادةً الشخص الجديد للتدريب؟
	</li>
	<li>
		ما الذي يمكن أن يكون مثالًا لمهمة أعمل عليها؟
	</li>
	<li>
		ما هي التقنيات الرئيسية التي تستخدمونها لهذا المشروع؟
	</li>
	<li>
		ما الفرص المتاحة للعمل على مشاريع مختلفة؟
	</li>
	<li>
		كيف يمكنك إجراء مراجعات الأداء؟ كيف يبدو التقدم الوظيفي لهذا المنصب تحديدًا؟
	</li>
</ul>

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

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

<h2>
	طريقة التفكير مهمة
</h2>

<p>
	بالطبع، تعد مهاراتك التقنية مهمة جدًا لمقابلات <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>
	جمع وفهم المتطلبات، والقدرة على التعبير عن المشكلة بطرق تقنية وطرق غير تقنية، إبلاغ أصحاب المصلحة حول التقدم والمشاكل ليست سوى جزء منها. إذا كنت تعتقد أنّ لديك مصدر قوة في شخصيتك فحاول أن تظهر ذلك خلال العملية.
</p>

<h3>
	اهدف للتحسين المستمر
</h3>

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

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

	<p data-gramm="false">
		القليل من التحضير المركّز يقطع شوطًا طويلًا، لكن لا تبالغ فيه
	</p>
</blockquote>

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

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

<p>
	ترجمة -وبتصرف- للمقال <a data-ss1627733072="1" href="https://www.intercom.com/blog/software-engineering-interview-questions/" rel="external nofollow">How to prepare for software engineering interview questions‎</a> لصاحبته Sofia Tzima
</p>
]]></description><guid isPermaLink="false">751</guid><pubDate>Mon, 25 Nov 2019 14:02:00 +0000</pubDate></item><item><title>&#x646;&#x645;&#x637; &#x627;&#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x645;&#x639;&#x645;&#x644; &#x627;&#x644;&#x62A;&#x62C;&#x631;&#x64A;&#x62F; Abstract Factory</title><link>https://academy.hsoub.com/programming/advanced/%D9%86%D9%85%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%85%D8%B9%D9%85%D9%84-%D8%A7%D9%84%D8%AA%D8%AC%D8%B1%D9%8A%D8%AF-abstract-factory-r504/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2017_06/main2.png.275f52a4f461b0e9455c3ce0344c97c7.png" /></p>

<p>
	يهدف نمط التصميم <strong>معمل التجريد Abstract Factory</strong> إلى توفير واجهة Interface لإنشاء مجموعة من الكائنات المرتبطة أو المنفصلة دون الحاجة إلى التعامل مع أصنافها الفعليّة. كما يوفّر هرمية Hierarchy تغلّف الكثير من المنصات Platforms المحتملة، وتعمل على بناء مجموعة من “المنتجات”.
</p>

<p style="text-align: center;">
	<img alt="main2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="23767" data-unique="y3rmmlqt6" src="https://academy.hsoub.com/uploads/monthly_2017_06/main2.png.d5555ea60a53a19b1ca32b9672e4ac28.png"></p>

<p>
	<br>
	ينطلق نمط التصميم هذا من مبدأ أن العامل <code>new</code> مؤذٍ وينبغي التقليل من استخدامه.<br><strong>ملاحظة:</strong> نعني بالتغليف Encapsulation في إطار البرمجة كائنيّة التوجه Object-oriented programming تلك الآليّة التي تتيح جمع البيانات (الخاصيّات Attributes) والإجراءات المطبّقة عليها (التوابع Methods) ضمن نفس الكائن، مع تقييد إمكانيّة وصول الكائنات الأخرى إلى عناصر الكائن.
</p>

<h2 id="المشكلة">
	المشكلة
</h2>

<p>
	إذا توجب على التطبيق Application أن يكون محمولا Portable، فلا بد من تغليف اعتمادات المنصات Platform dependencies التي يُراد له أن يعمل عليها. قد تتضمن هذه الاعتمادات نظام النوافذ، نظام التشغيل، قاعدة البيانات وغيرها. في كثير من الأحيان لا يُصمَّم التغليف مسبقا لأخذ الاعتمادات بالحسبان، فتبدأ عبارات الاختبار (<code>if.. else</code>) مع خيارات لجميع المنصات الحالية المدعومة، بالتكاثر كالأرانب داخل الشفرة البرمجية.
</p>

<h2 id="المناقشة">
	المناقشة
</h2>

<p>
	يوفّر نمط التصميم <strong>معمل التجريد Abstract Factory</strong> مستوى من المراوغة يجرّد Abstract عمليّة إنشاء أصناف من الكائنات، المرتبطة أو المنفصلة؛ وبالتالي لا يحدّد طريقة التنفيذ الفعليّة للأصناف محلّ التساؤل.
</p>

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

<p>
	تسهّل هذه الآلية تبديل مجموعات المنتجات، لأن الصنف المحدد في كائن المعمل يظهر مرة واحدة فقط في التطبيق، وذلك في المكان الذي استهل Instantiated فيه. يمكن للتطبيق ببساطة أن يبادل بين مجموعة كاملة من المنتجات عن طريق استهلال صنف فعليّ آخر من المعمل. وبما أن الخدمة التي يقدمها كائن المعمل واسعة الانتشار، فإنها تُنفَّذ عادة باستخدام النمط المفرد The Singleton pattern.
</p>

<p>
	<strong>ملاحظة:</strong> يحيل مفهوم التجريد Abstraction في هندسة البرمجيّات إلى الآليّة التي يُتخلَّص بموجبها من تعقيدات نظام مّا بتحديد مستوى من التعقيد لا يتجاوزه من يتفاعل مع النظام، ممّا يعني أنه يحذف التفاصيل الأكثر تعقيدا. يعتمد التجريد على <a href="http://blogs.aljazeera.net/blogs/2017/1/30/%D8%A7%D9%84%D8%AA%D8%AC%D8%B1%D9%8A%D8%AF-%D9%83%D9%8A%D9%81-%D9%86%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AA%D8%B9%D9%82%D9%8A%D8%AF" rel="external nofollow">استخلاص العناصر الرئيسية</a> المطلوبة لحل المُشكلة والتركيز على هذه العناصر فقط وإهمال كافة التفاصيل الأخرى.
</p>

<h2 id="الهيكلية">
	الهيكلية
</h2>

<p>
	يعرّف معمل التجريد تابعا Method لكل صنف منتَج. يغلّف كل تابع العامل <code>new</code> والصنف الفعلي المحدد للمنصّة؛ ثم بعد ذلك، تتم نمذجة كل “منصة” بصنف مشتق Derived class من المعمل.
</p>

<h3 id="مثال">
	مثال
</h3>

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

<p>
	يوفّر معمل التجريد <code>StampingEquipment</code> في مخطَّط الأصناف التالي تابعًا <code>stampPart</code> لكلّ مجموعة منتجات (<code>stampWheel</code>مثلا للعجلات Wheels و<code>stampDoor</code> للأبواب Doors). تستدعي الكائنات العميلة Clients (بقيّة أجزاء التطبيق) التابع المناسب لمجموعة المنتجات التي تريدها، مع تحديد نوعيّة (صنف) النموذج الذي تريد إنشاء كائن منه. يتضمّن التابع <code>stampPart</code> العامل <code>new</code> الذي ينشئ نموذجا جديدا باستدعاء الصنف الفعلي من بين مجموعات الأصناف <code>ModelXHood</code>، <code>ModelXWheels</code> و<code>ModelXDoor</code> (حيث <code>X</code> عدد يمثل معرّف نموذج من المنتجات). نحصُل من العملية السابقة على كائن من معمل التجريد <code>StampingEquipment</code> لكنّه يحمل خواصّ المنتج الذي نريده، ولا يتبقّى لنا للحصول على المنتج سوى استدعاء التابع المناسب: <code>stampHood</code>، <code>stampWheel</code> أو <code>stampDoor</code>.
</p>

<p style="text-align: center;">
	<img alt="Abstract_Factory_example1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="23766" data-unique="7fmuj9u1p" src="https://academy.hsoub.com/uploads/monthly_2017_06/Abstract_Factory_example1.png.2f825499c21a54fe1ad30a7b7f533bfd.png"></p>

<p>
	إن استدعينا مثلا التابع <code>اstampWhee</code> في معمل التجريد <code>StampingEquipment</code> مع تحديد الصنف <code>Model1Wheels</code> (النموذج رقم 1 من العجلات) فسنحصُل على كائن من الصنف <code>StampingEquipment</code> لديه تابع <code>stampWheel</code> يؤدّي استدعاءه إلى الحصول على كائن من الصنف <code>Model1Wheels</code>. إن أردنا الآن عجلات من النموذج رقم <code>2</code> فكلّ ما علينا فعله هو اتباع نفس الطريقة ولكن مع تحديد <code>Model2Wheels</code> بدلا من <code>Model1Wheels</code> عند استدعاء <code>اstampWhee</code> في معمل التجريد.
</p>

<p>
	نفس الشيء ينطبق على الكائنات الأخرى، مع تغيير اسم التابع في معمل التجريد حسب مجموعة الأصناف (<code>stampHood</code> بالنسبة لنماذج اﻷغطية <code>ModelXHood</code>، و<code>stampDoor</code> بالنسبة لنماذج الأبواب <code>ModelXDoor</code>).
</p>

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

<h2 id="قائمة-التدقيق">
	قائمة التدقيق
</h2>

<p>
	ينبغي - قبل اللجوء إلى نمط التصميم <strong>معمل التجريد</strong> التأكّد من حاجتك إليه عمليًّا وذلك بـ:
</p>

<ol>
<li>
		تقرير ما إذا كانت اعتمادات المنصة وخدمات الإنشاء هي المصدر الحالي للمشكلة.
	</li>
	<li>
		إعداد مصفوفة “منصات” مقابل “منتجات”.
	</li>
</ol>
<p>
	تأكّد عند استخدام هذا النمط من:
</p>

<ol>
<li>
		تعريف واجهة للمعمل تتكّون من تابع معمل لكل “منتج”.
	</li>
	<li>
		تعريف صنف مشتق من المعمل لكل منصة لتغليف كافة استدعاءات العامل <code>new</code>.
	</li>
	<li>
		حذف جميع استدعاءات العامل <code>new</code> من الصنف العميل وجعله يستخدم توابع المعمل في إنشاء الكائنات للمنتَج.
	</li>
</ol>
<p>
	ترجمة - بتصرّف - للمقال <a href="https://sourcemaking.com/design_patterns/abstract_factory" rel="external nofollow">Abstract Factory Design Pattern</a> لأصحابه Alexander Shvets, Gerhard Frey, Marina Pavlova. 
</p>

<p>
	حقوق الصورة البارزة محفوظة لـ <a href="http://www.freepik.com/free-vector/pattern-template_883511.htm" rel="external nofollow">Freepik</a>
</p>
]]></description><guid isPermaLink="false">504</guid><pubDate>Thu, 22 Jun 2017 19:53:27 +0000</pubDate></item><item><title>&#x646;&#x645;&#x637; &#x627;&#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A; &#x645;&#x62C;&#x645;&#x639; &#x627;&#x644;&#x643;&#x627;&#x626;&#x646;&#x627;&#x62A; Object pool pattern</title><link>https://academy.hsoub.com/programming/advanced/%D9%86%D9%85%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A-%D9%85%D8%AC%D9%85%D8%B9-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-object-pool-pattern-r501/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2017_06/main2.png.aa43a3a14258e097993f60912223b15e.png" /></p>

<p>
	يمكن لنمط التصميم البرمجي <strong>مجمع الكائنات Object pool</strong> أن يؤمّن دَفعة كبيرة للأداء ، ويكون أكثر فعالية في الحالات التي تكون فيها كلفة بدء عينة من الصنف Class instance مرتفعة، أو ترتفع فيها نسبة استهلال الأصناف مع انخفاض عدد العيّنات المستخدمة في آن واحد.
</p>

<p style="text-align: center;">
	<img alt="main2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="23730" data-unique="ytmai509p" src="https://academy.hsoub.com/uploads/monthly_2017_06/main2.png.4364096b461a93dc3099736d9ff410ac.png"></p>

<h2 id="المشكلة">
	المشكلة
</h2>

<p>
	تستخدم <strong>مجمعات الكائنات</strong> (والتي تعرف أيضا باسم <strong>مجمعات المصادر Resources pools</strong>) لإدارة تخبئة الكائنات Object caching. يستطيع العميل الذي يملك وصولا إلى <strong>مجمع الكائنات</strong> أن يتجنب إنشاء كائنات جديدة بسؤال المجمع عوضا عن ذلك عن كائن سبق استهلاله. ينشئ المجمع كائنات جديدة إذا كان فارغًا وينمو حجمُه، إلّا أنه من الممكن تقييد عدد الكائنات المُنشَأة.
</p>

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

<h2 id="المناقشة">
	المناقشة
</h2>

<p>
	يسمح <strong>مجمع الكائنات</strong> للعميل بـ”استعارة” الكائنات الموجودة فيه. يعيد العملاء الكائنات بعد استخدامها إلى المجمع، الذي يمكنه بعد ذلك إعارتها لعميل آخر ليستخدمها بدوره. إلا أننا لا نريد أن ينتظر عميلٌ إعادةَ كائن مستخدَم، فهذا مخالف للرغبة في تحسين اﻷداء؛ لذا فإن <strong>مجمع الكائنات</strong> يمكنه أن يستهل كائنات جديدة عندما تظهر الحاجة إليها، لكنْ يجب عليه أيضا أن يستخدم وسيلة لتنظيف الكائنات غير المستخدمة دوريّا.
</p>

<h2 id="الهيكلية">
	الهيكلية
</h2>

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

<p>
	تظهر في المخطّط التالي ثلاثةُ أصناف:
</p>

<p style="text-align: center;">
	<img alt="01_Object_pool1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="23729" data-unique="vp4f8scc9" src="https://academy.hsoub.com/uploads/monthly_2017_06/01_Object_pool1.png.557177551f4ce7004407e322994bbcd9.png"></p>

<ul>
<li>
		الصّنف <code>Reusable</code>: تتعاون الكائنات من هذا الصّنف مع بقيّة الكائنات (الكائنات العميلة المذكورة أدناه) لمدّة محدودة، تصبح بعدها كائنات الصّنف <code>Reusable</code> غير ضروريّة لعمل العملاء.
	</li>
	<li>
		الصّنف <code>Client</code>: تستخدم الكائنات التي تلعب دور العميل عيّنات من الكائنات القابلة لإعادة الاستخدام +(أي كائنات الصّنف <code>Reusable</code> المذكور في النقطة اﻷولى).
	</li>
	<li>
		الصّنف <code>ReusablePool</code>: تُدير الكائنات من هذا الصنف كائناتٍ قابلةً لإعادة الاستخدام (دور الصّنف <code>Reusable</code>) وتوفّرها للكائنات العميل +(الصّنف <code>Client</code>).
	</li>
</ul>
<p>
	ينبغي الانتباه هنا إلى أن الأصناف المذكورة أعلاه تمثّل أدوارا Roles، إذ يمكن أن يوجد أكثر من مجمع كائنات (الصّنف <code>ReusablePool</code>)، ولكلّ مجمع أصناف <code>Reusable</code> خاصّة به يوفّرها للعملاء الذين يطلُبون ذلك. يمكن لنفس العميل أن يطلُب خِدمات مجمعَيْ كائنات أو أكثر في نفس الوقت.
</p>

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

<p>
	يستدعي الكائن العميل <code>Client</code>، عندما يحتاج لعيّنة من الصّنف القابل لإعادة الاستخدام، التابعَ <code>acquireReusable</code> الذي يوفّره الصّنف <code>ReusablePool</code>. يحتفظ كائن <code>ReusablePool</code> مجموعة من الكائنات القابلة لإعادة الاستخدام <code>Reusable</code> غير المستخدمة حاليا.
</p>

<p>
	ينظُر كائن <code>ReusablePool</code> عند استدعاء التابع <code>acquireReusable</code> في مجموعة كائنات <code>Reusable</code> التي يحتفظ بها، ويحذف منها كائنا ويعيده إلى العميل الذي طلب ذلك. إذا كان المجمع فارغا فإن التابع <code>acquireReusable</code> ينشئ - إن استطاع - كائنا جديدا. إن لم يستطع التابع <code>acquireReusable</code> إنشاء كائن جديد فإنه ينتظر أن يُرجِع أحد الكائنات العميلة كائنَ <code>Reusable</code>.
</p>

<p>
	يمرّر الكائنُ العميل كائنَ الصّنف <code>Reusable</code> عند انتهائه من استخدامه إلى التابع <code>releaseReusable</code> الذي يوفّره كائن الصّنف <code>ReusablePool</code>. يعيد التابعُ <code>releaseReusable</code> الكائنَ الذي تلقّاه من العميل إلى مجمع كائنات <code>Reusable</code> غير المستخدمة.
</p>

<p>
	تقيّد كثير من التطبيقات التي تستخدم نمط التصميم <strong>مجمع الكائنات</strong>، لأسباب مختلفة، عددَ كائنات الصنف <code>Reusable</code> التي يمكن تواجدها. يكون كائن <code>ReusablePool</code> الذي ينشئ كائنات <code>Reusable</code> مسؤولا في هذه الحالة من احترام هذا الشرط. تتوفّر كائنات <code>ReusablePool</code> - في حالة تقييد عدد الكائنات - على تابع لتحديد عدد كائنات <code>Reusable</code> الذي لا ينبغي تجاوزه. يظهر هذا التابع في المخطَّط أعلاه باسم <code>setMaxPoolSize</code>.
</p>

<h2 id="قائمة-التدقيق">
	قائمة التدقيق
</h2>

<p>
	تأكّد عند استخدام نمط التصميم <strong>مجمع الكائنات</strong> من:
</p>

<ol>
<li>
		إنشاء صنف <strong>مجمع الكائنات</strong> بمصفوفة خاصة من اﻷصناف القابلة لإعادة الاستخدام داخله.
	</li>
	<li>
		إنشاء تابعَي طلب الكائنات وتحريرها ضمن صنف <strong>مجمع الكائنات</strong>.
	</li>
	<li>
		استخدام نمط الصّنف المنفرد Singleton لتطبيق <strong>مجمع الكائنات</strong>.
	</li>
</ol>
<p>
	ترجمة - بتصرّف - للمقال <a href="https://sourcemaking.com/design_patterns/object_pool" rel="external nofollow">Object Pool Design Pattern</a> لأصحابه Alexander Shvets ،Gerhard Frey وMarina Pavlova.
</p>

<p>
	حقوق الصورة البارزة محفوظة لـ <a href="http://www.freepik.com/free-vector/pattern-template_883511.htm" rel="external nofollow">Freepik</a>
</p>
]]></description><guid isPermaLink="false">501</guid><pubDate>Sun, 18 Jun 2017 07:40:00 +0000</pubDate></item><item><title>&#x646;&#x645;&#x637; &#x627;&#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A; &#x62A;&#x627;&#x628;&#x639; &#x627;&#x644;&#x645;&#x639;&#x645;&#x644; Factory method</title><link>https://academy.hsoub.com/programming/advanced/%D9%86%D9%85%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A-%D8%AA%D8%A7%D8%A8%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%84-factory-method-r499/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2017_06/main2.png.bbe785fe3400f643e0a42422f3c023d8.png" /></p>

<p>
	يهدف نمط التصميم تابع المعمل Factory Method إلى تعريف واجهة Interface لإنشاء الكائنات مع ترك مهمّة تحديد الصنف الذي سيُستهَل Instantiate إلى الأصناف المتفرّعة عن الواجهة.
</p>

<p style="text-align: center;">
	<img alt="main2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="23601" data-unique="g9megoaui" src="https://academy.hsoub.com/uploads/monthly_2017_06/main2.png.1a34a742592d682e9616d0c979d47d88.png"></p>

<p>
	يعرّف نمط تابع المعمل تابِعا (دالة) مشيّدا افتراضيا Virtual، كما ينطلق من مبدأ أن استخدام العامل <code>new</code> مؤذ ويجب تغليفه Encapsulate.
</p>

<p>
	<strong>ملاحظة:</strong> نعني بالتغليف Encapsulation في إطار البرمجة كائنيّة التوجه Object-oriented programming تلك الآليّة التي تتيح جمع البيانات (الخاصيّات Attributes) والإجراءات المطبّقة عليها (التوابع Methods) ضمن نفس الكائن، مع تقييد إمكانيّة وصول الكائنات الأخرى إلى عناصر الكائن.
</p>

<h2 id="المشكلة">
	المشكلة
</h2>

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

<h2 id="المناقشة">
	المناقشة
</h2>

<p>
	يشبه نمط <strong>تابع المعمل</strong> في إنشائها للكائنات طريقة القالب Template Method في تطبيقها للخوارزميات، إذ يقوم الصنف الأساسي بتحديد السلوك العام والقياسي (مستخدما ماسكات مكان افتراضية Virtual Placeholders واضحة لخطوات الإنشاء)، ومن ثم يفوّض إنشاء التفاصيل إلى الأصناف الفرعية التي يؤمنها العميل.<br>
	يجعل نمط <strong>تابع المعمل</strong> التصميم أكثر قابلية للتخصيص من دون تعقيده كثيرا. وفي حين تتطلب أنماط التصميم الأخرى أصنافا جديدة فإن <strong>تابع المعمل</strong> لا يحتاج سوى لتابع جديد.
</p>

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

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

<h2 id="الهيكلية">
	الهيكلية
</h2>

<p>
	تتداخل طريقة التطبيق الأصليّة - كما عرَّفها روّاد أنماط التصميم البرمجي - مع طريقة تطبيق <strong>معمل التجريد</strong>؛ إلا أن طريقة أخرى اشتهرت بعد ذلك.
</p>

<p>
	يتمثّل التطبيق الأكثر شعبية لنمط <strong>تابع المعمل</strong> في استخدام تابع ثابت Static يُرجِع كائنا من الصنف حيثُ يوجد التابع المذكور. يختلف التابع المُستخدَم عن التابع المشيّد Constructor في نقاط عدّة، منها:
</p>

<ul>
<li>
		أن الكائن المُرجَع قد يكون عيّنة من صنف فرعي.
	</li>
	<li>
		أنه يمكن استخدام كائن موجود - سبق إنشاءه - بدلا من إنشاء كائن جديد.
	</li>
	<li>
		أن توابع المعمل يمكنها أن تتسمّى بطريقة مغايرة لتلك التي تفرضها لغة البرمجة، ممّا يعني أن الأسماء ستكون أكثر دلالة على عمل التوابع. على سبيل المثال: <code>Color.make_HSB_color(float hue, float saturation, float brightness)</code> و<code>Color.make_RGB_color(float red, float green, float blue)</code>.
	</li>
</ul>
<p style="text-align: center;">
	<img alt="1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="23599" data-unique="crret6kbz" src="https://academy.hsoub.com/uploads/monthly_2017_06/1.png.90a1a4bc738cd7620bf570eb532061b7.png"></p>

<p>
	يستقبل التابع <code>()makeProduct</code> الثابت في الواجهة <code>Product</code> ضمن مخطَّط الأصناف أعلاه المُعطَيات ثمّ يقرّر بناءً عليها الصنف الفعلي للكائن الذي سيُرجعه (<code>ProductOne</code> أو <code>ProductTwo</code>).
</p>

<p>
	تُفصَل بقيّة أجزاء البرنامج كليّا عن التفاصيل الخاصّة بالأصناف المشتقة، ويصبح الإنشاء متعدد الأشكال Polymorphic ممكنا.
</p>

<p>
	<strong>ملحوظة:</strong> يشير مصطلح تعدّد الأشكال Polymorphism إلى القدرة على توفير صنف وحيد يعمل واجهةً لأصناف أخرى مختلفة. تستفيد الأصناف العميلة من الواجهة دون أن تعبأ بتفاصيل تنفيذ الأصناف (الفعلية) التي “تختبئ” وراءها.
</p>

<h2 id="مثال">
	مثال
</h2>

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

<p style="text-align: center;">
	<img alt="2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="23600" data-unique="argq3aaof" src="https://academy.hsoub.com/uploads/monthly_2017_06/2.png.7ac69a2733237d8e1aec8bc02c65fe2c.png"></p>

<h2 id="قائمة-التدقيق">
	قائمة التدقيق
</h2>

<ol>
<li>
		إن كان لديك هرمية توارث Inheritance hierarchy تمارس تعددية الأشكال، فكر في إضافة إمكانية الإنشاء متعدد الأشكال عن طريق تعريف تابع معمل ثابت Static في الصنف الأساسي.
	</li>
	<li>
		صمم معطيات Arguements تابع المعمل. ما هي النوعيات والخصائص الضرورية والكافية لتعريف الصنف المشتق الصحيح الذي سيُستهَل؟
	</li>
	<li>
		فكر في تصميم <strong>مجمع كائنات</strong> Object Pool يسمح بإعادة استخدام الكائنات عوضا عن إنشائها من الصفر.
	</li>
	<li>
		فكر في جعل جميع التوابع المشيّدة خاصّة Private أو محميّة Protected.
	</li>
</ol>
<p>
	ترجمة - بتصرّف - للمقال <a href="https://sourcemaking.com/design_patterns/factory_method" rel="external nofollow">Factory Method Design Pattern</a> لأصحابه Alexander Shvets، Gerhard Frey وMarina Pavlova.<br>
	حقوق الصورة البارزة محفوظة لـ <a href="http://www.freepik.com/free-vector/pattern-template_883511.htm" rel="external nofollow">Freepik</a>
</p>
]]></description><guid isPermaLink="false">499</guid><pubDate>Sun, 11 Jun 2017 21:06:00 +0000</pubDate></item><item><title>&#x646;&#x645;&#x637; &#x627;&#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x627;&#x644;&#x628;&#x627;&#x646;&#x64A; The Builder Design Pattern</title><link>https://academy.hsoub.com/programming/advanced/%D9%86%D9%85%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%A7%D9%86%D9%8A-the-builder-design-pattern-r495/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2017_06/main3.png.88dc693a12a4a484c724284ecc139241.png" /></p>

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

<p style="text-align: center;">
	<img alt="main3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="23602" data-unique="bavhv8ljh" src="https://academy.hsoub.com/uploads/monthly_2017_06/main3.png.ec82f12da6f1d2ce4cd1476c82166497.png"></p>

<h2 id="عرض-المشكل">
	عرض المشكل
</h2>

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

<h2 id="المناقشة">
	المناقشة
</h2>

<p>
	تُفصَل خوارزمية التفسير (أي القراءة والتحليل) في آليّة التخزين المُستدام (كملفات RTF) عن خوارزمية بناء وعرض أحد المنتجات المستهدفة (مثل: ASCII، TeX والمربعات الجانبية “ودجة” Widgets). ينصب التركيز/التمييز على إنشاء تجميعات معقدة.
</p>

<p>
	يستدعي الصنف القائد <code>Director</code> خدمات <strong>الباني</strong> أثناء تفسير التنسيق الخارجي. ينشئ الباني جزءًا من الكائن المعقد في كلّ مرة يُستدعى فيها، كما يُحافظ على جميع الحالات الوسيطة. عندما يكتمل المنتج، يحصل العميل (وهو كائن آخر من التطبيق يحتاج الوظيفة المصمَّمة وفق نمط الباني) على النتيجة من “الباني”.
</p>

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

<h3 id="مثال">
	مثال
</h3>

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

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

<h2 id="كيف-يطبق-نمط-التصميم-الباني">
	كيف يُطبَّق نمط التصميم “الباني”
</h2>

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

<p>
	يغلّف Encapsulate الصنف القارئ <code>Reader</code> تحليل المُدخلات المشتركة. تسمح هرمية الباني بالإنشاء متعدد الأشكال Polymorphic للعديد من الأهداف أو العروض المتميزة.
</p>

<p>
	<strong>ملاحظة:</strong> نعني بالتغليف Encapsulation في إطار البرمجة كائنيّة التوجه Object-oriented programming تلك الآليّة التي تتيح جمع البيانات (الخاصيّات Attributes) والإجراءات المطبّقة عليها (التوابع Methods) ضمن نفس الكائن، مع تقييد إمكانيّة وصول الكائنات الأخرى إلى عناصر الكائن.
</p>

<p>
	يبيّن مخطَّط الأصناف التالي الارتباط بين الأصناف الداخلة في تنفيذ هذا النمط:
</p>

<p style="text-align: center;">
	<img alt="builder.png" class="ipsImage ipsImage_thumbnailed" data-fileid="23487" data-unique="xelrhklmt" src="https://academy.hsoub.com/uploads/monthly_2017_06/builder.png.c1d474cb92546880ccc794e471b1fde5.png"></p>

<ul>
<li>
		يُغلّف تحليل المدخلات المشتركة ضمن صنف القارئ.
	</li>
	<li>
		يُصمَّم ميثاق (بروتوكول) قياسي لإنشاء جميع العروض المحتملة في المُخرجات. - تُحفظ خطوات عمل هذا البروتوكول في واجهة الباني (الصنف <code>Converter</code> في المخطَّط).
	</li>
	<li>
		يُعرّف صنف مشتق من الباني لكل عرض مستهدف (الأصناف <code>ASCIIConverter</code>، <code>PostScriptConverter</code> و<code>PDFConverter</code>في المخطَّط).
	</li>
	<li>
		ينشئ العميل كائنًا للقراءة وكائنًا للبناء، ثم يسجّل الأخيرَ لدى الأول.
	</li>
	<li>
		يطلب العميل من القارئ أن “يبني”.
	</li>
	<li>
		يطلب العميل من الباني أن يعيد النتيجة.
	</li>
</ul>
<p>
	 
</p>

<p>
	ترجمة - بتصرّف - للمقال <a href="https://sourcemaking.com/design_patterns/builder" rel="external nofollow">Builder Design Pattern</a> لأصحابه Alexander Shvets, Gerhard Frey, Marina Pavlova.<br>
	حقوق الصورة البارزة محفوظة لـ <a href="http://www.freepik.com/free-vector/pattern-template_883511.htm" rel="external nofollow">Freepik</a>
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="23488" href="https://academy.hsoub.com/uploads/monthly_2017_06/main2.png.aff85c29663d516dde7d898c41411c1d.png" rel=""><img alt="main2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="23488" src="https://academy.hsoub.com/uploads/monthly_2017_06/main2.png.aff85c29663d516dde7d898c41411c1d.png"></a>
</p>
]]></description><guid isPermaLink="false">495</guid><pubDate>Sat, 10 Jun 2017 21:15:00 +0000</pubDate></item><item><title>&#x623;&#x646;&#x645;&#x627;&#x637; &#x627;&#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A; Design patterns</title><link>https://academy.hsoub.com/programming/advanced/%D8%A3%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A-design-patterns-r498/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2017_06/main2.png.363f5e01cb35b64638ec2472171aac87.png" /></p>

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

<p style="text-align: center;">
	<img alt="main2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="23578" data-unique="mgbi44ihw" src="https://academy.hsoub.com/uploads/monthly_2017_06/main2.png.c9661c7679d2472efea124f8932029f2.png"></p>

<h2 id="استخدام-أنماط-التصميم">
	استخدام أنماط التصميم
</h2>

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

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

<h3 id="أنماط-التصميم-الإنشائية-creational-design-patterns">
	أنماط التصميم الإنشائية Creational design patterns
</h3>

<p>
	تعنى نماذج التصميم هذه باستهلال الأصناف Class instantiation. يمكن تقسيم هذه النوعيّة من النماذج إلى فئتيْن: نماذج لإنشاء الأصناف ونماذج لإنشاء الكائنات. في حين تستخدم نماذج إنشاء الفئات التوريث Inheritance بفعالية في عملية التكوين، فإن نماذج إنشاء الكائنات تستخدم التفويض Delegation بفعالية لإنجاز العمل.<br>
	من أمثلة أنماط التصميم الإنشائية:
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%86%D9%85%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%85%D8%B9%D9%85%D9%84-%D8%A7%D9%84%D8%AA%D8%AC%D8%B1%D9%8A%D8%AF-abstract-factory-r504/" rel="">نمط معمل التجريد Abstract Factory</a>: يخلق عينة Instance لعدة عائلات من الأصناف.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%86%D9%85%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%A7%D9%86%D9%8A-the-builder-design-pattern-r495/" rel="">نمط الباني Builder</a>: يقوم بفصل عملية بناء الكائن Object Construction عن عملية تمثيله Representation.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%86%D9%85%D8%B7-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A-%D8%AA%D8%A7%D8%A8%D8%B9-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D9%84-factory-method-r499/" rel="">نمط طريقة المعمل Factory Method</a>: يخلق عينة لعدة أصناف مشتقة Derived classes.
	</li>
</ul>
<h3 id="أنماط-التصميم-الهيكلية-structural-design-patterns">
	أنماط التصميم الهيكلية Structural design patterns
</h3>

<p>
	تعنى أنماط التصميم هذه بتركيب Composition الكائنات والأصناف. تستخدم الأنماطُ الهيكليّة لإنشاء الأصناف التوريثَ Inheritance لتركيب واجهات Interfaces، أما الأنماطُ الهيكليّة لإنشاء الكائنات فتعرّف طرقا لتكوين الكائنات بهدف الحصول على وظائف جديدة.<br>
	من أمثلة هذه الأنماط:
</p>

<ul>
<li>
		نمط المحول Adapter: يربط الواجهات Interfaces بأصناف مختلفة.
	</li>
	<li>
		نمط الجسر Bridge: يفصل واجهة الكائن عن تطبيقه Implementation.
	</li>
	<li>
		نمط المظهر Facade: صنف مفرد يمثل نظاما فرعيا Subsystem كاملا.
	</li>
	<li>
		نمط بيانات الصنف الخاصة Private Class Data: يقيد وصول <a href="https://academy.hsoub.com/programming/php/laravel/%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%B1%D8%AC%D8%B9%D8%A7%D8%AA-accessors-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%AF%D9%84%D8%A7%D8%AA-mutators-%D9%81%D9%8A-laravel-r365/" rel="">المسترجعات Accessors والمعدّلات Mutators</a> إلى خاصيّات الصنف.
	</li>
</ul>
<h3 id="أنماط-التصميم-السلوكية-behavioral-design-patterns">
	أنماط التصميم السلوكية Behavioral design patterns
</h3>

<p>
	تعنى أنماط التصميم هذه بالتواصل Communication بين كائنات الأصناف. النماذج السلوكية هي تلك النماذج التي تهتم على وجه الخصوص بالتواصل بين الكائنات، ومن بينها:
</p>

<ul>
<li>
		نمط سلسلة المسؤوليات Chain of Responsibility: طريقة لتمرير الطلب Request بين سلسلة من الكائنات.
	</li>
	<li>
		نمط السيطرة Command: يقوم بتغليف Encapsulate الطلب على هيئة كائن.
	</li>
	<li>
		نمط المفسّر Interpreter: طريقة لتضمين عناصر اللغة في البرنامج.
	</li>
	<li>
		نمط المكرّر Iterator: يؤمن وصولا تسلسليا للعناصر في مجموعة ما.<br>
		نموذج الوسيط Mediator
	</li>
</ul>
<h2 id="النقد">
	النقد
</h2>

<p>
	انتقد بعض العاملين في مجال علوم الحاسب مفهوم أنماط التصميم وأبدوا اعتراضاتٍ عليه نجملها في ما يلي.
</p>

<h3 id="يستهدف-المشكلة-الخاطئة">
	يستهدف المشكلة الخاطئة
</h3>

<p>
	تظهر الحاجة إلى الأنماط عند استخدام لغات البرمجة أو التقنيات التي لا تملك قدرة تجريد Abstraction كافية. في الحالة المثالية، لا ينبغي نسخ مفهوم ما بل تجدر الإشارة إليه. ولكن عند الإشارة إلى شيء ما بدل نسخه فلن يكون هناك نمط لتسميته أو الدلالة عليه، وهذا ما كتبه بول غراهام Paul Graham في مقال بعنوان “انتقام المهووسين Revenge of the Nerds “.
</p>

<p>
	يقدم بيتر نورفيغ Peter Norvig نقاشا مشابها، حيث يوضح أن 16 نمطا من أصل 23 في كتاب أنماط التصميم (الذي يركز بشكل أساسي على ++C ) بُسِّطت أو ألغيت (عن طريق دعم اللغة المباشر لها) في كل من لغتي Lisp و Dylan.
</p>

<h3 id="يفتقر-إلى-الأسس-الرسمية">
	يفتقر إلى الأسس الرسمية
</h3>

<p>
	لقد كانت دراسة أنماط التصميم متخصّصة جدا، وقد جادل البعض في الحاجة الملحة لوضع هذا المفهوم في إطار أكثر رسمية. في مؤتمر OOPSLA (البرمجة كائنيّة التوجّه: الأنظمة، اللغات والتطبيقات) عام 1999، خضعت عصابة الأربعة Gang of Four (بتعاونهم الكامل) إلى محاكمة علنية، اتهموا فيها بعدة جرائم تمس علوم الحاسب. وقد تمت إدانتهم من قبل ثلثي المحلفين الذين حضروا المحاكمة.
</p>

<p>
	<strong>ملحوظة:</strong> غالبا ما يُشار إلى مؤلّفي كتاب Design Patterns: Elements of Reusable Object-Oriented Software (أنماط التصميم: مكوّنات من البرامج كائنيّة التوجّه القابلة لإعادة الاستخدام) الذي روّج لأنماط التصميم، غالبا ما يُشار إليهم باسم “عصابة الأربعة”.
</p>

<h3 id="يقود-إلى-حلول-غير-فعالة">
	يقود إلى حلول غير فعالة
</h3>

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

<h3 id="لا-يختلف-كثيرا-عن-التجريدات-الأخرى">
	لا يختلف كثيرا عن التجريدات الأخرى
</h3>

<p>
	يزعم بعض المؤلفين أن أنماط التصميم لا تختلف كثيرا عن أشكال التجريد الأخرى، وبأن استخدام مصطلح جديد (استعير من مجتمع العمارة) لوصف ظاهرة موجودة سابقا في مجال البرمجة يعتبر أمرا غير ضروري. تعدّ بنية <a href="https://academy.hsoub.com/programming/php/laravel/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-laravel-5-r218/#%D8%A8%D9%86%D9%8A%D8%A9-mvc" rel="">بنية MVC</a> ( “نموذج – عرض – متحكم”، “Model – View – Controller”) مثالا عن “نمط” يسبق مفهوم أنماط التصميم بعدة سنوات. ويجادل البعض بأن أول مساهمة في مجتمع أنماط التصميم (وكتاب عصابة الأربعة) هو استخدام كتاب A pattern language (لغة نمط) طريقةً للتوثيق Documentation؛ وهي ممارسة غالبا ما يتجاهلها المختصّون عند عرضهم لأصول مفهوم أنماط التصميم.
</p>

<p>
	ترجمة - بتصرّف - لمقال <a href="https://sourcemaking.com/design_patterns" rel="external nofollow">Design Patterns</a> لأصحابه Alexander Shvets, Gerhard Frey, Marina Pavlova.<br>
	حقوق الصورة البارزة محفوظة لـ <a href="http://www.freepik.com/free-vector/pattern-template_883511.htm" rel="external nofollow">Freepik</a>
</p>
]]></description><guid isPermaLink="false">498</guid><pubDate>Sun, 04 Jun 2017 21:00:00 +0000</pubDate></item><item><title>&#x645;&#x628;&#x627;&#x62F;&#x626; SOLID &#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x627;&#x62A;: &#x645;&#x628;&#x62F;&#x623; &#x639;&#x643;&#x633; &#x627;&#x644;&#x62A;&#x627;&#x628;&#x639;&#x64A;&#x651;&#x629; Dependency Inversion Principle</title><link>https://academy.hsoub.com/programming/advanced/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-solid-%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D9%85%D8%A8%D8%AF%D8%A3-%D8%B9%D9%83%D8%B3-%D8%A7%D9%84%D8%AA%D8%A7%D8%A8%D8%B9%D9%8A%D9%91%D8%A9-dependency-inversion-principle-r397/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_12/solid-DIP.png.b69e55bf6d063b7f71309de2db54cfdc.png" /></p>

<p>
	مبدأ عكس التابعيّة Dependency Inversion Principle أو اختصارًا DIP، هو آخر مبادئ التصميم الكائنيّ SOLID ويتمتّع بمزايا كبيرة عند تطبيقه بالشكل السليم.
</p>

<p>
	أوّل من قدّم هذا المبدأ هو <a href="http://en.wikipedia.org/wiki/Robert_C._Martin" rel="external nofollow">روبرت مارتن</a> في <a href="http://www.objectmentor.com/publications/dip.pdf" rel="external nofollow">مقالته</a> التي نشرها عام 1996. أشار روبرت إلى أنّ الأسلوب الشائع في تصميم التابعيّة dependency ضمن المشاريع البرمجيّة في جعل الوحدات البرمجيّة عالية المستوى تعتمد على الوحدات البرمجيّة منخفضة المستوى بشكل مباشر هو أسلوب غير عمليّ ويؤدّي إلى مشاكل كبيرة عند إعادة استخدام الوحدات البرمجيّة عالية المستوى، تتمثّل هذه المشاكل في إجراء تعديلات برمجيّة عديدة عليها كي تتلاءم مع الاستخدام الجديد لها، وهذا بالطبع أمر غير جيّد.
</p>

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

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

<p>
	بالنسبة للقسم الثاني من المبدأ (الفقرة ب) فهو ينبّه بأنّ الواجهات ينبغي ألّا تصمّم وفقًا للوحدات البرمجيّة منخفضة المستوى (التفاصيل). حيث ينبغي أن تُحقّق الأصناف الوجهات على نفس مستوى التجريد الذي تتمتّع به الوحدات البرمجيّة عالية المستوى.
</p>

<h2>
	مثال عن عكس التابعيّة
</h2>

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

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_6599_10">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Button</span><span class="pln">
</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">void</span><span class="pln"> makeVisible</span><span class="pun">();</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Window</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Button</span><span class="pun">*</span><span class="pln"> okButton</span><span class="pun">;</span><span class="pln">
    </span><span class="typ">Button</span><span class="pun">*</span><span class="pln"> cancelButton</span><span class="pun">;</span><span class="pln">

    </span><span class="typ">Window</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
        okButton </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">;</span><span class="pln">
        okButton</span><span class="pun">-&gt;</span><span class="pln">makeVisible</span><span class="pun">();</span><span class="pln">

        cancelButton </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">;</span><span class="pln">
        cancelButton</span><span class="pun">-&gt;</span><span class="pln">makeVisible</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

</span><span class="pun">};</span></pre>

<p>
	<br>
	 
</p>

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

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6599_12">
<span class="pln">class IButton
{
    public:
        static virtual IButton* getInstance() = 0; // factory method
        virtual void show() = 0;
};

class Window
{
    IButton* okButton;
    IButton* cancelButton;

    public:
        Window()
        {
            okButton = IButton::getInstance();
            okButton-&gt;show();

            cancelButton = IButton::getInstance();
            cancelButton-&gt;show();
        }
};

class Button : public IButton
{
    public:
        void show();
};</span></pre>

<p>
	<br>
	 
</p>

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

<p>
	<br>
	 
</p>

<p>
	المصادر
</p>

<p>
	<a href="http://www.objectmentor.com/publications/dip.pdf" ipsnoembed="false" rel="external nofollow">http://www.objectmentor.com/publications/dip.pdf</a>
</p>

<p>
	<a href="http://www.oodesign.com/dependency-inversion-principle.html" ipsnoembed="false" rel="external nofollow">http://www.oodesign.com/dependency-inversion-principle.html</a>
</p>

<p>
	<a href="http://en.wikipedia.org/wiki/Dependency_inversion_principle" ipsnoembed="false" rel="external nofollow">http://en.wikipedia.org/wiki/Dependency_inversion_principle</a>
</p>

<p>
	<br>
	 
</p>

<p>
	ترجمة -وبتصرّف- للمقال <a href="http://radek.io/2011/08/10/dependency-inversion-principle" rel="external nofollow">Dependency Inversion Principle</a> لصاحبه Radek Pazdera.
</p>
]]></description><guid isPermaLink="false">397</guid><pubDate>Sun, 20 Nov 2016 13:18:00 +0000</pubDate></item><item><title>&#x645;&#x628;&#x627;&#x62F;&#x626; SOLID &#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x627;&#x62A;: &#x645;&#x628;&#x62F;&#x623; &#x641;&#x635;&#x644; &#x627;&#x644;&#x648;&#x627;&#x62C;&#x647;&#x627;&#x62A; Interface Segregation Principle</title><link>https://academy.hsoub.com/programming/advanced/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-solid-%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D9%85%D8%A8%D8%AF%D8%A3-%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-interface-segregation-principle-r396/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_12/solid-ISP.png.172e69b0e087f5c917b08f48e5f5b51d.png" /></p>

<p>
	مبدأ فصل الواجهات <strong>Interface Segregation Principle</strong> أو اختصارًا <strong><a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-solid-%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D9%85%D8%A8%D8%AF%D8%A3-%D9%84%D9%8A%D8%B3%D9%83%D9%88%D9%81-%D9%84%D9%84%D8%A7%D8%B3%D8%AA%D8%A8%D8%AF%D8%A7%D9%84-liskov-substitution-principle-r382/" rel="">ISP</a></strong> هو أحد المبادئ الشهيرة من مبادئ SOLID في التصميم الكائنيّ. أوّل من قدّم هذا المبدأ <a href="http://en.wikipedia.org/wiki/Robert_C._Martin" rel="external nofollow">روبرت مارتن</a> في سلسلة مقالات في عام 1996، ويهدف هذا المبدأ إلى تجنُّب إنشاء واجهات "سمينة".
</p>

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

<p>
	تظهر الواجهات السمينة (أو الملوّثة) بسبب توسعة صنف واجهة حالي ببعض النواحي الوظيفيّة الجديدة المفيدة لمجموعة جزئيّة فقط من التوابع methods. تؤدّي هذه الظاهرة في نهاية الأمر إلى إنشاء توابع ليس لها أيّ فائدة سوى تحقيق الواجهة للتمكّن من استخدامها. وهذا أمر سيّء بالطبع. تُعتبر تلك التوابع خطرة وهي تخرق على أيّة حال مبدأ LSP أيضًا. ينص مبدأ ISP كما كتبه المؤلّف:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		Quote
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			لا ينبغي إجبار المستخدمين على استخدام واجهات لا يحتاجونها.
		</p>
	</div>
</blockquote>

<p>
	 
</p>

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

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_9287_11">
<span class="com">/* Bad example */</span><span class="pln">
</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">CarOperation</span><span class="pln">
</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">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> steer</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> degrees</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="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> pullHandbrake</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="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> accelerate</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="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> shift</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> gear</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="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> toggleAirConditioning</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></pre>

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

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_9287_13">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">BasicCarOperation</span><span class="pln">
</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">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> steer</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> degrees</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="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> pullHandbrake</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="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> accelerate</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">class</span><span class="pln"> </span><span class="typ">GearboxCarOperation</span><span class="pln">
</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">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> shift</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> gear</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">class</span><span class="pln"> </span><span class="typ">AirConditioningCarOperation</span><span class="pln">
</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">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> toggleAirConditioning</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">class</span><span class="pln"> </span><span class="typ">AlfaRomeo166</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">BasicCarOperation</span><span class="pun">,</span><span class="pln"> </span><span class="typ">GearboxCarOperation</span><span class="pun">,</span><span class="pln"> </span><span class="typ">AirConditioningCarOperation</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="com">/* Implementation of all the interfaces. */</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">SkodaFavorit136L</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">BasicCarOperation</span><span class="pun">,</span><span class="pln"> </span><span class="typ">GearboxCarOperation</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="com">/* No air conditioning for old cars. */</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	سيلجأ الصنفان الابنان AlfaRomeo166 وSkodaFavorit136L إلى الوراثة المتعدّدة من الأصناف الواجهات. فإذا أردنا تشغيل جهاز التكييف في سيّارة ما عن طريق الدالّة beCool فسيكون ذلك كالتالي:
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_9287_16">
<span class="kwd">void</span><span class="pln"> beCool</span><span class="pun">(</span><span class="typ">AirConditioningCarOperation</span><span class="pun">*</span><span class="pln"> vehicle</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    vehicle</span><span class="pun">-&gt;</span><span class="pln">toggleAirConditioning</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<p>
	المصادر:
</p>

<p>
	<a href="http://www.oodesign.com/interface-segregation-principle.html" ipsnoembed="false" rel="external nofollow">http://www.oodesign.com/interface-segregation-principle.html</a>
</p>

<p>
	<a href="http://en.wikipedia.org/wiki/Interface_segregation_principle" ipsnoembed="false" rel="external nofollow">http://en.wikipedia.org/wiki/Interface_segregation_principle</a>
</p>

<p>
	<a href="http://www.objectmentor.com/resources/articles/isp.pdf" ipsnoembed="false" rel="external nofollow">http://www.objectmentor.com/resources/articles/isp.pdf</a><br>
	 
</p>

<p>
	ترجمة -وبتصرّف- للمقال  <a href="http://radek.io/2011/08/12/interface-segregation-principle-in-software-design/" rel="external nofollow">Interface Segregation Principle</a> لصاحبه Radek Pazdera.
</p>
]]></description><guid isPermaLink="false">396</guid><pubDate>Sun, 30 Oct 2016 23:00:00 +0000</pubDate></item><item><title>&#x645;&#x628;&#x627;&#x62F;&#x626; SOLID &#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x627;&#x62A;: &#x645;&#x628;&#x62F;&#x623; &#x644;&#x64A;&#x633;&#x643;&#x648;&#x641; &#x644;&#x644;&#x627;&#x633;&#x62A;&#x628;&#x62F;&#x627;&#x644; Liskov Substitution Principle</title><link>https://academy.hsoub.com/programming/advanced/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-solid-%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D9%85%D8%A8%D8%AF%D8%A3-%D9%84%D9%8A%D8%B3%D9%83%D9%88%D9%81-%D9%84%D9%84%D8%A7%D8%B3%D8%AA%D8%A8%D8%AF%D8%A7%D9%84-liskov-substitution-principle-r382/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_10/solid-liskov-substitution-principle.png.5bc3f90f287457d4f483b696e3a6052d.png" /></p>

<p>
	مبدأ آخر من <a href="https://academy.hsoub.com/tags/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6%20solid/" rel="">مبادئ التصميم الكائنيّ التوجّه ضمن مبادئ SOLID</a> يُطلق عليه اسم مبدأ ليسكوف للاستبدال Liskov Substitution Principle ويُرمز له اختصارًا بالرمز LSP.
</p>

<p style="text-align: center;">
	<img alt="solid-liskov-substitution-principle.png" class="ipsImage ipsImage_thumbnailed" data-fileid="19251" data-unique="p8vi785nj" src="https://academy.hsoub.com/uploads/monthly_2016_10/solid-liskov-substitution-principle.png.42ae44c26a4a40f6bde41045513e28ca.png"></p>

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

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

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			"المطلوب هنا هو شيء يشبه خاصيّة الاستبدال التالية: إذا كان من أجل كل كائن O1 من النوع S يوجد كائن O2 من النوع T، فمن أجل جميع البرامج P المعرّفة ضمن النوع T بحيث أنّ سلوك البرنامج P لا يتغيّر عند استبدال O2 بـ O1 فعندها يكون S هو نوع فرعي من T."
		</p>
	</div>
</blockquote>

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

	<div class="ipsQuote_contents ipsClearfix">
		<p style="text-align: left;">
			What is wanted here is something like the following substitution property: If for each object O1 of type S there is an object O2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when O1 is substituted for O2 then S is a subtype of T.
		</p>
	</div>
</blockquote>

<p>
	قد يبدو الكلام السابق مبهمًا بعض الشيء، يمكننا توضيحه بالشكل التالي: "إذا استطعنا استبدال كل كائن O1 مكان كائن O2 فمن الممكن الجزم بأنّ S هو نوع فرعي (نوع ابن) للنوع T". ولكن في بعض الأحيان رغم أنّ S هو نوع فرعي للنوع T ولكن لا يمكن الاستبدال بين كائناتهما بالصورة الموضّحة قبل قليل وهذا بالطبع أمر غير جيّد. أعادت باربارا ليسكوف بالاشتراك مع جانيت وينغ Jeannette Wing صياغة المبدأ السابق بشكل مختصر أكثر في عام 1994 ليصبح على الشكل التالي:
</p>

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

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			بفرض أنّ (q(x هي خاصيّة يحملها أيّ كائن x من النوع T. عندها يجب أن تحمل الكائنات y من النوع S الخاصيّة (q(y بحيث يكون S هو النوع الفرعي من النوع T.
		</p>
	</div>
</blockquote>

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

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

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			الدوال التي تستخدم مؤشّرات pointers أو مراجع references إلى أصناف آباء، يجب أن تكون قادرةً على استخدام كائنات من الأنواع الأبناء بدون المعرفة المسبقة بها.
		</p>
	</div>
</blockquote>

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

<p>
	من البديهي تمامًا أن يكون لدينا صنف اسمه <span style="font-family:courier new,courier,monospace;">Square</span> (مربّع) يكون صنفًا ابنًا للصنف <span style="font-family:courier new,courier,monospace;">Rectangle</span> (مستطيل)، فمن الناحية الرياضيّة يُعتبر المربّع حالة خاصّة من المستطيل، فهو مستطيل تساوى فيه بعداه. لنعبّر في البداية عن الصنف <span style="font-family:courier new,courier,monospace;">Rectangle</span> برمجيًّا بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_294_8">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
	</span><span class="kwd">int</span><span class="pln"> width</span><span class="pun">;</span><span class="pln">
	</span><span class="kwd">int</span><span class="pln"> height</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">int</span><span class="pln"> getWidth</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"> width</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
		</span><span class="kwd">int</span><span class="pln"> getHeight</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"> height</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">

	virtual </span><span class="kwd">void</span><span class="pln"> setWidth</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> value</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"> value</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
	virtual </span><span class="kwd">void</span><span class="pln"> setHeight</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> height </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="pun">};</span></pre>

<p>
	وبما أنّ المربّع هو حالة خاصّة من المستطيل كما أسلفنا، فيمكن كتابة الصنف <span style="font-family:courier new,courier,monospace;">Square</span> مع إعادة تعريف الطريقتين <span style="font-family:courier new,courier,monospace;">setWidth</span> و <span style="font-family:courier new,courier,monospace;">setHeight</span>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_294_10">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Square</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Rectangle</span><span class="pln">
</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">void</span><span class="pln"> setWidth</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> value</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"> value</span><span class="pun">;</span><span class="pln"> height </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="kwd">void</span><span class="pln"> setHeight</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> value</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"> value</span><span class="pun">;</span><span class="pln"> height </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="pun">};</span></pre>

<p>
	سيضمن هذا التعديل على الطريقتين <span style="font-family:courier new,courier,monospace;">setWidth</span> و <span style="font-family:courier new,courier,monospace;">setHeight</span> ضمن الصنف <span style="font-family:courier new,courier,monospace;">Square</span> أنّ أي كائن (مربّع) ننشئه من الصنف <span style="font-family:courier new,courier,monospace;">Square</span> ستكون أضلاعه الأربعة متساوية الطول. لننظر الآن إلى الدّالة التالية التي سنستخدمها لتجريب البنية الكائنيّة السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_294_12">
<span class="pln">bool test</span><span class="pun">(</span><span class="typ">Rectangle</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">rectangle</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
	rectangle</span><span class="pun">.</span><span class="pln">setWidth</span><span class="pun">(</span><span class="lit">2</span><span class="pun">);</span><span class="pln">
	rectangle</span><span class="pun">.</span><span class="pln">setHeight</span><span class="pun">(</span><span class="lit">3</span><span class="pun">);</span><span class="pln">

	</span><span class="kwd">return</span><span class="pln"> rectangle</span><span class="pun">.</span><span class="pln">getWidth</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> rectangle</span><span class="pun">.</span><span class="pln">getHeight</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">6</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تقبل الدّالة <span style="font-family:courier new,courier,monospace;">test</span> تمرير كائن من النوع <span style="font-family:courier new,courier,monospace;">Rectangle</span> أو كائن من النوع <span style="font-family:courier new,courier,monospace;">Square</span>، وهذا جائز بالطبع لأنّ <span style="font-family:courier new,courier,monospace;">Square</span> هو نوع ابن للنوع <span style="font-family:courier new,courier,monospace;">Rectangle</span>. السؤال هنا هو ماذا سيحدث عند تمرير كائن من النوع <span style="font-family:courier new,courier,monospace;">Square</span> إلى الدّالة <span style="font-family:courier new,courier,monospace;">test</span>؟ ستُعيد الدّالة القيمة <span style="font-family:courier new,courier,monospace;">false</span> رغم أنّ التقييم يجري على مرجع من النوع <span style="font-family:courier new,courier,monospace;">Rectangle</span> (وسيط الدّالة). المشكلة هنا أنّه رغم أنّ المربّع هو مستطيل من الناحية الرياضيّة إلّا أنّه لا يتشارك السلوك نفسه معه. وهذا يُعتبر خرقًا واضحاً لمبدأ الاستبدال. فالكائنات من النوع <span style="font-family:courier new,courier,monospace;">Rectangle</span> لا يمكن أن يتمّ استبدالها بكائنات من النوع <span style="font-family:courier new,courier,monospace;">Square</span> رغم أنّ النوع <span style="font-family:courier new,courier,monospace;">Square</span> هو نوع ابن للنوع <span style="font-family:courier new,courier,monospace;">Rectangle</span>، لأنّ ذلك سيؤدّي إلى تغيّر في سلوك الطرائق الموجودة ضمن الصنف <span style="font-family:courier new,courier,monospace;">Rectangle</span> كما هو واضح.
</p>

<p>
	لقد أوضح برتراند ماير هذه المسألة أيضًا على الشكل التالي:
</p>

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

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			عند إعادة تعريف إجراء في نوع ابن، يمكننا فقط، استبدال شروطه البادئة preconditions بشروط أضعف، وشروطه اللّاحقة postconditions بشروط أقوى.
		</p>
	</div>
</blockquote>

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

<p>
	في مسألتنا السابقة (مسألة المربّع والمستطيل)، لم تكن هناك أيّ شروط بادئة، ولكن كان هناك شرط لاحق للطريقة <span style="font-family:courier new,courier,monospace;">setHeight</span> وهو أنّ هذه الطريقة يجب ألّا تُغيّر العرض <span style="font-family:courier new,courier,monospace;">width</span>، وهذا الشرط تمّ خرقه (أصبح أضعف) عندما أعدنا تعريف الطريقة <span style="font-family:courier new,courier,monospace;">setHeight</span> ضمن النوع <span style="font-family:courier new,courier,monospace;">Square</span>.
</p>

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

<p>
	ترجمة -وبتصرّف- للمقال <a href="http://radek.io/2011/08/09/liskov-substitution-principle/" rel="external nofollow">Liskov Substitution Principle</a> لصاحبه Radek Pazdera.
</p>
]]></description><guid isPermaLink="false">382</guid><pubDate>Fri, 07 Oct 2016 17:38:00 +0000</pubDate></item><item><title>&#x645;&#x628;&#x627;&#x62F;&#x626; SOLID &#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x627;&#x62A;: &#x645;&#x628;&#x62F;&#x623; &#x627;&#x644;&#x641;&#x62A;&#x62D; &#x648;&#x627;&#x644;&#x625;&#x63A;&#x644;&#x627;&#x642; Open/Closed Principle</title><link>https://academy.hsoub.com/programming/advanced/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-solid-%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D9%85%D8%A8%D8%AF%D8%A3-%D8%A7%D9%84%D9%81%D8%AA%D8%AD-%D9%88%D8%A7%D9%84%D8%A5%D8%BA%D9%84%D8%A7%D9%82-openclosed-principle-r374/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_08/solid-open-closed-principle.png.fd375a1e9348a8a0ff3c2f4b5b61c3ac.png" /></p>

<p>
	يُعتبر مبدأ الفتح والإغلاق Open/Closed Principle أو اختصارًا OCP، من المبادئ التي تساعد مطوّري البرمجيّات على تحقيق تصاميم برمجيّة عالية الجودة. على أيّة حال، قد يكون من الصعب أحيانًا أن نوضّح ما الذي نعنيه بالبرمجيّات عالية الجودة. بالعودة إلى المبدأ OCP، يعود الفضل إلى برتراند ماير في وضع مصطلح مبدأ الفتح والإغلاق، حيث ظهر أوّل الأمر في كتابه البنية كائنيّة التوجّه للبرمجيّات "Object Oriented Software Construction" ينص هذا المبدأ على ما يلي:
</p>

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

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			يجب أن تكون كيانات entities البرنامج (الأصناف، الوحدات البرمجيّة modules، الدوال functions، ...الخ) مفتوحةً للتوسّع ومغلقةً للتعديل.
		</p>
	</div>
</blockquote>

<p style="text-align: center;">
	<img alt="solid-open-closed-principle.png" class="ipsImage ipsImage_thumbnailed" data-fileid="18852" data-unique="ox4kb3ap5" src="https://academy.hsoub.com/uploads/monthly_2016_08/solid-open-closed-principle.png.7852129cfa08824c9ea617b99de79100.png"></p>

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

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

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5030_7">
<span class="pln">def area(geometric_entity):

	if geometric_entity.type() == SQUARE:
		return geometric_entity.a * geometric_entity.a

	elif geometric_entity.type() == CIRCLE:
		return PI * geometric_entity.r * geometric_entity.r
	
	else:
		raise UnknownEntityError("I literally have no idea.")</span></pre>

<p>
	قد توحي الشيفرة السابقة بالبساطة أوّل الأمر، ولكنّها تُظهر جانبًا أساسيًّا من مبدأ OCP. فإذا أردنا مثلًا أن تدعم الدالّة السابقة إمكانية حساب مساحة مستطيل فيمكن ذلك بسهولة وذلك بإضافة مقطع elif جديد. ولكن بالمتابعة على هذا المنوال، وفي حالة حساب مساحة شكل هندسي غير قياسي، فستتحول الأسطر البرمجيّة البسيطة السابقة إلى ما يزيد عن 1500 سطر برمجي لحساب مساحة هذا الشكل باستخدام تكامل ريمان Riemann Integral، مما سيجعل هذه الأسطر كوحش برمجيّ إذا لم يلتهمك، فإنّ مدير المشاريع سيفعل ذلك حتمًا!
</p>

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

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

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

<p>
	ترجمة -وبتصرّف- للمقال <a href="http://radek.io/2011/08/04/openclosed-principle-in-software-design/" rel="external nofollow">Open/Closed Principle in Software Design</a> لصاحبه Radek Pazdera.
</p>
]]></description><guid isPermaLink="false">374</guid><pubDate>Tue, 16 Aug 2016 08:45:00 +0000</pubDate></item><item><title>&#x645;&#x628;&#x627;&#x62F;&#x626; SOLID &#x644;&#x62A;&#x635;&#x645;&#x64A;&#x645; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x627;&#x62A;: &#x645;&#x628;&#x62F;&#x623; &#x627;&#x644;&#x645;&#x633;&#x624;&#x648;&#x644;&#x64A;&#x629; &#x627;&#x644;&#x648;&#x627;&#x62D;&#x62F;&#x629; Single Responsibility Principle</title><link>https://academy.hsoub.com/programming/advanced/%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-solid-%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D9%85%D8%A8%D8%AF%D8%A3-%D8%A7%D9%84%D9%85%D8%B3%D8%A4%D9%88%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D9%88%D8%A7%D8%AD%D8%AF%D8%A9-single-responsibility-principle-r367/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_07/single-responsibility-principle.png.aa5bfcce52474f994ea21a1e78c1ac62.png" /></p>

<p>
	يُعتبر مبدأ المسؤوليّة الواحدة Single Responsibility Principle (أو اختصارًا SRP) المبدأ الأوّل من مبادئ التصميم SOLID، وهو مفيد بصورة خاصّة في التصميم كائنيّ التوجّه object-oriented design. يعتمد هذا المبدأ على تجزئة مكوّنات النظام البرمجي بحيث يكون لكلّ جزء منه مهمّة (مسؤوليّة) واحدة ووحيدة.
</p>

<p style="text-align: center;">
	<img alt="single-responsibility-principle.png" class="ipsImage ipsImage_thumbnailed" data-fileid="18566" data-unique="1t7gq0gfs" src="https://academy.hsoub.com/uploads/monthly_2016_07/single-responsibility-principle.png.0a76fe92ac6bc981840375e46c63772d.png"></p>

<p>
	ينص هذا المبدأ على ما يلي:
</p>

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

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			يجب ألّا يكون هناك أكثر من سبب واحد لتغيير صنف class ما.
		</p>
	</div>
</blockquote>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6768_7">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">MySQL</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
	</span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
		bool connect</span><span class="pun">();</span><span class="pln">
		</span><span class="kwd">void</span><span class="pln"> disconnect</span><span class="pun">();</span><span class="pln">
		bool executeQuery</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string queryString</span><span class="pun">);</span><span class="pln">
		</span><span class="typ">MySQLResult</span><span class="pun">*</span><span class="pln"> getQueryResult</span><span class="pun">();</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	من الواضح أنّ لهذا الصنف مهمّتان أساسيّتان، الأولى هي إدارة عملية الاتصال مع خادوم MySQL (فتح وإغلاق الاتصال) والثانية هي التواصل مع الخادوم في إجراء الاستعلامات واستقبال النتائج (تنفيذ استعلامات SQL). لو افترضنا الآن حدوث السيناريو التالي:
</p>

<ul>
<li>
		أصبح خادوم MySQL يقبل الاتصالات المشفّرة فقط.
	</li>
	<li>
		حدثت بعض التغييرات ضمن الخادوم بحيث أنّه بدأ بالاستجابة بشكل مختلف لبعض الاستعلامات.
	</li>
</ul>
<p>
	سيؤدي ذلك بالطبع إلى حدوث تغييرين ضمن صنف MySQL السابق، أو بمعنى آخر، سيكون هناك سببان لتغيير الصنف MySQL. ويُعدّ هذا خرقًا لمبدأ المسؤولية الواحدة كما هو واضح. يُعتبر وضع أكثر من مهمّة قابلة للتغيير (من أجل سبب ما) لأحد الأصناف خطأً تصميميًّا. قد لا تبدو تلك مشكلةً في الوقت الحالي، ولكن أيّ نظام برمجي يتغيّر ويتطوّر. فما يبدو حلًّا مقبولًا في الوقت الحاضر، قد يُفضي إلى نتائج سيّئة في المستقبل. يمكن استخدام الحل التالي لمشكلتنا السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6768_9">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">MySQLConnection</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
	</span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
		bool open</span><span class="pun">();</span><span class="pln"> </span><span class="com">/* former connect() */</span><span class="pln">
		</span><span class="kwd">void</span><span class="pln"> close</span><span class="pun">();</span><span class="pln"> </span><span class="com">/* former disconnect() */</span><span class="pln">
</span><span class="pun">};</span><span class="pln">


</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">MySQLQuery</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
	</span><span class="typ">MySQLConnection</span><span class="pun">*</span><span class="pln"> session</span><span class="pun">;</span><span class="pln">
  
	</span><span class="kwd">public</span><span class="pun">:</span><span class="pln">
		bool execute</span><span class="pun">(</span><span class="pln">std</span><span class="pun">::</span><span class="pln">string queryString</span><span class="pun">);</span><span class="pln">
		</span><span class="typ">MySQLResult</span><span class="pun">*</span><span class="pln"> getResult</span><span class="pun">();</span><span class="pln">
</span><span class="pun">};</span></pre>

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

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

<p>
	ترجمة -وبتصرّف- للمقال <a href="http://radek.io/2011/08/08/single-responsibility-principle" rel="external nofollow">Single Responsibility Principle</a> لصاحبه Radek Pazder.
</p>
]]></description><guid isPermaLink="false">367</guid><pubDate>Tue, 26 Jul 2016 16:34:00 +0000</pubDate></item></channel></rss>
