Abather

Members
  • عدد منشوراتي

    12
  • تاريخ الإنضمام

  • تاريخ اخر زياره

السمعه بالموقع

19 Good

1 متابع

عن العضو Abather

  • الرتبه
    مبدع مثابر
  • تاريخ الميلاد 06/15/90

معلومات عامة

  • الجنس
    ذكر
  • السكن
    الأحساء
  1. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته ...، أولاً اعتذر جداً عن التأخر في طرح الشرح الذي كان من المفترض يكون بعد أسبوع من الموضوع السابق تطرقنا في شرحنا السابق الى مفهوم القائمة أحادية الرابط وشرحنا استخداماته وطريقة عملها نظريا شرحنا هذا سيكون عمليا من الدرجة الأولى بحيث سيكون كله حول الأكواد وتفصيلها. ١. إنشاء العقدة Node: كما نعلم بأن Node هي الوحدة الأساسية لبناء قائمة أحادية الرابط أو القوائم بشكل عام ونستطيع بأن نصفها بأنها الحجر الذي يستخدم لبنائها. لذلك يجب علينا بنائها ابتداءً. public class SLLNode { public int info; public SLLNode next; public SLLNode(int i){ this(i, null); } public SLLNode(int i, SLLNode n){ this.info = i; this.next = n; } } هذا الكلاس والذي سمي بـ SSLNode هو الذي يصف لنا كل عقدة على حدى. وكل عقدة تتكون من info وهو المتغير الذي يحتوي على البيانات (المعلومة) وفي حالتنا هذه تكون البيانات من نوع int. وكذلك next وهو عبارة عن الرابط الذي يصل العقدة الحالية بالعقدة التي تليها ومن الطبيعي أن يكون من نوع SLLNode. هذا الكلاس يحتوي على two Constructor وهما عبارة عن وظائف تستخدم لإنشاء العقد Nodes. الأولى تستخدم لإنشاء عقدة لكنها لا ترتبط بأخرى كما نرى فيها فقد قمنا باستدعاء الميثود الثانية وقمنا بوضع null مكان الرابط. أما الثانية فتنشأ عقدة ترتبط بأخرى. (قمت بتفصيلها في الموضوع السابق) ٢. إنشاء القائمة أحادية الرابط: الكلاس السابق يمكننا من إنشاء عقدة واحدة لكن في هذا الكلاس سنتمكن من إنشاء قائمة كاملة تتكون من عدد من العقد Nodes. أ. مؤشر لأول عنصر وآخر عنصر بالقائمة: لكي نسهل عملية التنقل داخل القائمة سنقوم بإنشاء مؤشرين أحدهما يشير إلى أول عنصر بالقائمة (head: الرأس)، والآخر يشير إلى آخر عنصر بالقائمة (tail: الذيل). هذين المؤشرين عبارة عن متغيريين من نوع SSNode. protected SLLNode head, tail; في بداية إنشاء القائمة (أو في حالة كانت القائمة خالية) فإن هذين العنصريين يكونان خاليين: public SLList(){ head = tail = null; } كما يتبين في هذه الـ Constructor. ب. فحص القائمة إذا كانت خالية أو لا: هذه الـ Method مهمة جداً ولها أستخدام في جل الـ Methods الأخرى، ومهمتها هي فحص إذا ما كانت القائمة خالية أو لا. وتقوم بإرجاع قيمة Boolean إما true أو false. public boolean isEmpty(){ return head == null; //or return tail == null; } بكل بساطة في حال كان الرأس خالي فإن القائمة خالية كذلك، بالإمكان الإستعانة بالذيل كذلك فمن المستحيل أن يكون أحدهما خالي دون الآخر. ب. إضافة عنصر جديد إلى الرأس (بداية القائمة): public void addToHead(int el){ head.next = head; head = new SLLNode(el); if (tail = null){ tail = head; } } أولا نجعل الرأس يشير إلى نفسه لكي نربط العنصر الجديد بالعنصر الأول السابق. عندها نقوم بإنشاء عنصر Node ونجعله الرأس الجديد في حالة كانت القائمة خالية نجعل الذيل يشير إلى نفس العنصر الذي يشير له الرأس. ملاحظة: عندما يشير الرأس والذيل إلى نفس العنصر فهذا يعني بأن لدينا عنصر واحد فقط في القائمة. ج. إضافة عنصر جديد إلى الذيل (نهاية القائمة): public void addToTail(int el){ if(!isEmpty()){ tail.next = new SLLNode(el); tail = tail.next; }else{ head = tail = new SLLNode(el); } } نفحص القائمة إذا كانت خالية: - في حالة كانت غير خالية: ننشأ عنصر جديد ونجعل الذيل الحالي يشير إليه، ومن ثم نجعله الذيل الجديد. - في حال كانت القائمة خالية: ننشأ عنصر جديد ونجعل الرأس والذيل يشيران إليه. د. حذف العنصر الموجود بالرأس (أول عنصر): public int deleteHead(){ int el = head.info; if(head == tail){ head = tail = null; }else{ head = head.next; } return el; } هذه الـ method تقوم بحذف العنصر الموجود بالرأس ومن ثم ترجع قيمته. أولا نحفظ القيمة داخل متغير el لكي لا تضيع بعد حذف العنصر. ومن ثم نفحص إذا كانت القائمة: - تحتوي على عنصر واحد: فعندها نجعل الرأس والذيل يسيران إلى لا شيء null وتكون القائمة حينها خالية. - تحتوي على أكثر من عنصر: فعندها نجعل العنصر الذي بعد الرأس هو الرأس الجديد، وفي هذه الحالة سيكون العنصر لا يوجود ما يشير إليه ويحذف. ومن ثم ترجع القيمة التي تم حذفها. هـ- حذف العنصر الموجود بالذيل (آخر عنصر): public int deleteTail(){ int el = tail.info; if(tail == head){ head = tail = null; }else{ SLLNode temp; for(temp = head; temp.next != tail; temp = temp.next); tail = temp; tail.next = null; } return el; } كما في السابق نحتفظ بقيمة العنصر لكي نقوم بإعادته بعد الإنتهاء من عملية الحذف لكن الحذف من الذيل ليس بسهولة الحذف من الرأس. نفحص التالي: - في حالة أن القائمة تحتوي على عنصر واحد فقط: فعندها نحذف القائمة كلها بجعل الرأس والذيل null. - في حالة أن القائمة تحتوي على أكثر من عنصر: أولا علينا أن نبحث عن العنصر السابق للذيل (العنصر قبل الأخير)، وذلك بأستخدام for loop قبلها علينا أن ننشأ مؤشر مؤقت باسم temp وفي بداية الـ loop نجعله يشير إلى الرأس، وننقله من عنصر إلى آخر إلى أن يصل إلى العنصر ما قبل الأخير وذلك بفحص إذا ما كان temp.next أي العنصر الذي يليه هو tail فعندها نكون وصلنا إلى العنصر ما قبل الأخير. بعد أن نجد العنصر ما قبل الأخير نجعل الذيل الجديد ومن ثم نجعل الذيل يشير إلى null لكي يتم حذف العنصر الأخير تماماً. و. طباعة جميع عناصر القائمة: public void printAll(){ System.out.print("["); for(SLLNode temp = head; temp != null; temp = temp.next){ System.out.print(temp.info + " "); } System.out.println("]"); } كما شرحنا سابقا نستخدم for loop للتنقل بين عناصر القائمة وطباعة قيمة كل عنصر على الشاشة. ز. فحص وجود العنصر في القائمة أو لا: public boolean isInList(int el){ if(isEmpty()){ return false; }else{ SLLNode temp; for(temp = head; temp.info != el && temp.next != null; temp = temp.next); return temp != null; } } للبحث عن وجود قيمة داخل القائمة من عدمه يجب علينا أن نتنقل داخل القائمة عنصر بعنصر، وسنستخدم الأسلوب السابق وهو باستخدام for loop. ولكن قبلها يجب علينا أن نتأكد من أن القائمة غير خالية. الـ for loop ستتوقف في إحدى حالتين في حالة وجدة العنصر داخل القائمة وذلك ببطلان الشرط (tmep.info != el) أو في حالة وصول temp إلى نهاية القائمة. عندها سنرجع القية المعاكسة لهذا الشرط (tmep != null) في حالة وصول الـ temp إلى نهاية القائمة بدون أن يجد العنصر فعندا سيكون temp يساوي null وفي هذه الحالة سيرسل false أما في حالة إجاد العنصر فعندها temp لن يكون null ويرسل true. ح. حذف عنصر من القائمة: public void delete(int el){ if(!isEmpty() && isInList()){ if(head == tail && head.info == el){ head = tail = null; }else if(el == head.info){ head = head.next; }else{ SLLNode pre, temp; for(temp = head.next, pre = head; temp.info != el; pre = temp, temp = temp.next); if(temp != null){ pre.next = temp.next; if(temp == tail){ tail = pre; } } } } } في حالة أردنا حذف عنصر من القائمة علينا أن نفكر في جميع الإحتمالات وهي كالتالي: - هل القائمة غير خالية؟! وهل العنصر في القائمة ولتححق سنستخدم isEmpty و isInList، في حالة لم تكن القائمة خالية وكان العنصر بالقائمة ننتقل للخطوة التالية: * هل القائمة تحتوي على عنصر واحد؟!: في هذه الحالة نقوم بحذف هذا العنصر ونجعل القائمة خالية. * هل القائمة تحتوي على أكثر من عنصر والعنصر المراد حذفة بالرأس؟: عنذها نحذف الرأس كما أشرنا في الفقرة (د). *الخيار الأخير هو أن العنصر موجود داخل القائمة: في هذه الحالة نحتاج للبحث عن العنصر بنفس الأسلوب السابق لكننا هنا على خلاف الخطوات السابقة نحتاج إلى متغيرين مؤقتين أحدهما يشير إلى العنصر المراد حذفه والآخر يشير إلى العنصر الذي يسبقه. ولكي نقوم بذلك فعند إنشاء temp وهو العنصر المؤقت الذي سنبحث عن طريقه للعنصر المراد حذفه نجعل pre العنصر الذي يسبقه وذلك كما يلي: pre = head , temp = pre.next كما نعلم فنحن لسنا بحاجة لفحص الرأس فقد تأكدنا من أن العنصر ليس بالرأس. تنتهي for loop في حال إجاد العنصر المراد حذف مع العلم بأننا لن نصل إلى نهاية القائمة لأننا تأكدنا من أن العنصر موجود في القائمة في الخطوة الأولى. وعند نهاية كل دورة من for loop نقوم بتحديث قيمتي pre and temp. بعد ذلك نجعل الـ pre يشير للعنصر الذي يلي temp أي يشير للعنصر الذي بعد العنصر المراد حذفه عندها لن يكون هناك ما يشير إليه ويحذف. ** تأكد أخير في حال أن العنصر الذي حذفنها هو الأخير فعندها يجب أن نجعل الذيل يشير إلى null. #شرح مفصل للـ for loop المستخدمة مع القوائم بشكل عام: لأهميتها القصوى سوف أفصلها أكثر، إبتداءً بالشكل العام لها: for (SLLNode temp = head; temp != null; temp = temp.next); أولا نقوم بإنشاء متغير مؤقت ونسميه بأي أسم كان هذا المتغير في بداية الـ loop سيشير لأول عنصر بالقائمة وهو الرأس head بعد ذلك يتأكد من تحقق الشرط وهو أن هذا المتغير لا يساوي null (أي أنه غير خالي)، ومن ثم يحدث قيمة المتغير وذلك بجعله العنصر التالي له عن طريق (temp = temp.next) ومن ثم يقوم بالتأكد من الشرط مجدداً هكذا حتى يصل إلى أن temp مساوي لـ tail وعندها يسنتقل للعنصر الذي يليه وهو في الحقيقة null لأنه tail.next دائما يساوي null وهكذا تنتهي for loop. ملاحظة: قمت بوضع ملفات للـ SLLNode and SLList classes في المرفقات تحتوي على تعليقات توضيحية. أتمنى من الله أن أكون قد وفقت في تفصيل موضوعنا هذا في حفظ الله إلى لقاء آخر SLList.java SLLNode.java
  2. float max = 0, min = 0; int maxIndex = 0, minIndex = 0; for(int s = 0; s<avg.length; s++){ if(avg[s] > max){ max = avg[s]; maxIndex = s; } } for(int s = 0; s<avg.length; s++){ if(min == 0 ){ min = avg[s]; minIndex = s; }else if(min > avg[s]){ min = avg[s]; minIndex = s; } } System.out.println("Highest Average is " + name[maxIndex]); System.out.println("lowest Average is "+name[minIndex]); طبعا هذا خليه في الخارج مو في داخل for loop الأولى.
  3. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته ...، أولاً اعتذر جداً عن التأخر في طرح الشرح الذي كان من المفترض يكون بعد أسبوع من الموضوع السابق تطرقنا في شرحنا السابق الى مفهوم القائمة أحادية الرابط وشرحنا استخداماته وطريقة عملها نظريا شرحنا هذا سيكون عمليا من الدرجة الأولى بحيث سيكون كله حول الأكواد وتفصيلها. ١. إنشاء العقدة Node: كما نعلم بأن Node هي الوحدة الأساسية لبناء قائمة أحادية الرابط أو القوائم بشكل عام ونستطيع بأن نصفها بأنها الحجر الذي يستخدم لبنائها. لذلك يجب علينا بنائها ابتداءً. public class SLLNode { public int info; public SLLNode next; public SLLNode(int i){ this(i, null); } public SLLNode(int i, SLLNode n){ this.info = i; this.next = n; } } هذا الكلاس والذي سمي بـ SSLNode هو الذي يصف لنا كل عقدة على حدى. وكل عقدة تتكون من info وهو المتغير الذي يحتوي على البيانات (المعلومة) وفي حالتنا هذه تكون البيانات من نوع int. وكذلك next وهو عبارة عن الرابط الذي يصل العقدة الحالية بالعقدة التي تليها ومن الطبيعي أن يكون من نوع SLLNode. هذا الكلاس يحتوي على two Constructor وهما عبارة عن وظائف تستخدم لإنشاء العقد Nodes. الأولى تستخدم لإنشاء عقدة لكنها لا ترتبط بأخرى كما نرى فيها فقد قمنا باستدعاء الميثود الثانية وقمنا بوضع null مكان الرابط. أما الثانية فتنشأ عقدة ترتبط بأخرى. (قمت بتفصيلها في الموضوع السابق) ٢. إنشاء القائمة أحادية الرابط: الكلاس السابق يمكننا من إنشاء عقدة واحدة لكن في هذا الكلاس سنتمكن من إنشاء قائمة كاملة تتكون من عدد من العقد Nodes. أ. مؤشر لأول عنصر وآخر عنصر بالقائمة: لكي نسهل عملية التنقل داخل القائمة سنقوم بإنشاء مؤشرين أحدهما يشير إلى أول عنصر بالقائمة (head: الرأس)، والآخر يشير إلى آخر عنصر بالقائمة (tail: الذيل). هذين المؤشرين عبارة عن متغيريين من نوع SSNode. protected SLLNode head, tail; في بداية إنشاء القائمة (أو في حالة كانت القائمة خالية) فإن هذين العنصريين يكونان خاليين: public SLList(){ head = tail = null; } كما يتبين في هذه الـ Constructor. ب. فحص القائمة إذا كانت خالية أو لا: هذه الـ Method مهمة جداً ولها أستخدام في جل الـ Methods الأخرى، ومهمتها هي فحص إذا ما كانت القائمة خالية أو لا. وتقوم بإرجاع قيمة Boolean إما true أو false. public boolean isEmpty(){ return head == null; //or return tail == null; } بكل بساطة في حال كان الرأس خالي فإن القائمة خالية كذلك، بالإمكان الإستعانة بالذيل كذلك فمن المستحيل أن يكون أحدهما خالي دون الآخر. ب. إضافة عنصر جديد إلى الرأس (بداية القائمة): public void addToHead(int el){ head.next = head; head = new SLLNode(el); if (tail = null){ tail = head; } } أولا نجعل الرأس يشير إلى نفسه لكي نربط العنصر الجديد بالعنصر الأول السابق. عندها نقوم بإنشاء عنصر Node ونجعله الرأس الجديد في حالة كانت القائمة خالية نجعل الذيل يشير إلى نفس العنصر الذي يشير له الرأس. ملاحظة: عندما يشير الرأس والذيل إلى نفس العنصر فهذا يعني بأن لدينا عنصر واحد فقط في القائمة. ج. إضافة عنصر جديد إلى الذيل (نهاية القائمة): public void addToTail(int el){ if(!isEmpty()){ tail.next = new SLLNode(el); tail = tail.next; }else{ head = tail = new SLLNode(el); } } نفحص القائمة إذا كانت خالية: - في حالة كانت غير خالية: ننشأ عنصر جديد ونجعل الذيل الحالي يشير إليه، ومن ثم نجعله الذيل الجديد. - في حال كانت القائمة خالية: ننشأ عنصر جديد ونجعل الرأس والذيل يشيران إليه. د. حذف العنصر الموجود بالرأس (أول عنصر): public int deleteHead(){ int el = head.info; if(head == tail){ head = tail = null; }else{ head = head.next; } return el; } هذه الـ method تقوم بحذف العنصر الموجود بالرأس ومن ثم ترجع قيمته. أولا نحفظ القيمة داخل متغير el لكي لا تضيع بعد حذف العنصر. ومن ثم نفحص إذا كانت القائمة: - تحتوي على عنصر واحد: فعندها نجعل الرأس والذيل يسيران إلى لا شيء null وتكون القائمة حينها خالية. - تحتوي على أكثر من عنصر: فعندها نجعل العنصر الذي بعد الرأس هو الرأس الجديد، وفي هذه الحالة سيكون العنصر لا يوجود ما يشير إليه ويحذف. ومن ثم ترجع القيمة التي تم حذفها. هـ- حذف العنصر الموجود بالذيل (آخر عنصر): public int deleteTail(){ int el = tail.info; if(tail == head){ head = tail = null; }else{ SLLNode temp; for(temp = head; temp.next != tail; temp = temp.next); tail = temp; tail.next = null; } return el; } كما في السابق نحتفظ بقيمة العنصر لكي نقوم بإعادته بعد الإنتهاء من عملية الحذف لكن الحذف من الذيل ليس بسهولة الحذف من الرأس. نفحص التالي: - في حالة أن القائمة تحتوي على عنصر واحد فقط: فعندها نحذف القائمة كلها بجعل الرأس والذيل null. - في حالة أن القائمة تحتوي على أكثر من عنصر: أولا علينا أن نبحث عن العنصر السابق للذيل (العنصر قبل الأخير)، وذلك بأستخدام for loop قبلها علينا أن ننشأ مؤشر مؤقت باسم temp وفي بداية الـ loop نجعله يشير إلى الرأس، وننقله من عنصر إلى آخر إلى أن يصل إلى العنصر ما قبل الأخير وذلك بفحص إذا ما كان temp.next أي العنصر الذي يليه هو tail فعندها نكون وصلنا إلى العنصر ما قبل الأخير. بعد أن نجد العنصر ما قبل الأخير نجعل الذيل الجديد ومن ثم نجعل الذيل يشير إلى null لكي يتم حذف العنصر الأخير تماماً. و. طباعة جميع عناصر القائمة: public void printAll(){ System.out.print("["); for(SLLNode temp = head; temp != null; temp = temp.next){ System.out.print(temp.info + " "); } System.out.println("]"); } كما شرحنا سابقا نستخدم for loop للتنقل بين عناصر القائمة وطباعة قيمة كل عنصر على الشاشة. ز. فحص وجود العنصر في القائمة أو لا: public boolean isInList(int el){ if(isEmpty()){ return false; }else{ SLLNode temp; for(temp = head; temp.info != el && temp.next != null; temp = temp.next); return temp != null; } } للبحث عن وجود قيمة داخل القائمة من عدمه يجب علينا أن نتنقل داخل القائمة عنصر بعنصر، وسنستخدم الأسلوب السابق وهو باستخدام for loop. ولكن قبلها يجب علينا أن نتأكد من أن القائمة غير خالية. الـ for loop ستتوقف في إحدى حالتين في حالة وجدة العنصر داخل القائمة وذلك ببطلان الشرط (tmep.info != el) أو في حالة وصول temp إلى نهاية القائمة. عندها سنرجع القية المعاكسة لهذا الشرط (tmep != null) في حالة وصول الـ temp إلى نهاية القائمة بدون أن يجد العنصر فعندا سيكون temp يساوي null وفي هذه الحالة سيرسل false أما في حالة إجاد العنصر فعندها temp لن يكون null ويرسل true. ح. حذف عنصر من القائمة: public void delete(int el){ if(!isEmpty() && isInList()){ if(head == tail && head.info == el){ head = tail = null; }else if(el == head.info){ head = head.next; }else{ SLLNode pre, temp; for(temp = head.next, pre = head; temp.info != el; pre = temp, temp = temp.next); if(temp != null){ pre.next = temp.next; if(temp == tail){ tail = pre; } } } } } في حالة أردنا حذف عنصر من القائمة علينا أن نفكر في جميع الإحتمالات وهي كالتالي: - هل القائمة غير خالية؟! وهل العنصر في القائمة ولتححق سنستخدم isEmpty و isInList، في حالة لم تكن القائمة خالية وكان العنصر بالقائمة ننتقل للخطوة التالية: * هل القائمة تحتوي على عنصر واحد؟!: في هذه الحالة نقوم بحذف هذا العنصر ونجعل القائمة خالية. * هل القائمة تحتوي على أكثر من عنصر والعنصر المراد حذفة بالرأس؟: عنذها نحذف الرأس كما أشرنا في الفقرة (د). *الخيار الأخير هو أن العنصر موجود داخل القائمة: في هذه الحالة نحتاج للبحث عن العنصر بنفس الأسلوب السابق لكننا هنا على خلاف الخطوات السابقة نحتاج إلى متغيرين مؤقتين أحدهما يشير إلى العنصر المراد حذفه والآخر يشير إلى العنصر الذي يسبقه. ولكي نقوم بذلك فعند إنشاء temp وهو العنصر المؤقت الذي سنبحث عن طريقه للعنصر المراد حذفه نجعل pre العنصر الذي يسبقه وذلك كما يلي: pre = head , temp = pre.next كما نعلم فنحن لسنا بحاجة لفحص الرأس فقد تأكدنا من أن العنصر ليس بالرأس. تنتهي for loop في حال إجاد العنصر المراد حذف مع العلم بأننا لن نصل إلى نهاية القائمة لأننا تأكدنا من أن العنصر موجود في القائمة في الخطوة الأولى. وعند نهاية كل دورة من for loop نقوم بتحديث قيمتي pre and temp. بعد ذلك نجعل الـ pre يشير للعنصر الذي يلي temp أي يشير للعنصر الذي بعد العنصر المراد حذفه عندها لن يكون هناك ما يشير إليه ويحذف. ** تأكد أخير في حال أن العنصر الذي حذفنها هو الأخير فعندها يجب أن نجعل الذيل يشير إلى null. #شرح مفصل للـ for loop المستخدمة مع القوائم بشكل عام: لأهميتها القصوى سوف أفصلها أكثر، إبتداءً بالشكل العام لها: for (SLLNode temp = head; temp != null; temp = temp.next); أولا نقوم بإنشاء متغير مؤقت ونسميه بأي أسم كان هذا المتغير في بداية الـ loop سيشير لأول عنصر بالقائمة وهو الرأس head بعد ذلك يتأكد من تحقق الشرط وهو أن هذا المتغير لا يساوي null (أي أنه غير خالي)، ومن ثم يحدث قيمة المتغير وذلك بجعله العنصر التالي له عن طريق (temp = temp.next) ومن ثم يقوم بالتأكد من الشرط مجدداً هكذا حتى يصل إلى أن temp مساوي لـ tail وعندها يسنتقل للعنصر الذي يليه وهو في الحقيقة null لأنه tail.next دائما يساوي null وهكذا تنتهي for loop. ملاحظة: قمت بوضع ملفات للـ SLLNode and SLList classes في المرفقات تحتوي على تعليقات توضيحية. أتمنى من الله أن أكون قد وفقت في تفصيل موضوعنا هذا في حفظ الله إلى لقاء آخر SLList.java SLLNode.java تم ترقية هذا الطرح المميز الى صفحة المقالات
  4. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته إن شاء الله في هذه السلسلة من المواضيع سنتطرق لهيكلة البيانات بأنواعها المختلفة مع أن هذه الدروس لا ترتبط بأي لغة إلا أن اللغة المستخدمة للتطبيق العملي هي Java، يجب أن يكون القارئ ملم بأساسيات اللغة، كذلك سأستخدم في أغلب الأحيان المصطلحات الإنجليزية لصعوبة ترجمتها في بعض الحالات. قبل الشروع في الموضوع ماذا نقصد بهيكلة البيانات؟ المعنى هنا كبير جداً فقصودنا يحتوي على تشريح كامل لكل خصائص أي نوع من أنواع حفظ البيانات فعلى سبيل المثال عندما نتحدث عن المصفوفات واللتي تعتبر من أبسط أنواع حفظ البيانات فعندها سنتطرق لطريقة إنشاء المصفوفة طريقة حفظ أو حذف عنصر من المصفوفة وكذلك كيف لنا أن نبحث عن عنصر في المصفوفة وهكذا. من البديهي أن هناك أنواع مختلفة لحفظ البيانات ولكل منها خائصها وعيبوبها، في موضوعنا هذا سوف نتخطى المصفوفات والتي كما أشرت مسبقاً بأنها تعتبر من أبسط أنواع حفظ البيانات، وسنشرع مباشرة إلى القوائم أحادية الرابط. القوائم أحادية الرابط لماذا نحن بحاجة إلى القوائم بما أنه لدينا المصوفوفات؟ للمصفوفات عيبين رئسيين فعندما نريد تغيير حجم المصفوفة فإننا بحاجة إلى إنشاء مصفوفة جديدة ومن ثم نقوم بنسخ جميع عناصر المصفوفة القديمة إلى المصفوفة الجديدة ذات الحجم الجديد، كذلك عناصر المصفوفات مخزنة في الذاكرة في مواقع متتالية لذلك عندما نريد إضافة عنصر جديد أو حذف عنصر ما فهذا يضطرنا إلى تحريك بقية العناصر في حال كانت عناصر المصفوفة مرتبة بشكل ما. آفضل حل لهاتين المشكلتين هي القوائم وأبسط شكل من أشكال القوائم هي "القوائم أحادية الرابط". ماذا نعني بالقوائم أحادية الرابط؟! لنفرض بأن لدينا صندوق هذا يحتوي على قسمين القسم الأول نخزن فيه ما نريد أما القسم الثاني فيحتوي على فتحة لكي نستطيع أن نربط هذا الصندوق بالصندوق الذي بعده، في القوائم نطلق على هذا الصندوق أسم "العقدة" كل عقدة تحتوي على مكان لحفظ البيانات ورابط يربطها بما بعدها من العقد. عند تجميع عدد من العقد مع بعضها نطلق على هذا التجمع بإسم قائمة وبما أن كل عقدة ترتبط فقط بالعقدة اللتي تليها فنطلق عليها أحادية الرابط. بعد ان انتهينا من التعريفات وفهم معنى، لنترك النظري قليلاً ونتجه إلى العملي كما نعلم فإن القائمة أحادية الرابط تتكون من عدد من العقد، والعقدة الواحدة تحتوي على مكونين رئيسين (البيانات المخزنة و الرابط الذي يربط العقدة بما بعدها). إذاً علينا أولاً ان نقوم بإنشاء Class للعقدة الواحد ونوضح داخله مواصفات هذه العقدة واللتي هي مكان لحفظ البيانات (للتسهيل ستكون البيانات من نوع int) ومكان لربط هذه العقدة بالعقدة اللتي تليها (يجب أن يكون نوع هذا الرابط عقدة) لماذا يجب أن يكن الرابط من نفس نوع العقدة؟ بكل بساطة الرابط ما هو إلا سهم يشير إلى شيء ما لذلك يجب أن يعرف إلى ماذا يشير. هذا هو الكود اللذي سننتهي به: public class SLLNode { public int info; public SLLNode next; public SLLNode(int i){ this(i, null); } public SLLNode(int i, SLLNode n){ this.info = i; this.next = n; } } اسم الـ Class : SLLNode وهو اختصار لـ Single Linked Lists Node == عقدة لقائمة أحادية الرابط ويحتوي على متغيرين: info: وهو من نوع int ، وهو المكان اللذي سنقوم بحفظ البيانات داخله. next: وهو من نوع SLLNode وهو الجزء اللذي سيربط هذه العقدة بالعقدة اللتي تليها هذا الـ Class يحتوي على اثنتين من الـ Constructors : الأولى: public SLLNode(int i){ this(i, null); } وهذه تمكننا من إنشاء عقدة بتخزين المعلومات فقط بدون الحاجة لربطها بالعقدة اللتي تليها، وداخلها كما نرى نداء للـ Constructor الأخرى بإعطائها البيانات المدخلة + Null لخانة الرابط اللذي يربط العقدة للعقدة اللتي تليها. لماذا لا نقوم بإعطاء العقدة رابط للعقدة اللتي تليها؟! هذا مهم ففي حالة آخر عقدة لن يكون هناك عقدة بعدها فلا يوجد ما ترتبط به لذلك نضع علامة بأنه لا يوجد شيء بعد هذه العقدة وهذه العلامة هي null . الثانية: public SLLNode(int i, SLLNode n){ this.info = i; this.next = n; } وهذه تمكننا من إنشاء عقدة بإعطائها البيانات التي ستحفظها ورابط للعقدة اللتي تليها. هذه هي نهاية الجزء الأول من درسنا إن شاء الله سأكمل موضوع القوائم أحادية الرابط في الموضوع التالي مع العلم بأن باب النقاش مفتوح لأي نقطة لتوظيحها أكثر ولإزالة أي لبس حولها.
  5. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته إن شاء الله في هذه السلسلة من المواضيع سنتطرق لهيكلة البيانات بأنواعها المختلفة مع أن هذه الدروس لا ترتبط بأي لغة إلا أن اللغة المستخدمة للتطبيق العملي هي Java، يجب أن يكون القارئ ملم بأساسيات اللغة، كذلك سأستخدم في أغلب الأحيان المصطلحات الإنجليزية لصعوبة ترجمتها في بعض الحالات. قبل الشروع في الموضوع ماذا نقصد بهيكلة البيانات؟ المعنى هنا كبير جداً فقصودنا يحتوي على تشريح كامل لكل خصائص أي نوع من أنواع حفظ البيانات فعلى سبيل المثال عندما نتحدث عن المصفوفات واللتي تعتبر من أبسط أنواع حفظ البيانات فعندها سنتطرق لطريقة إنشاء المصفوفة طريقة حفظ أو حذف عنصر من المصفوفة وكذلك كيف لنا أن نبحث عن عنصر في المصفوفة وهكذا. من البديهي أن هناك أنواع مختلفة لحفظ البيانات ولكل منها خائصها وعيبوبها، في موضوعنا هذا سوف نتخطى المصفوفات والتي كما أشرت مسبقاً بأنها تعتبر من أبسط أنواع حفظ البيانات، وسنشرع مباشرة إلى القوائم أحادية الرابط. القوائم أحادية الرابط لماذا نحن بحاجة إلى القوائم بما أنه لدينا المصوفوفات؟ للمصفوفات عيبين رئسيين فعندما نريد تغيير حجم المصفوفة فإننا بحاجة إلى إنشاء مصفوفة جديدة ومن ثم نقوم بنسخ جميع عناصر المصفوفة القديمة إلى المصفوفة الجديدة ذات الحجم الجديد، كذلك عناصر المصفوفات مخزنة في الذاكرة في مواقع متتالية لذلك عندما نريد إضافة عنصر جديد أو حذف عنصر ما فهذا يضطرنا إلى تحريك بقية العناصر في حال كانت عناصر المصفوفة مرتبة بشكل ما. آفضل حل لهاتين المشكلتين هي القوائم وأبسط شكل من أشكال القوائم هي "القوائم أحادية الرابط". ماذا نعني بالقوائم أحادية الرابط؟! لنفرض بأن لدينا صندوق هذا يحتوي على قسمين القسم الأول نخزن فيه ما نريد أما القسم الثاني فيحتوي على فتحة لكي نستطيع أن نربط هذا الصندوق بالصندوق الذي بعده، في القوائم نطلق على هذا الصندوق أسم "العقدة" كل عقدة تحتوي على مكان لحفظ البيانات ورابط يربطها بما بعدها من العقد. عند تجميع عدد من العقد مع بعضها نطلق على هذا التجمع بإسم قائمة وبما أن كل عقدة ترتبط فقط بالعقدة اللتي تليها فنطلق عليها أحادية الرابط. بعد ان انتهينا من التعريفات وفهم معنى، لنترك النظري قليلاً ونتجه إلى العملي كما نعلم فإن القائمة أحادية الرابط تتكون من عدد من العقد، والعقدة الواحدة تحتوي على مكونين رئيسين (البيانات المخزنة و الرابط الذي يربط العقدة بما بعدها). إذاً علينا أولاً ان نقوم بإنشاء Class للعقدة الواحد ونوضح داخله مواصفات هذه العقدة واللتي هي مكان لحفظ البيانات (للتسهيل ستكون البيانات من نوع int) ومكان لربط هذه العقدة بالعقدة اللتي تليها (يجب أن يكون نوع هذا الرابط عقدة) لماذا يجب أن يكن الرابط من نفس نوع العقدة؟ بكل بساطة الرابط ما هو إلا سهم يشير إلى شيء ما لذلك يجب أن يعرف إلى ماذا يشير. هذا هو الكود اللذي سننتهي به: public class SLLNode { public int info; public SLLNode next; public SLLNode(int i){ this(i, null); } public SLLNode(int i, SLLNode n){ this.info = i; this.next = n; } } اسم الـ Class : SLLNode وهو اختصار لـ Single Linked Lists Node == عقدة لقائمة أحادية الرابط ويحتوي على متغيرين: info: وهو من نوع int ، وهو المكان اللذي سنقوم بحفظ البيانات داخله. next: وهو من نوع SLLNode وهو الجزء اللذي سيربط هذه العقدة بالعقدة اللتي تليها هذا الـ Class يحتوي على اثنتين من الـ Constructors : الأولى: public SLLNode(int i){ this(i, null); } وهذه تمكننا من إنشاء عقدة بتخزين المعلومات فقط بدون الحاجة لربطها بالعقدة اللتي تليها، وداخلها كما نرى نداء للـ Constructor الأخرى بإعطائها البيانات المدخلة + Null لخانة الرابط اللذي يربط العقدة للعقدة اللتي تليها. لماذا لا نقوم بإعطاء العقدة رابط للعقدة اللتي تليها؟! هذا مهم ففي حالة آخر عقدة لن يكون هناك عقدة بعدها فلا يوجد ما ترتبط به لذلك نضع علامة بأنه لا يوجد شيء بعد هذه العقدة وهذه العلامة هي null . الثانية: public SLLNode(int i, SLLNode n){ this.info = i; this.next = n; } وهذه تمكننا من إنشاء عقدة بإعطائها البيانات التي ستحفظها ورابط للعقدة اللتي تليها. هذه هي نهاية الجزء الأول من درسنا إن شاء الله سأكمل موضوع القوائم أحادية الرابط في الموضوع التالي مع العلم بأن باب النقاش مفتوح لأي نقطة لتوظيحها أكثر ولإزالة أي لبس حولها. تم ترقية هذا الطرح المميز الى صفحة المقالات
  6. Swift 08 : تطبيقات على ما تم شرحه

    بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته تكلمنا في الموضوع السابق عن المتغيرات و الأنواع و المصفوفات والدوال والجمل الشرطية للغة البرمجة Swift وفي هذا الموضوع سأتطرق لبعض الأمثلة لتوضح بعض النقاط حول كل هذه الأمور. المثال الأول: حفظ أرقام داخل المصفوفة (سنستخدم أرقام من نوع Int): مصفوفتنا الأولى ستحتوي على مجموعة من الأرقام وسنقوم بإجراء عدد من الأمور على هذه المصفوفة لنبدأ آولاً بإنشاء المصفوفة: var numbers : Array<Int> = [12, 2, 5, 8, 17, 13, 4] بعد أن آنشئنا مصفوفتنا دعونا نتعرف على بعض خصائصها: حجم المصفوفة : var size = numbers.capacity capacity : هي دالة تستخدم للإستعلام عن حجم المصفوفة حيث تقوم بإعادة رقم من نوع Int كما هو ملاحظ لقد قمنا بتخزين حجم المصفوفة داخل المتغير size. - عناوين عناصر المصفوفة: مصفوفتنا لديها 7 عناصر لذلك حجمها هو 7. أما عناوين العناصر فتبدأ من 0 وتنتهي عند 6 (عنوان آخر عنصر هو حجم المصفوفة -1). - تخزين أحد عناصر المصفوفة داخل متغير: لكي نقوم بتخزين أحد عناصر المصفوفة داخل متغير علينا أن ننتبه لأمر واحد ومهم وهو أن يكون نوع المتغير هو نفسه نوع عناصر المصفوفة لكي لا نواجه أي خطأ (يفضل أن لا أحدد نوع المتغير وأترك Swift يحدده لي تلقائياً لتفادي أي خطأ) var numberAt2 : Int = numbers[2] var numberAt5 = numbers[5] في السطر الأول قمنا بتحديد نوع المتغير Int أما في السطر الثاني فتركنا الأمر لـ Swift وهو الأمر الأسلم لنفرض أننا قمنا بإعطاء المتغير نوع يختلف عن نوع المصفوفة ماذا سيحدث؟! var numberAt3 : Double = numbers[4] في مثالنا هذا ظهر لنا الخطأ التالي: error: cannot convert value of type 'Int' to specified type 'Double' خطأ: لا يمكن تحويل المتغير من نوع int إلى متغير من نوع Double لنفرض بأننا نريد أن نقوم بتخزين العنصر من المصفوفة داخل المتغير من نوع Double ما هو الحل؟! الحل بسيط كل ما علينا فعله: اسم النوع (المتغير)، عندها سيتم التحويل من نوع الى آخر. var numberAt3 : Double = Double(numbers[3]) كما هو واضح هنا فإن المتغير numberAt3 هو متغير من نوع Double بينما مصففوتنا نوع المتغيرات بها هي من نوع Int قما بتغير نوع العنصر في المصفوفة إلى Double قبل إعطاء القيمة للمتغير numberAt3 ماذا لو آردنا تخزن مجموع عنصرين أو أكثر من المصفوفة داخل متغير؟! var numberAt4pAt6 = numbers[4] + numbers[6] بهذه البساطة وهذا ينطبق على جميع العمليات الأخرى من ضرب وطرح وقسمة. لكن ماذا لو آردنا مجموع جميع العناصر بالمصفوفة؟! var sum = 0 for i in numbers{ sum = sum + i } هنا يأتي دور الحلقات وسنستخدم for loop كذلك نحن بحاجة لمتغير سنسميه sum وهو الذي سنحفظ فيه المجموع في التكرار هذا في كل مرة سيخزن عنصر من عناصر المصفوفة داخل المتغير i إبتداء من أول عنصر إلى آخر عنصر من المصفوفة هذه هي أحد الطرق للتعامل مع الـ for loop مع المصفوفات بعد انتهاء الـ for سيكون لدينا مجموع كل الأعداد في المصفوفة, لكن ماذا لو أردت أن أستخدم هذه العملية على أكثر من مصفوفة هنا يجب علينا أن نقوم بإنشاء دالة، هذه الدالة يجب أن تأخذ مصفوفة من نوع Int وتعيد لنا عدد من نوع Int كذلك: func ArraySum (Arr : Array<Int>) -> Int{ var sum = 0 for i in Arr { sum = sum + i } return sum } دالتنا هذه عند إعطائك لها أي مصفوفة فإنها ستعيد لك مجموع عناصرها ترتيب عناصر المصفوفة من الأشياء المهمة اللتي سنستخدمها هي الترتيب فمن المهم أن تكون مصفوفتي مرتبة فهذا أفضل من العشوائية فلو رتبتها تصاعديا فعندها سأجد أكبر آو أصغر عنصر بكل سهولة، كيف أقوم بترتيب مصفوفتي؟! for i in 0..<numbers.endIndex { for j in 0..<numbers.endIndex{ if numbers[j] > numbers[i]{ var temp = numbers[j] numbers[j] = numbers[i] numbers[i] = temp } } } عناصر المصفوفة قبل الترتيب: [12, 2, 5, 8, 17, 13, 4] عناصر المصفوفة بعد الترتيب: [2, 4, 5, 8, 12, 13, 17] كما نلاحظ لقد قمنا بترتيب عناصر المصفوفة تصاعدياً لكن ما هو تفصيل الكود السابق؟! لنبدأ به سطراً بسطر لدينا تكرار من نوع for هذا التكرار لديه متغير i يبدأ من 0 وينتهي عند عنوان آخر عنصر داخل هذا التكرار لدينا تكرار آخر من نفس النوع ولديه متغير j كذلك يبدأ عند 0 وينتهي عند عنوان آخر عنصر في داخل التكرار الثاني لدينا شرط فإذا كان العنصر في المصفوفة الذي في العنوان j أكبر من العنصر الذي في العنوان i عندها سنقوم بالتبديل بينهما وإلا ستنتهي هذه الدورة من التكرار لتذهب للتي بعدها للتكرار الثاني إلى أن تنتهي التكرار الداخليه لتبدأ الدورة الثانية من بعد ذلك وهكذا حتى الإنتهاء من التكرار الخارجي والذي مع انتهائه ستكون المصفوفة مرتبه. كما الحال في مجموع عناصر المصفوفة فإنه يفضل أستخدام دالة لترتيب العناصر هذه الدالة تستقبل مصفوفة وتعيد مصفوفة: func arrInc (array : Array<Int>) -> Array<Int>{ var arr = array for i in 0..<arr.endIndex { for j in 0..<arr.endIndex{ if arr[j] > arr[i]{ let temp = arr[j] arr[j] = arr[i] arr[i] = temp } } } return arr } في هذه الحالة لن تتأثر مصفوفتنا الأساسية إنما يمكننا ان نخزن ترتيب المصفوفة داخل مصفوفة جديدة. اتمنى من الله أن أكون وفقت لشرح بعض النقاط المفيدة بالامكان الاطلاع على الأمثلة السابقة بالمرفقات تبقى لدي شرح أخير في اساسيات اللغة إن شاء الله سأكتبه في القريب العاجل في حفظ الله تعالى Examples.playground.zip
  7. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته تكلمنا في الموضوع السابق عن المتغيرات و الأنواع و المصفوفات والدوال والجمل الشرطية للغة البرمجة Swift وفي هذا الموضوع سأتطرق لبعض الأمثلة لتوضح بعض النقاط حول كل هذه الأمور. المثال الأول: حفظ أرقام داخل المصفوفة (سنستخدم أرقام من نوع Int): مصفوفتنا الأولى ستحتوي على مجموعة من الأرقام وسنقوم بإجراء عدد من الأمور على هذه المصفوفة لنبدأ آولاً بإنشاء المصفوفة: var numbers : Array<Int> = [12, 2, 5, 8, 17, 13, 4] بعد أن آنشئنا مصفوفتنا دعونا نتعرف على بعض خصائصها: حجم المصفوفة : var size = numbers.capacity capacity : هي دالة تستخدم للإستعلام عن حجم المصفوفة حيث تقوم بإعادة رقم من نوع Int كما هو ملاحظ لقد قمنا بتخزين حجم المصفوفة داخل المتغير size. - عناوين عناصر المصفوفة: مصفوفتنا لديها 7 عناصر لذلك حجمها هو 7. أما عناوين العناصر فتبدأ من 0 وتنتهي عند 6 (عنوان آخر عنصر هو حجم المصفوفة -1). - تخزين أحد عناصر المصفوفة داخل متغير: لكي نقوم بتخزين أحد عناصر المصفوفة داخل متغير علينا أن ننتبه لأمر واحد ومهم وهو أن يكون نوع المتغير هو نفسه نوع عناصر المصفوفة لكي لا نواجه أي خطأ (يفضل أن لا أحدد نوع المتغير وأترك Swift يحدده لي تلقائياً لتفادي أي خطأ) var numberAt2 : Int = numbers[2] var numberAt5 = numbers[5] في السطر الأول قمنا بتحديد نوع المتغير Int أما في السطر الثاني فتركنا الأمر لـ Swift وهو الأمر الأسلم لنفرض أننا قمنا بإعطاء المتغير نوع يختلف عن نوع المصفوفة ماذا سيحدث؟! var numberAt3 : Double = numbers[4] في مثالنا هذا ظهر لنا الخطأ التالي: error: cannot convert value of type 'Int' to specified type 'Double' خطأ: لا يمكن تحويل المتغير من نوع int إلى متغير من نوع Double لنفرض بأننا نريد أن نقوم بتخزين العنصر من المصفوفة داخل المتغير من نوع Double ما هو الحل؟! الحل بسيط كل ما علينا فعله: اسم النوع (المتغير)، عندها سيتم التحويل من نوع الى آخر. var numberAt3 : Double = Double(numbers[3]) كما هو واضح هنا فإن المتغير numberAt3 هو متغير من نوع Double بينما مصففوتنا نوع المتغيرات بها هي من نوع Int قما بتغير نوع العنصر في المصفوفة إلى Double قبل إعطاء القيمة للمتغير numberAt3 ماذا لو آردنا تخزن مجموع عنصرين أو أكثر من المصفوفة داخل متغير؟! var numberAt4pAt6 = numbers[4] + numbers[6] بهذه البساطة وهذا ينطبق على جميع العمليات الأخرى من ضرب وطرح وقسمة. لكن ماذا لو آردنا مجموع جميع العناصر بالمصفوفة؟! var sum = 0 for i in numbers{ sum = sum + i } هنا يأتي دور الحلقات وسنستخدم for loop كذلك نحن بحاجة لمتغير سنسميه sum وهو الذي سنحفظ فيه المجموع في التكرار هذا في كل مرة سيخزن عنصر من عناصر المصفوفة داخل المتغير i إبتداء من أول عنصر إلى آخر عنصر من المصفوفة هذه هي أحد الطرق للتعامل مع الـ for loop مع المصفوفات بعد انتهاء الـ for سيكون لدينا مجموع كل الأعداد في المصفوفة, لكن ماذا لو أردت أن أستخدم هذه العملية على أكثر من مصفوفة هنا يجب علينا أن نقوم بإنشاء دالة، هذه الدالة يجب أن تأخذ مصفوفة من نوع Int وتعيد لنا عدد من نوع Int كذلك: func ArraySum (Arr : Array<Int>) -> Int{ var sum = 0 for i in Arr { sum = sum + i } return sum } دالتنا هذه عند إعطائك لها أي مصفوفة فإنها ستعيد لك مجموع عناصرها ترتيب عناصر المصفوفة من الأشياء المهمة اللتي سنستخدمها هي الترتيب فمن المهم أن تكون مصفوفتي مرتبة فهذا أفضل من العشوائية فلو رتبتها تصاعديا فعندها سأجد أكبر آو أصغر عنصر بكل سهولة، كيف أقوم بترتيب مصفوفتي؟! for i in 0..<numbers.endIndex { for j in 0..<numbers.endIndex{ if numbers[j] > numbers[i]{ var temp = numbers[j] numbers[j] = numbers[i] numbers[i] = temp } } } عناصر المصفوفة قبل الترتيب: [12, 2, 5, 8, 17, 13, 4] عناصر المصفوفة بعد الترتيب: [2, 4, 5, 8, 12, 13, 17] كما نلاحظ لقد قمنا بترتيب عناصر المصفوفة تصاعدياً لكن ما هو تفصيل الكود السابق؟! لنبدأ به سطراً بسطر لدينا تكرار من نوع for هذا التكرار لديه متغير i يبدأ من 0 وينتهي عند عنوان آخر عنصر داخل هذا التكرار لدينا تكرار آخر من نفس النوع ولديه متغير j كذلك يبدأ عند 0 وينتهي عند عنوان آخر عنصر في داخل التكرار الثاني لدينا شرط فإذا كان العنصر في المصفوفة الذي في العنوان j أكبر من العنصر الذي في العنوان i عندها سنقوم بالتبديل بينهما وإلا ستنتهي هذه الدورة من التكرار لتذهب للتي بعدها للتكرار الثاني إلى أن تنتهي التكرار الداخليه لتبدأ الدورة الثانية من بعد ذلك وهكذا حتى الإنتهاء من التكرار الخارجي والذي مع انتهائه ستكون المصفوفة مرتبه. كما الحال في مجموع عناصر المصفوفة فإنه يفضل أستخدام دالة لترتيب العناصر هذه الدالة تستقبل مصفوفة وتعيد مصفوفة: func arrInc (array : Array<Int>) -> Array<Int>{ var arr = array for i in 0..<arr.endIndex { for j in 0..<arr.endIndex{ if arr[j] > arr[i]{ let temp = arr[j] arr[j] = arr[i] arr[i] = temp } } } return arr } في هذه الحالة لن تتأثر مصفوفتنا الأساسية إنما يمكننا ان نخزن ترتيب المصفوفة داخل مصفوفة جديدة. اتمنى من الله أن أكون وفقت لشرح بعض النقاط المفيدة بالامكان الاطلاع على الأمثلة السابقة بالمرفقات تبقى لدي شرح أخير في اساسيات اللغة إن شاء الله سأكتبه في القريب العاجل في حفظ الله تعالى Examples.playground.zip تم ترقية هذا الطرح المميز الى صفحة المقالات
  8. Swift 07 : الدوال Functions الجزء الثاني

    بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته استكمالاً لما بدأناه في موضوعنا Functions نبدأ مباشرة في أستكمال أنواعها: 3- في هذا النوع عندما ننادي الـ Function فإنها تستجيب لنا بإعادة بعض البيانات ويختلف نوع البيانات اللتي تعيدها لنا الـ Function على حسب ما وضعناه أثناء إنشاء الدالة: لنعد قليلا إلى مثالنا في موضوعنا السابق إلى العقل والحواس، هذا النوع من الدوال يمثل النظر، فعندما يقوم العقل بنداء العينين تستجيب العين بإعادة صورة إلى العقل لنقم بإنشاء Function تعيد لنا بيانات من نوع Int: func getAge () -> Int{ return 23 } طريقة إنشاء هذا النوع من الـ Function شبيه لسابقة لكن هناك بعض الإختلافات : - بعد () نضع <- ومن بعدها نضع نوع البيانات التي نريد أستردادها عن نداء الدالة. نوع البيانات يمكن أن يكون أي نوع من الأنواع السابقة (String, Int, Double, Bool).. وغيرها من الأنواع. - في داخل الـ Function يجب وضع return ومن بعدها نضع البيانات التي نريد اعطائها للمستدعي مع ملاحظة أن نوع البيانات يجب أن يطابق نوع الذي وضعناه أثناء إنشاء الدالة. return تكون في النهاية حيث أن الدالة ستنتهي عند الوصول لهذه الكلمة. 4- هذا النوع يجمع ما بين النوع 2 و 3 حيث أن الـ Function تأخذ معطيات وتقوم بإعادة البيانات إلى المستدعي: بإمكاننا أن نضرب مثال لدالة تعطيها رقمين وتقوم بجمع هذين الرقمين وإعادة المجموع، لنقم أولاً بإنشاء الدالة: func add2Numers (num1: Int, _ num2: Int) -> Int { var add : Int = num1 + num2 return add } في هذه الدالة يقوم المستدعي بإعطاء الدالة رقمين وفي المقابل تقوم الدالة بإرجاع مجموعهما. بديهياً بأنه بإمكاننا أن نقوم بإعطاء القيمة التي تقوم الـ Function بإرجاعها لأي متغير وللتوضيح أكثر فإنه بإمكاننا نداء Function السابقة وإعطاء البيانات المرتجعة إلى المتغير addtion : var addtion : Int = add2Numbers(2, 3) في مثالنا هذا ستكون قيمة addtion هي مجموع 2 و 3 وهو 5 ويجب الإنتباه أن نوع المتغير مماثل للبيانات التي تقوم Function بإعادتها. هذين النوعين بالإمكان ان يحتوي على عدد من الأكواد ومن بعدها نقوم بإرجاع البيانات كذلك يمكن أن تتكر return أكثر من مرة لكن ندعها لموضوعنا التالي والذي سنتطرق فيه لعدد من الأمثال. اسأل الله أني وفقت لإصال المعلومة بكل سهولة ويسر في حفظ الله
  9. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته استكمالاً لما بدأناه في موضوعنا Functions نبدأ مباشرة في أستكمال أنواعها: 3- في هذا النوع عندما ننادي الـ Function فإنها تستجيب لنا بإعادة بعض البيانات ويختلف نوع البيانات اللتي تعيدها لنا الـ Function على حسب ما وضعناه أثناء إنشاء الدالة: لنعد قليلا إلى مثالنا في موضوعنا السابق إلى العقل والحواس، هذا النوع من الدوال يمثل النظر، فعندما يقوم العقل بنداء العينين تستجيب العين بإعادة صورة إلى العقل لنقم بإنشاء Function تعيد لنا بيانات من نوع Int: func getAge () -> Int{ return 23 } طريقة إنشاء هذا النوع من الـ Function شبيه لسابقة لكن هناك بعض الإختلافات : - بعد () نضع <- ومن بعدها نضع نوع البيانات التي نريد أستردادها عن نداء الدالة. نوع البيانات يمكن أن يكون أي نوع من الأنواع السابقة (String, Int, Double, Bool).. وغيرها من الأنواع. - في داخل الـ Function يجب وضع return ومن بعدها نضع البيانات التي نريد اعطائها للمستدعي مع ملاحظة أن نوع البيانات يجب أن يطابق نوع الذي وضعناه أثناء إنشاء الدالة. return تكون في النهاية حيث أن الدالة ستنتهي عند الوصول لهذه الكلمة. 4- هذا النوع يجمع ما بين النوع 2 و 3 حيث أن الـ Function تأخذ معطيات وتقوم بإعادة البيانات إلى المستدعي: بإمكاننا أن نضرب مثال لدالة تعطيها رقمين وتقوم بجمع هذين الرقمين وإعادة المجموع، لنقم أولاً بإنشاء الدالة: func add2Numers (num1: Int, _ num2: Int) -> Int { var add : Int = num1 + num2 return add } في هذه الدالة يقوم المستدعي بإعطاء الدالة رقمين وفي المقابل تقوم الدالة بإرجاع مجموعهما. بديهياً بأنه بإمكاننا أن نقوم بإعطاء القيمة التي تقوم الـ Function بإرجاعها لأي متغير وللتوضيح أكثر فإنه بإمكاننا نداء Function السابقة وإعطاء البيانات المرتجعة إلى المتغير addtion : var addtion : Int = add2Numbers(2, 3) في مثالنا هذا ستكون قيمة addtion هي مجموع 2 و 3 وهو 5 ويجب الإنتباه أن نوع المتغير مماثل للبيانات التي تقوم Function بإعادتها. هذين النوعين بالإمكان ان يحتوي على عدد من الأكواد ومن بعدها نقوم بإرجاع البيانات كذلك يمكن أن تتكر return أكثر من مرة لكن ندعها لموضوعنا التالي والذي سنتطرق فيه لعدد من الأمثال. اسأل الله أني وفقت لإصال المعلومة بكل سهولة ويسر في حفظ الله تم ترقية هذا الطرح المميز الى صفحة المقالات
  10. 06 Swift : الدوال Functions الجزء الأول

    بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته في شرحنا لهذا اليوم سنتطرق لعنصر مهم في أي لغة برمجة ألا وهو Function (الترجمة الحرفية : الوظائف). لنفهم الـ function بشكل بسيط لنأخذ الجسم البشري كمثال لكل عضو من أعضاء الجسم وظيفة يقوم بها، فاللسان مسؤول عن الكلام، العينان عن النظر و الأذنان عن السمع وهكذا كل عضو يقوم بما أوكل إليه كذلك الـ Functions فهي عبارة عن مجموعة من الأكواد المترابطة وتقوم بمهمة محددة، في جسمنا عندما نريد الكلام فإن المخ يرسل الأوامر إلى اللسان ليبدأ بالكلام كذلك عندما نريد من Function معينة أن تقوم بمهمتها كل ما علينا هو مناداتها عن طريق أسمها الذي نعطيه لها في أثناء إنشاء هذه الـ Function. في الشروحات السابقة أستخدمنا أحد هذه الـ Function وهي print() فما هذه إلا وظيفة تقوم بطباعة ما داخل القوسين على الشاشة. هناك أنواع من Functions ويختلف طريقة تعريفها على حسب نوعها سنتطرق لأهمها : 1- هذا النوع من Function مهمته هي القيام بمجموعة من الأكواد فقط، لنفرض أني أريد طباعة أسم أحدهم وعمره وبعض بياناته: print("myName") print("myAge") print("myInfo") بعض الأحيان قد أريد طباعة هذه البيانات في مكان آخر، قد يقول أحدهم بإمكانك نسخ هذه الأسطر ولصقها في المكان الذي تريد هذا صحيح بإمكاني فعل هذا ولكن ماذا لو كان عدد الأسطر كثير؟ ماذا لو كان لدي خطأ في هذه الأكواد؟ أو ماذا لو أردة القيام ببعض التغير؟ عندها سأضطر لأن أراجع كل مكان قمت فيه بلصق هذه الأسطر وبعض الأحيان قد أغفل عن بعضها ناهيك عن الوقت الذي سأقوم بتضيعه. هنا يأتي دور الـ Function بكل بساطة أنشأ واحدة بأسم myInfo وعندها ستقوم بطباعة كل هذا كلما قمت بمناداتها: func myInfo(){ print("myName") print("myAge") print("myOtherInfo") } هذه هي الطريقة لإنشاء هذا النوع من الـ Function: - نكتب الكلمة الدلالية func. - اسم الـ Function متبعة بـ (). - الأكواد اللتي نريدها داخل هذه الـ Function بين {}. وبكل بساطة عندما أريد أن آنادي هذه الـ Function كل ما علي هو كتابة أسمها كالتالي: myInfo() ملاحظة: عند مناداة الـ Function لا تنسى إضافة الـ () وستقوم بكل ما داخلها من أكواد. 2- هذا النوع شبيه لـ print() فعند مناداتك لهذه الـ Function يجب عليك أن تعطيها قيمة داخل ()، لنفرض أنك قمت بإنشاء Function تأخذ البيانات من المستخدم ومن ثم تقوم ببعض الأشياء بها، لنقم ببعض التعديلات على مثالنا السابق: func myInfo(Name: String, Age: Int){ print("your name is \(Name) and you are \(Age) years old") } نلاحظ بأن هناك أختلاف في أنشاء الـ Function هنا ففي هذه الحالة ما بين () ليس فارغ وإنما يحتوي على بعض الأشياء وهي ما نطلق عليها Parameters (المعطيات أو المعامل). الفائدة من هذه المعطيات هي تمكين المستخدم في أثناء استخدام الـ Function من تمرير بعض المعطيات إليها ففي مثالنا السابق بإمكاني أعطاء الـ Function أي أسم وأي عمر وستقوم بطباعته لي. كيف أقوم بإنشاء المعطيات؟! أهم نقطة يجب معرفتها بأن المعطيات تكون بين () ولإنشاء أي معطى يجب عليك أن تعطيه أسم وأن تحدد نوعه. في مثالنا السابق لدينا معطيين أحدهما يحمل الأسم Name والنوع String أما الآخر فيحمل الأسم Age والنوع Int. كيف نقوم بنداء هذه الـ Function؟! myInfo("Haider", Age: 22) كما في السابق ولكن علينا أن نعطيها المتغيرات. كما نلاحظ فإن المتغير الأول كتبناه مباشرة أما الثاني فإننا كتبنا أسمه قبل وضع قيمته. بإمكاننا التحكم في الأسم الذي نريد استخدامه في أثاء نداء الـ Function وذلك في أثناء إنشائها كما في التالي: func myInfo(Name: String, A Age: Int){ print("your name is \(Name) and you are \(Age) years old") } كل ما علينا فعله هو إضافة أسم للمعطى قبله وعندها في نداء الـ Function سيكون كالتالي: myInfo("Haider", A: 22) كذلك لديك الخيار بأن لا يكون هناك اسم مطلاقاً وبإمكنك فعل ذلك بالتغير في الـ Function : func myInfo(Name: String, _ Age: Int){ print("your name is \(Name) and you are \(Age) years old") } هكذا ستتمكن من إعطاء المعطيات بدون أي أسماء كما التالي: myInfo("Haider", 22) ويجب عليك الإنتباه إلى أنه يجب أن يتطابق نوع البيانات اللتي تمررها إلى الدالة مع النوع الموجود في Function تبقى لدينا نوع أخير وأفضل أن آتركه لما بعد لأنه يتطلب بعض من الأمثلة والكثير من الشرح أسأل الله القدير بأن أكون قد وفقت لإصال المعلومة بيسر وسهولة في حفظ الله
  11. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته في شرحنا لهذا اليوم سنتطرق لعنصر مهم في أي لغة برمجة ألا وهو Function (الترجمة الحرفية : الوظائف). لنفهم الـ function بشكل بسيط لنأخذ الجسم البشري كمثال لكل عضو من أعضاء الجسم وظيفة يقوم بها، فاللسان مسؤول عن الكلام، العينان عن النظر و الأذنان عن السمع وهكذا كل عضو يقوم بما أوكل إليه كذلك الـ Functions فهي عبارة عن مجموعة من الأكواد المترابطة وتقوم بمهمة محددة، في جسمنا عندما نريد الكلام فإن المخ يرسل الأوامر إلى اللسان ليبدأ بالكلام كذلك عندما نريد من Function معينة أن تقوم بمهمتها كل ما علينا هو مناداتها عن طريق أسمها الذي نعطيه لها في أثناء إنشاء هذه الـ Function. في الشروحات السابقة أستخدمنا أحد هذه الـ Function وهي print() فما هذه إلا وظيفة تقوم بطباعة ما داخل القوسين على الشاشة. هناك أنواع من Functions ويختلف طريقة تعريفها على حسب نوعها سنتطرق لأهمها : 1- هذا النوع من Function مهمته هي القيام بمجموعة من الأكواد فقط، لنفرض أني أريد طباعة أسم أحدهم وعمره وبعض بياناته: print("myName") print("myAge") print("myInfo") بعض الأحيان قد أريد طباعة هذه البيانات في مكان آخر، قد يقول أحدهم بإمكانك نسخ هذه الأسطر ولصقها في المكان الذي تريد هذا صحيح بإمكاني فعل هذا ولكن ماذا لو كان عدد الأسطر كثير؟ ماذا لو كان لدي خطأ في هذه الأكواد؟ أو ماذا لو أردة القيام ببعض التغير؟ عندها سأضطر لأن أراجع كل مكان قمت فيه بلصق هذه الأسطر وبعض الأحيان قد أغفل عن بعضها ناهيك عن الوقت الذي سأقوم بتضيعه. هنا يأتي دور الـ Function بكل بساطة أنشأ واحدة بأسم myInfo وعندها ستقوم بطباعة كل هذا كلما قمت بمناداتها: func myInfo(){ print("myName") print("myAge") print("myOtherInfo") } هذه هي الطريقة لإنشاء هذا النوع من الـ Function: - نكتب الكلمة الدلالية func. - اسم الـ Function متبعة بـ (). - الأكواد اللتي نريدها داخل هذه الـ Function بين {}. وبكل بساطة عندما أريد أن آنادي هذه الـ Function كل ما علي هو كتابة أسمها كالتالي: myInfo() ملاحظة: عند مناداة الـ Function لا تنسى إضافة الـ () وستقوم بكل ما داخلها من أكواد. 2- هذا النوع شبيه لـ print() فعند مناداتك لهذه الـ Function يجب عليك أن تعطيها قيمة داخل ()، لنفرض أنك قمت بإنشاء Function تأخذ البيانات من المستخدم ومن ثم تقوم ببعض الأشياء بها، لنقم ببعض التعديلات على مثالنا السابق: func myInfo(Name: String, Age: Int){ print("your name is \(Name) and you are \(Age) years old") } نلاحظ بأن هناك أختلاف في أنشاء الـ Function هنا ففي هذه الحالة ما بين () ليس فارغ وإنما يحتوي على بعض الأشياء وهي ما نطلق عليها Parameters (المعطيات أو المعامل). الفائدة من هذه المعطيات هي تمكين المستخدم في أثناء استخدام الـ Function من تمرير بعض المعطيات إليها ففي مثالنا السابق بإمكاني أعطاء الـ Function أي أسم وأي عمر وستقوم بطباعته لي. كيف أقوم بإنشاء المعطيات؟! أهم نقطة يجب معرفتها بأن المعطيات تكون بين () ولإنشاء أي معطى يجب عليك أن تعطيه أسم وأن تحدد نوعه. في مثالنا السابق لدينا معطيين أحدهما يحمل الأسم Name والنوع String أما الآخر فيحمل الأسم Age والنوع Int. كيف نقوم بنداء هذه الـ Function؟! myInfo("Haider", Age: 22) كما في السابق ولكن علينا أن نعطيها المتغيرات. كما نلاحظ فإن المتغير الأول كتبناه مباشرة أما الثاني فإننا كتبنا أسمه قبل وضع قيمته. بإمكاننا التحكم في الأسم الذي نريد استخدامه في أثاء نداء الـ Function وذلك في أثناء إنشائها كما في التالي: func myInfo(Name: String, A Age: Int){ print("your name is \(Name) and you are \(Age) years old") } كل ما علينا فعله هو إضافة أسم للمعطى قبله وعندها في نداء الـ Function سيكون كالتالي: myInfo("Haider", A: 22) كذلك لديك الخيار بأن لا يكون هناك اسم مطلاقاً وبإمكنك فعل ذلك بالتغير في الـ Function : func myInfo(Name: String, _ Age: Int){ print("your name is \(Name) and you are \(Age) years old") } هكذا ستتمكن من إعطاء المعطيات بدون أي أسماء كما التالي: myInfo("Haider", 22) ويجب عليك الإنتباه إلى أنه يجب أن يتطابق نوع البيانات اللتي تمررها إلى الدالة مع النوع الموجود في Function تبقى لدينا نوع أخير وأفضل أن آتركه لما بعد لأنه يتطلب بعض من الأمثلة والكثير من الشرح أسأل الله القدير بأن أكون قد وفقت لإصال المعلومة بيسر وسهولة في حفظ الله تم ترقية هذا الطرح المميز الى صفحة المقالات
  12. Swift 05 : التكرار (الحلقات loops)

    بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته في شرحنا لهذا اليوم سوف نتطرق للتكرار Loops وبالتحديد لـ for and while å قد تحتاج بعض الأحيان لتكرار code ما لسبب او لآخر بإمكانك ان تعيد كتابته أكثر من مرة لنفرض أننا نريد طباعة الآعداد من 0 الى 5 بإمكاننا كتابة الكود التالي: print(0) print(1) print(2) print(3) print(4) print(5) قد يبدوا هذا الأمر سهلاً للوهلة الأولى لكن ماذا لو آردت أن اطبع من 0 إلى 100 ألن يكون لدي الكثير من السطور المتكررة وماذا لو كان هناك خطأ ولو في حرف بسيط ألن يتوجب علي أن اعيد كتبابة المئة سطر من جديد؟! هنا تأتي أهمية الـ loops فكل ما عليك هو كتابة الكود الرئيسي وعدد المرات اللتي تريد منه تكرارها وهو سيتكرر من تلقاء نفسه. لنفرض أني آريد كتابة حرف الألف 10 مرات (العقاب السابق الذي كنا نتلقاه من مدرسينا): for i in 0...5 { print("أ") } هذا الـ code سيقوم بطباعة حرف “أ” خمس مرات لكن ما هي الطريقة؟! أولا هذا الـ loop من نوع for لأننا كما هو واضح استخدمنا الكلمة الدلالية for في بدايته إذا تشريح هذا الكود كما التالي: for: نوع الـ lopp الذي نستخدمه i : متغير (سنتطرق لإستخداماته فيما يلي) 0..<5 : النطاق ويحمل عدد مرات التكرار اللتي نريدها وهذه الرموز اللتي تستخدم في النطاق الرمز المعنى مثال … من العدد الأول إلى أن تصل إلى العدد الثاني 0…5 من الصفر إلى ان تصل إلى 5 ما فوق 5 لا يحسب (عدد مرات التكرار 6) ..< من العدد الأول إلى ما قبل العدد الثاني 0..<5 من الصفر إلى ما قبل 5 الـ 5 وما بعده لا يحسب (عدد مرات التكرار هنا 5 مرات ) النطاق عبارة عن نوع حاله من حال الـ Int, Doblue, or String بما انه مثيل لهم اذا بكل تأكيد بإمكانك حفظ النطاق داخل متغير كما التالي: المتغير range عبارة عن نطاق يبدأ من 0 وينتهي عن 5 (5 ضمن هذا النطاق) اذا يمكننا تغير كودنا إلى التالي: var range = 0..<5 قلنا بأننا سنتكلم لاحقا عن المتغير الموجود بـ for والذي هو i بكل بساطة عمل المتغير i هو حمل القيمة داخل النطاق في البداية في مثالنا السابق ستكون قيمته 0 لأن بداية النطاق لدينا من 0 بعد أن ينتهي من أول دورة ويعود لبدأ الدورة الأخرى ستكون قيمته 1 وهكذا حتى يصل إلى نهاية النطاق وفي النهاية ستكون قيمته 4، للتوضيح أكثر لنقوم ببعض التغيرات على مثالنا السابق: for i in range { print(i) } من الواضح بأن لدينا تكرار لطباعة المتغير i لكن ماذا سيطبع؟! بكل تأكيد سيكون كالتالي: 0 1 2 3 4 ماذا لو كان النطاق يبدأ من عدد غير الـ 0؟! var range = 5...10 for i in range { print(i) } في هذا المثال لن تكون المخرجات كما السابق لكنها ستكون كالتالي: 5 6 7 8 9 10 لأن النطاق في هذه الحالة لا يبدأ من 0 إنما 5، وكما قلنا فإن المتغير i يأخذ قيمة النطاق. أتمنى بأن يكون السطر الأول من for loop قد اتضح. أما بالنسبة لما بين {} فيمكن أن يكون أي كود بالإمكان إنشاء متغيرات القيام بالعمليات الحسابية أو وضع الجملة الشرطية بل وحتى وضع loop أخرى في داخله. لنفرض أني أريد طباعة الأعداد الزوجية (الأعداد التي تقبل القسمة على 2) داخل نطاق معين؟! بكل تأكيد بما أني اريد تكرار الطباعة فسوف استخدم loop ولا ننسى بأن هناك شرط آخرى وهو ان يقبل العدد القسمة على 2 في هذه الحالة سيكون لدينا هذا الكود: var range = 0...10 for i in range { if(i % 2 == 0){ print(i) } } نطاقنا هنا من 0 إلى 10 (10 ضمن النطاق) وكما نلاحظ فإن داخل loop هناك شرط ألا وهو بأن يكون باقي القسمة يساوي 0 إذا تحقق الشرط سيقوم بطباعة العدد وإلا ينهي هذه الجولة من الـ loop ويدخل في الآخرى. في هذه الحالة ستكون المخرجات لدينا كما التالي: 0 2 4 6 8 10 ما هذه الا أمثلة بسيطة للشرح أتمنى أن لا تحصر عقلك بها إنما تبحر بخيالك لكي تستخدمها على أكمل وجه. هناك نقطة أخيرة بالنسبة للـ for loop خصوصاً لمن لديهم خلفية في لغات برمجة أخرى مثل Java : for var i = 0 ; i <= 10; i++{ //your Code } هذا النمط يمكنك استخدامه في Swift حالياً لكنه سيتم التخلص منه في التحديثات القادمة لذلك لم أقم بشرحة. While: var m = 5 while(m > 0){ print("True") m -= 1 } هذا هو الشكل العالم للـ while loop تبدأ بالكلمة المفتاحية while ومن بعدها () يجب عليك ان تضع بينهما الشرط الذي في حال تحققه سيستمر التكرار. أما داخل {} فهنا نجد الكود المراد تكراه كما في for loop أما في نهاية الكود (ليس حتما أن يكون في النهاية) فهنا نجد تحديث لقيمة المتغير الذي داخل الشرط فلو ابقينا هذا المتغير ثابت فعندها سنكون في مشكلة لأننا سنواجه عندها تكرار غير منتهي. في كل مرة نقوم بالدخول داخل الـ loop تقل قيمة المتغير m بـ 1 ويتسمر التكرار حتى يكون الـ m أصغر من 0 . لن نطيل في الـ while loop فحالها من حال for لكن الأختلاف فقط في الشرط داخل الـ while وآنه يجب علينا تحديث المتغير. أخطاء شائعة في الـ loop قد تقع بها: - التكرار الغير منتهي: وتقع عادة في هذا الخطأ القاتل عندما تعطي الـ loop حالة من المستحيل أن تكون خاطئة مثال على ذلك: var n = 15 while (n > 0){ print("yes") n += 1 } في هذه الحالة سيستمر البرنامج بتكرار الكود الى مالنهاية (وقد يتسبب في تعطيل البرنامج كما حدث لي الآن 😔) لأن المتغير n سيكون دائما أكبر من 0. أو أن تنسى أن تقوم بتحديث المتغير كما في هذا المثال: var n = 15 while (n > 0){ print("yes") } - عدد التكرار الخاطأ: دائما ما يكون هناك اخطاء تافهة مثل هذا، فمثلا لو أنك أردت من التكرار أن يتم 10 مرات لكنه يتم 11 مرة أ 9 مرات ونقع في هذا الخطأ عند الخطأ في أعطاء النطاق: for i in 0..<9 { //Your Code } سيكون التكرار أقل من المطلوب بخطوة. for i in 0...10 { //Your Code } سيكون التكرار أكثر من المطلوب بخطوة. أتمنى من الله أن أكون أوصلت المعلومة بيسر وسهولة في حفظ الله
  13. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته في شرحنا لهذا اليوم سوف نتطرق للتكرار Loops وبالتحديد لـ for and while قد تحتاج بعض الأحيان لتكرار code ما لسبب او لآخر بإمكانك ان تعيد كتابته أكثر من مرة لنفرض أننا نريد طباعة الآعداد من 0 الى 5 بإمكاننا كتابة الكود التالي: print(0) print(1) print(2) print(3) print(4) print(5) قد يبدوا هذا الأمر سهلاً للوهلة الأولى لكن ماذا لو آردت أن اطبع من 0 إلى 100 ألن يكون لدي الكثير من السطور المتكررة وماذا لو كان هناك خطأ ولو في حرف بسيط ألن يتوجب علي أن اعيد كتبابة المئة سطر من جديد؟! هنا تأتي أهمية الـ loops فكل ما عليك هو كتابة الكود الرئيسي وعدد المرات اللتي تريد منه تكرارها وهو سيتكرر من تلقاء نفسه. لنفرض أني آريد كتابة حرف الألف 10 مرات (العقاب السابق الذي كنا نتلقاه من مدرسينا): for i in 0...5 { print("أ") } هذا الـ code سيقوم بطباعة حرف “أ” خمس مرات لكن ما هي الطريقة؟! أولا هذا الـ loop من نوع for لأننا كما هو واضح استخدمنا الكلمة الدلالية for في بدايته إذا تشريح هذا الكود كما التالي: for: نوع الـ lopp الذي نستخدمه i : متغير (سنتطرق لإستخداماته فيما يلي) 0..<5 : النطاق ويحمل عدد مرات التكرار اللتي نريدها وهذه الرموز اللتي تستخدم في النطاق الرمز المعنى مثال … من العدد الأول إلى أن تصل إلى العدد الثاني 0…5 من الصفر إلى ان تصل إلى 5 ما فوق 5 لا يحسب (عدد مرات التكرار 6) ..< من العدد الأول إلى ما قبل العدد الثاني 0..<5 من الصفر إلى ما قبل 5 الـ 5 وما بعده لا يحسب (عدد مرات التكرار هنا 5 مرات ) النطاق عبارة عن نوع حاله من حال الـ Int, Doblue, or String بما انه مثيل لهم اذا بكل تأكيد بإمكانك حفظ النطاق داخل متغير كما التالي: المتغير range عبارة عن نطاق يبدأ من 0 وينتهي عن 5 (5 ضمن هذا النطاق) اذا يمكننا تغير كودنا إلى التالي: var range = 0..<5 قلنا بأننا سنتكلم لاحقا عن المتغير الموجود بـ for والذي هو i بكل بساطة عمل المتغير i هو حمل القيمة داخل النطاق في البداية في مثالنا السابق ستكون قيمته 0 لأن بداية النطاق لدينا من 0 بعد أن ينتهي من أول دورة ويعود لبدأ الدورة الأخرى ستكون قيمته 1 وهكذا حتى يصل إلى نهاية النطاق وفي النهاية ستكون قيمته 4، للتوضيح أكثر لنقوم ببعض التغيرات على مثالنا السابق: for i in range { print(i) } من الواضح بأن لدينا تكرار لطباعة المتغير i لكن ماذا سيطبع؟! بكل تأكيد سيكون كالتالي: 0 1 2 3 4 ماذا لو كان النطاق يبدأ من عدد غير الـ 0؟! var range = 5...10 for i in range { print(i) } في هذا المثال لن تكون المخرجات كما السابق لكنها ستكون كالتالي: 5 6 7 8 9 10 لأن النطاق في هذه الحالة لا يبدأ من 0 إنما 5، وكما قلنا فإن المتغير i يأخذ قيمة النطاق. أتمنى بأن يكون السطر الأول من for loop قد اتضح. أما بالنسبة لما بين {} فيمكن أن يكون أي كود بالإمكان إنشاء متغيرات القيام بالعمليات الحسابية أو وضع الجملة الشرطية بل وحتى وضع loop أخرى في داخله. لنفرض أني أريد طباعة الأعداد الزوجية (الأعداد التي تقبل القسمة على 2) داخل نطاق معين؟! بكل تأكيد بما أني اريد تكرار الطباعة فسوف استخدم loop ولا ننسى بأن هناك شرط آخرى وهو ان يقبل العدد القسمة على 2 في هذه الحالة سيكون لدينا هذا الكود: var range = 0...10 for i in range { if(i % 2 == 0){ print(i) } } نطاقنا هنا من 0 إلى 10 (10 ضمن النطاق) وكما نلاحظ فإن داخل loop هناك شرط ألا وهو بأن يكون باقي القسمة يساوي 0 إذا تحقق الشرط سيقوم بطباعة العدد وإلا ينهي هذه الجولة من الـ loop ويدخل في الآخرى. في هذه الحالة ستكون المخرجات لدينا كما التالي: 0 2 4 6 8 10 ما هذه الا أمثلة بسيطة للشرح أتمنى أن لا تحصر عقلك بها إنما تبحر بخيالك لكي تستخدمها على أكمل وجه. هناك نقطة أخيرة بالنسبة للـ for loop خصوصاً لمن لديهم خلفية في لغات برمجة أخرى مثل Java : for var i = 0 ; i <= 10; i++{ //your Code } هذا النمط يمكنك استخدامه في Swift حالياً لكنه سيتم التخلص منه في التحديثات القادمة لذلك لم أقم بشرحة. While: var m = 5 while(m > 0){ print("True") m -= 1 } هذا هو الشكل العالم للـ while loop تبدأ بالكلمة المفتاحية while ومن بعدها () يجب عليك ان تضع بينهما الشرط الذي في حال تحققه سيستمر التكرار. أما داخل {} فهنا نجد الكود المراد تكراه كما في for loop أما في نهاية الكود (ليس حتما أن يكون في النهاية) فهنا نجد تحديث لقيمة المتغير الذي داخل الشرط فلو ابقينا هذا المتغير ثابت فعندها سنكون في مشكلة لأننا سنواجه عندها تكرار غير منتهي. في كل مرة نقوم بالدخول داخل الـ loop تقل قيمة المتغير m بـ 1 ويتسمر التكرار حتى يكون الـ m أصغر من 0 . لن نطيل في الـ while loop فحالها من حال for لكن الأختلاف فقط في الشرط داخل الـ while وآنه يجب علينا تحديث المتغير. أخطاء شائعة في الـ loop قد تقع بها: - التكرار الغير منتهي: وتقع عادة في هذا الخطأ القاتل عندما تعطي الـ loop حالة من المستحيل أن تكون خاطئة مثال على ذلك: var n = 15 while (n > 0){ print("yes") n += 1 } في هذه الحالة سيستمر البرنامج بتكرار الكود الى مالنهاية (وقد يتسبب في تعطيل البرنامج كما حدث لي الآن 😔) لأن المتغير n سيكون دائما أكبر من 0. أو أن تنسى أن تقوم بتحديث المتغير كما في هذا المثال: var n = 15 while (n > 0){ print("yes") } - عدد التكرار الخاطأ: دائما ما يكون هناك اخطاء تافهة مثل هذا، فمثلا لو أنك أردت من التكرار أن يتم 10 مرات لكنه يتم 11 مرة أ 9 مرات ونقع في هذا الخطأ عند الخطأ في أعطاء النطاق: for i in 0..<9 { //Your Code } سيكون التكرار أقل من المطلوب بخطوة. for i in 0...10 { //Your Code } سيكون التكرار أكثر من المطلوب بخطوة. أتمنى من الله أن أكون أوصلت المعلومة بيسر وسهولة في حفظ الله تم ترقية هذا الطرح المميز الى صفحة المقالات
  14. ملاحظة: endIndex لا يعطينا عنوان آخر عنصر إنما يعطينا العنوان الذي بعد آخر عنصر أعتذر على هذا الخطأ
  15. Swift 04 : المصفوفات

    بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته في هذا الموضوع سنتعرف إن شاء الله على المصفوفات وبعض من تطبيقاتها والوظائف (Methods) اللتي تستخدم معها ماذا نقصد بالمصفوفة؟! هي عبارة عن تجميع لعدد من القيم اللتي لها نفس النوع ( ... type: String, Int, or Double ) لتبسيط الأمر آكثر تخيل بأن لديك صندوق كبير وهذا الصندوق مقسم من الداخل إلى أكثر من قسم في كل قسم لديك نوع من الفواكه، في مثالنا هذا المصفوفه هي الصندوق، ونوع المتغير هو فواكه (أو أسماء الفواكه) مما تتكون المصفوفة؟! للمصفوفةمكونين رئسيين: 1- أسم المصفوفة. 2- نوع البيانات داخل المصفوفة. لنبتعد قليلا عن الأمور النظرية ولنبدأ بكتابة الأكود، وبالتأكيد أولا ما نقوم بكتابته هو إنشاء مصفوفة داخل برمجتنا، سنقوم بإنشاء مصفوفة من نوع String وتحمل اسم names var names = Array<String>() ونقصد بـ var بأن بيانات المصفوفة قابلة للتغير و names هو أسم المصفوفة أما String هو نوع البيانات داخل المصفوفة (يجب وضع النوع بين <>). هكذا قد قمنا بإنشاء مصفوف تحمل أسم names ونوع البيانات داخلها هو String هناك طرق أخرى لإنشاء المصفوفة سنتطرق لها لاحقاً إن شاء الله إلى الآن مصفوفتنا خالية ولا تحمل أية بيانات ولنقوم بإضافة البيانات يجب آن نستخدم .append names.append("محمد") names.append("حيدر") names.append("نايف") names.append("حمد") إذا لإضافة بيانات إلى المصفوفة علينا كتابة اسم المصفوفة ومن ثم نقطة بعدها append الآن هذه المصفوفة تحتوي على بعض البيانات اللتي قمنا بإدخالها هذه البيانات محفوظة داخل المصفوفة ولك منها عنوانه الخاص داخل هذه المصفوفة وهذا العنوان عبارة عن الأرقام إبتداءً من 0 إلى نهاية المصفوفة. كذلك لهذه المصفوفة حجم وهو عدد العناصر اللتي تحملها مصفوفتنا هذه تحتوي على 4 عناصر إذا حجمها 4 وعناوين العناصر هي: محمد: 0 حيدر: 1 نايف: 2 حمد: 3 (ملاحظة عنوان آخر عنصر دائما ما يكون حجم المصفوفة -1) بماذا نستفيد من هذا العنوان؟! لنتمعن في هذا المثال: names[1] في مثالنا هذا قمنا بإستدعاء جزء من المصفوفة وهي البيانات المخزنة بالعنوان 1 (وهو العنصر الثاني "حيدر") عندها يسكون لدينا اسم "حيدر"، إذا عندما نريد استدعاء اي عنصر من المصفوفة علينا ان نكتب اسم المصفوفة بعدها نضع عنوان العنصر بين []. append تستخدم لإضافة عناصر في آخر المصفوفة لكن ماذا لو اردنا آن نضع عنصر جديد في بداية المصفوفة أو في اي مكان داخلها؟! في هذه الحالة نستخدم insert : names.insert("عمار", atIndex: 2) insert بداخلها شيئا وليس فقط واحد في البداية علينا كتابة المتغير الذي نريد اضافته ومن ثم المكان الذي نريد اضافته فيه (يجب أن تكتب atIndex: متبوعة بالعنوان) في هذه الحالة اضفنا "عمار" في العنوان 2 وقمنا بإزاحة نايف ومابعده رقم إلى الأمام مصفوفتنا الجديدة ستكون: names[0] \\محمد names[1] \\حيدر names[2] \\عمار names[3] \\نايف names[4] \\حمد (حجم المصفوفة اصبح الآن 5 ) ماذا لو اردت أن ابدل أحد الأسماء؟! names[0] = "صادق" بكل بساطة في هذا المثال قمنا بمحو "محمد" من المصفوفة وإستبداله بـ "صادق" اذا لنقوم بتغير أحد المتغيرات في المصفوفة يجب علينا ان نستدعيه ومن ثم نضع = بعدها البيانات الجديدة. كذلك بإمكاننا أن نحذف أحد العناصر باستخدام removeAtIndex names.removeAtIndex(3) في هذه الحالة قمنا بحذف "نايف" من المصفوفة (جميع العناصر التي بعدها سيتغير عنوانها بمقدار -1) هذه بعض الوظائف المستخدمة في المصفوفات : Method الوظيفة isEmpty ترجع لنا قيمة صواب أو خاطىء وتستخدم لفحص المصفوفة إذا ما كانت فارقة أو لا. first يستخدم لإستدعاء أول عنصر بالمصفوفة last يستخدم لإستدعاء آخر عنصر بالمصفوفة count يستخدم للإستعلام عن حجم المصفوفة (عدد البيانات داخل المصفوفة) append يستخدم لإضافة عنصر في نهاية المصفوفة insert يستخدم لإضافة عنصر في مكان معين في المصفوفة removeFirst يستخدم لحذف أول عنصر بالمصفوفة removeLast يستخدم لحذف آخر عنصر بالمصفوفة removeAtIndex يستخدم لحذف العنصر المجود في العنوان المعطى removeAll يستخدم لحذف جميع البيانات داخل المصفوفة endIndex يرجع لنا قيمة من نوع Int وهي عنوان آخر عنصر في المصفوفة والذي يساوي حجم المصفوفة -1 sort يستخدم لعرض المصفوفة مرتبة تصاعديا )أبجدياً او عددياً) contains ترجع لنا قيمة من نوع Bool وتستخدم للبحث إذا ما كان العنصر داخل المصفوفة أو لا هذه بعض الوظائف المهمة وليست كلها لإستخدام أحدى هذه الوظائف نكتب أسم المصفوفة متبوعة بـ نقطة من ثم اسم الوظيفة أتمنى أن يكون الشرح وافياً وغير مشتت في حفظ الله تعالى

عالم البرمجة

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