بيت - إعداد الإنترنت
ما هو اسم الوظيفة في البرمجة. وظائف البرمجة

الهدف من العمل: 1) دراسة قواعد وصف الوظائف؛ 2) اكتساب مهارات استخدام الدوال عند كتابة البرامج بلغة C++.

المعلومات النظرية

الوحدة الرئيسية للبرامج في C++ هي الوظيفة.

وظيفة- جزء مكتمل منطقيًا ومصمم بالتأكيد من برنامج له اسم. تتيح لك الوظائف تقسيم المهام الحسابية الكبيرة إلى مهام أصغر.

يحتوي كل برنامج C++ بالضرورة على وظيفة تسمى main، وهي نص البرنامج. بالنسبة لجميع الوظائف الأخرى، إذا كانت موجودة في البرنامج، فيجب الإعلان عن النماذج الأولية - وهي تدوينات تخطيطية تخبر المترجم باسم وشكل كل وظيفة في البرنامج.

بناء جملة النموذج الأولي للوظيفة مع المعلمات هو:

return_value_type function_name (parameter_list_with_type_inification);

الوظائف في C++ قياسية (مكتبة) وقابلة للبرمجة بواسطة المستخدم.

الميزات القياسية

تم العثور على أوصاف الوظائف القياسية في الملفات المضمنة في البرنامج باستخدام التوجيه #include. تسمى هذه الملفات ملفات الرأس؛ لديهم الامتداد ح.

تسمى الإشارة إلى اسم الوظيفة في البرنامج الرئيسي باستدعاء دالة.

تؤدي وظائف الاستدعاء إلى تنفيذ بعض الإجراءات أو حساب بعض القيمة، والتي يتم استخدامها بعد ذلك في البرنامج.

ص = الخطيئة(س); // وظيفة حساب الجيب

تعريف الوظيفة

بشكل عام، يتم تعريف الوظائف على النحو التالي:

return_value_type function_name (اكتب اسم المعلمة،...، اكتب اسم المعلمة)

وظيفة الجسم

ميزات قابلة للبرمجة

الوظائف التي ينشئها المبرمج بنفسه تسهل عملية كتابة البرامج لأنها:

    تساعد على تجنب البرمجة المتكررة، حيث يمكن استخدام نفس الوظيفة في برامج مختلفة؛

    زيادة مستوى نمطية البرنامج، وبالتالي تسهيل القراءة وإجراء التغييرات وتصحيح الأخطاء.

مثال9 .1. لنقم بإنشاء دالة تطبع 65 حرفًا "*" على التوالي. ولجعل هذه الوظيفة تعمل في سياق ما، يتم تضمينها في برنامج طباعة الورق ذي الرأسية. يتكون البرنامج من الوظائف: main() وstars().

// ترويسة

#يشمل

حد ثابت = 65؛

نجوم باطلة (باطلة) ؛ // النجوم النموذجية للوظيفة ()

cout<<"Moscow Institute of Electronic Engineering"<

// تعريف وظيفة النجوم ().

ل (العد = 1؛ العد<=Limit; count++)

لقد نظرنا إلى مثال لدالة بسيطة لا تحتوي على وسائط ولا تُرجع أي قيم.

معلمات الوظيفة

دعونا نلقي نظرة على مثال لاستخدام معلمات الوظيفة.

مثال9. 2. لنكتب مساحة الوظيفة () ، ستكون حجتها هي عدد المسافات التي يجب أن تطبعها هذه الوظيفة.

# تحديد العنوان "زيلينوغراد"

#تعريف الاسم "معهد موسكو للهندسة الإلكترونية"

#تعريف قسم "المعلوماتية والبرمجة"

const int LIMIT=65;

#يشمل

مساحة فارغة (عدد صحيح)؛

cout<

space=(LIMIT - strlen(name))/2; // احسب كم

// تحتاج إلى مسافات

cout<

space((LIMIT - strlen(department))/2); // الوسيطة - التعبير

cout<

// تعريف وظيفة النجوم ().

ل (العد = 1؛ العد<=LIMIT; count++)

// تعريف وظيفة المساحة ().

مساحة فارغة (رقم int)

ل (العد = 1؛ العد<=number; count++)

يسمى المتغير الرقمي حجة رسمية. يأخذ هذا المتغير قيمة الوسيطة الفعلية عند استدعاء الدالة. بعبارة أخرى، حجة رسميةهو متغير في تعريف ما يسمى روتين فرعي، و الحجة الفعليةهي القيمة المحددة المخصصة لهذا المتغير بواسطة برنامج الاستدعاء.

إذا كانت الدالة تتطلب أكثر من وسيطة واحدة للتواصل معها، فيمكنك تحديد قائمة من الوسائط مفصولة بفواصل بالإضافة إلى اسم الدالة:

رقم الطباعة الفارغ (int i، int j)

(كوت<<"Координаты точек”<< i << j <

يمكن معالجة قيمة الإدخال للوظيفة بسبب وجودها دعوى; يتم إرجاع قيمة الإخراج باستخدام الكلمة الأساسية return.

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

ماذا تعني مصطلحات الوظيفة والإجراء؟

  • وظيفةفي البرمجة، يتم استدعاء روتين فرعي من الإجراءات الفرعية الأخرى بالعدد المطلوب من المرات.
  • إجراء- جزء مسمى من البرنامج (الروتين الفرعي)، يتم استدعاؤه بشكل متكرر من الأجزاء اللاحقة من البرنامج بالعدد المطلوب من المرات.

مقارنة الوظيفة والإجراءات

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

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

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

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

الفرق بين الوظيفة والإجراء في البرمجة هو كما يلي:

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

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

ما هي وظيفة

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

دعونا نلخص

لذلك، فإن الوظائف هي نوع من الحاويات لتجميع الخوارزميات. هم:

  1. مسؤول عن مهام محددة؛
  2. التفاعل مع الكائنات الأخرى.
  3. هي الأساس المفاهيمي للبرمجة الحديثة، مهما بدت مثيرة للشفقة.

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

المنشورات السابقة:

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

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

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

كيفية استدعاء وظيفة؟

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

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

وظيفة مرحبا () (صدى "مرحبا بالعالم!"؛)

ثم نتصل بها. علاوة على ذلك، إذا لم تكن تحتوي على معلمات، فإننا ببساطة نضع قوسين. لاستدعاء هذه الدالة نستخدم السطر فقط: مرحبًا()؛. يمكن لأي دالة أيضًا إرجاع قيمة باستخدام كلمة محجوزة يعود. يتوقف هذا البيان عن تنفيذ الوظيفة ويرسل القيمة المرجعة إلى برنامج الاستدعاء. مجموع الدالة($أولاً، $ثانيًا) ($r=$first + $ثانيًا؛ إرجاع $r;) echo sum(2,5); وستكون نتيجة تنفيذ البرنامج تساوي 7. المتغيرات المحلية والعالمية

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

$per="ديما"; function primer() // تنفيذ: عرض متغير محلي ( echo "My name is ".$per; ) echo primer();

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

$per="ديما"; function primer() // يؤدي: عرض متغير عام ( global $per; echo "My name is ".$per; ) echo primer();

يجب أن يكون كل شيء على ما يرام الآن - تم حل المشكلة. فقط لا تنس أنه إذا قامت دالة بتغيير قيمة متغير خارجي، فإن مثل هذا التغيير سيؤثر على البرنامج بأكمله، لذلك تحتاج إلى استخدام هذا العامل بعناية!

وظائف وسيطتين أو أكثر

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

… خط الوظيفة($text, $size=5) // افعل: حجم خط الإخراج ( echo " ".$نص."";) الخط("مرحبا
",1); الخط("مرحبا
",2); الخط("مرحبا
",3); الخط("مرحبا
",4); الخط("مرحبا
"،5)؛ الخط ("مرحبا
"،6)؛ الخط ("مرحبا
");

افتراضيًا، حجم الخط هو 5. إذا حذفنا المعلمة الثانية للدالة، فستكون مساوية لهذه القيمة.

خاتمة

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

include_once("function.php");

require_once("function.php");

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

هذا هو المقال الثالث في سلسلة “نظرية التصنيف للمبرمجين”.

من يحتاج إلى أنواع؟

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

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

لذا فإن السؤال هو، هل نريد أن تكون القرود سعيدة أم أن تنشئ برامج صحيحة؟
(ملاحظة المترجم: لا داعي للإهانة، فالمؤلف يحب الاستعارات الأقل مللًا من RNG و"التسلسلات العشوائية للبايتات"، ولا يطلق على المبرمجين اسم القرود).

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

هناك حاجة إلى أنواع للتكوين

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

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

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

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

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

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

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

ما هي الأنواع؟

أبسط وصف للأنواع هو أنها مجموعات من القيم. النوع Bool (تذكر أن الأنواع الملموسة تبدأ بحرف كبير في لغة هاسكل) يتوافق مع مجموعة من عنصرين: صحيح وخطأ. نوع Char هو مجموعة من كافة أحرف Unicode، مثل "a" أو "ą".

يمكن أن تكون المجموعات محدودة أو لا نهائية. يعد نوع السلسلة، وهو في الأساس مرادفًا لقائمة Char، مثالاً لمجموعة لا نهائية.

عندما نعلن أن x عدد صحيح:
س::عدد صحيح
نقول أنه عنصر من مجموعة الأعداد الصحيحة. العدد الصحيح في هاسكل هو مجموعة لا نهائية، ويمكن استخدامه في العمليات الحسابية بأي دقة. هناك أيضًا مجموعة محدودة من Int، والتي تتوافق مع نوع الجهاز، مثل int في C++.

هناك بعض التفاصيل الدقيقة التي تجعل من الصعب مساواة الأنواع بالمجموعات. هناك مشاكل مع الدوال متعددة الأشكال التي لها تعريفات دورية، وأيضًا مع حقيقة أنه لا يمكنك الحصول على مجموعة من جميع المجموعات؛ ولكن، كما وعدت، لن أكون عالم رياضيات صارمًا. المهم أن هناك فئة من المجموعات اسمها Set وسنعمل بها.
في المجموعة، الكائنات عبارة عن مجموعات والأشكال (الأسهم) هي وظائف.

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

في عالم مثالي، يمكننا أن نقول ببساطة أن الأنواع في هاسكل هي مجموعات، والدوال في هاسكل هي وظائف رياضية بينهما. هناك مشكلة صغيرة واحدة فقط: وظيفة الرياضيات لا تنفذ أي تعليمات برمجية - إنها تعرف الإجابة فقط. يجب أن تقوم دالة في هاسكل بحساب الإجابة. ولا تعتبر هذه مشكلة إذا كان من الممكن الحصول على الإجابة بعدد محدود من الخطوات، مهما كان هذا العدد كبيرًا. ولكن هناك بعض الحسابات التي تنطوي على التكرار، والتي قد لا تكتمل أبدًا. لا يمكننا ببساطة عدم السماح بالوظائف غير المنتهية في هاسكل لأن التمييز بين انتهاء الوظيفة أم لا - مشكلة التوقف الشهيرة - غير قابل للحل. ولهذا السبب توصل علماء الكمبيوتر إلى فكرة عبقرية، أو اختراق قذر حسب وجهة نظرك، لمد كل نوع بقيمة خاصة تسمى القاع (ملاحظة المترجم: هذا المصطلح (الأسفل) يبدو غبيًا نوعًا ما باللغة الروسية، إذا كان أي شخص يعرف خيارًا جيدًا، يرجى اقتراحه.)، والذي يُشار إليه بـ _|_ أو في Unicode ⊥. تتوافق هذه "القيمة" مع عملية حسابية غير مكتملة. لذلك تم الإعلان عن وظيفة على النحو التالي:
و::بول -> بول
قد يُرجع True أو False أو _|_؛ وهذا الأخير يعني أن الوظيفة لا تكتمل أبدًا.

ومن المثير للاهتمام، أنه بمجرد قبول "الأسفل" في نظام الكتابة، فمن المناسب التعامل مع كل خطأ في وقت التشغيل على أنه "أسفل"، وحتى السماح للوظيفة بالعودة إلى "الأسفل" بشكل صريح. يتم تنفيذ الأخير عادةً باستخدام التعبير غير المحدد:
f::Bool -> Bool f x = غير محدد
يقوم هذا التعريف بتمرير فحص النوع نظرًا لأنه يتم تقييم غير المحدد إلى الأسفل، وهو ما يتم تضمينه في جميع الأنواع، بما في ذلك Bool. يمكنك حتى أن تكتب:
f::Bool -> Bool f = غير محدد
(بدون x) لأن الجزء السفلي أيضًا عضو في النوع Bool -> Bool.

تسمى الوظائف التي يمكن أن ترجع إلى الأسفل جزئية، على عكس الوظائف العادية التي ترجع نتائج صالحة لجميع الوسائط الممكنة.

بسبب القاع، تسمى فئة أنواع ووظائف Haskell Hask، وليس Set. من وجهة نظر نظرية، يعد هذا مصدرًا لتعقيدات لا نهاية لها، لذا في هذه المرحلة سأستخدم سكين الجزار الخاص بي وسأنهي الأمر. من وجهة نظر عملية، من الممكن تجاهل الوظائف غير المنتهية والجزء السفلي ومعاملة Hask كمجموعة كاملة.

لماذا نحتاج إلى نموذج رياضي؟

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

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

تكمن المشكلة في أنه من الصعب جدًا إثبات أي شيء يتعلق بالبرامج التي تستخدم الدلالات التشغيلية. لإظهار خاصية برنامج ما، عليك بشكل أساسي "تشغيله" من خلال مترجم مثالي.

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

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

خذ بعين الاعتبار تعريف دالة المضروب في هاسكل، وهي لغة تفسح المجال بسهولة للدلالات الدلالية:
الحقيقة ن = المنتج
التعبير عبارة عن قائمة من الأعداد الصحيحة من 1 إلى n. تقوم دالة المنتج بضرب جميع عناصر القائمة. تمامًا مثل تعريف المضروب المأخوذ من الكتاب المدرسي. قارن هذا بـ C:
حقيقة int (int n) ( int i؛ نتيجة int = 1؛ لـ (i = 2؛ i<= n; ++i) result *= i; return result; }
هل يجب أن أستمر؟ (ملاحظة المترجم: غش المؤلف قليلاً من خلال تولي وظيفة مكتبة في هاسكل. في الواقع، لم تكن هناك حاجة للغش؛ فالوصف الصادق، بحكم التعريف، ليس أكثر صعوبة):
حقيقة 0 = 1 حقيقة ن = ن * حقيقة (ن - 1)
حسنًا، سأعترف على الفور أنها كانت لقطة رخيصة! العامل له تعريف رياضي واضح. قد يتساءل القارئ الذكي: ما هو النموذج الرياضي لقراءة حرف ما من لوحة المفاتيح، أو إرسال حزمة عبر الشبكة؟ لفترة طويلة، سيكون هذا سؤالا محرجا، مما يؤدي إلى تفسيرات مربكة إلى حد ما. يبدو أن الدلالات الدلالية غير مناسبة لعدد كبير من المشكلات المهمة التي كانت ضرورية لكتابة برامج مفيدة والتي يمكن حلها بسهولة عن طريق الدلالات التشغيلية. جاء الاختراق من نظرية الفئة. اكتشف يوجينيو موجي أن التأثيرات الحسابية يمكن تحويلها إلى أحاديات. وتبين أن هذه ملاحظة مهمة لم تمنح الدلالات الدلالية حياة جديدة فحسب، بل جعلت البرامج الوظيفية البحتة أكثر ملاءمة، ولكنها قدمت أيضًا معلومات جديدة حول البرمجة التقليدية. سأتحدث عن المونادات لاحقًا عندما نطور المزيد من الأدوات الفئوية.

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

وظائف نظيفة وقذرة

ما نسميه الدوال في لغة C++ أو أي لغة أمرية أخرى ليس هو نفسه ما يسميه علماء الرياضيات الدوال. الوظيفة الرياضية هي ببساطة تعيين من القيم إلى القيم.

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

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

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

أمثلة على الأنواع

بمجرد أن تقرر أن الأنواع عبارة عن مجموعات، يمكنك التوصل إلى بعض الأمثلة الغريبة جدًا. على سبيل المثال، ما هو النوع الذي يتوافق مع المجموعة الفارغة؟ لا، فهو ليس باطلاً في لغة C++، على الرغم من أن هذا النوع يسمى Void في لغة هاسكل. هذا هو النوع الذي لا يتم ملؤه بأي قيمة. يمكنك تحديد دالة تأخذ فراغًا، لكن لا يمكنك استدعاؤها أبدًا. لتسميتها، يجب عليك توفير قيمة من النوع Void، وهي ببساطة غير موجودة. أما بالنسبة لما يمكن أن تعوده هذه الوظيفة، فلا توجد قيود. يمكنه إرجاع أي نوع (على الرغم من أن هذا لن يحدث أبدًا لأنه لا يمكن استدعاؤه). بمعنى آخر، إنها دالة متعددة الأشكال في نوع الإرجاع الخاص بها. وقد أطلق عليها آل هاسكيلر:
سخيف::باطل -> أ
(ملاحظة المترجم: من المستحيل تعريف مثل هذه الوظيفة في لغة C++: في لغة C++، كل نوع له قيمة واحدة على الأقل.)

(تذكر أن a هو متغير النوع، والذي يمكن أن يكون أي نوع.) هذا الاسم ليس من قبيل الصدفة. هناك تفسير أعمق للأنواع والوظائف من حيث المنطق يسمى تماثل كاري هوارد. يمثل نوع الفراغ عدم الصدق، وتمثل الدالة السخيفة التأكيد على أن شيئًا ما يتبع من كذب، كما في العبارة اللاتينية "ex falso sequitur quodlibet". (ملاحظة المترجم: كل شيء يأتي من الكذب.)

بعد ذلك يأتي النوع المطابق للمجموعة المفردة. هذا هو النوع الذي له قيمة واحدة محتملة فقط. هذا المعنى هو ببساطة "هناك". قد لا تتعرف عليه على الفور، ولكنه باطل في لغة C++. فكر في الوظائف من وإلى هذا النوع. يمكن دائمًا استدعاء دالة باطلة. إذا كانت دالة خالصة، فسوف تُرجع دائمًا نفس النتيجة. فيما يلي مثال على هذه الوظيفة:
إنت f44 () (إرجاع 44؛)
قد تظن أن هذه الدالة تقبل "لا شيء"، ولكن كما رأينا للتو، لا يمكن استدعاء دالة تقبل "لا شيء" لأنه لا توجد قيمة تمثل النوع "لا شيء". إذن ماذا تقبل هذه الوظيفة؟ من الناحية النظرية، يتطلب الأمر قيمة وهمية لها مثيل واحد فقط، لذلك لا يتعين علينا تحديدها بشكل صريح في الكود. ومع ذلك، فإن هاسكل لديه رمز لهذا المعنى: الزوج الفارغ من الأقواس (). وبالتالي، نظرًا لمصادفة مضحكة (أو ليست مصادفة؟)، فإن استدعاء دالة من void يبدو هو نفسه في كل من C++ وHaskell. بالإضافة إلى ذلك، وبسبب حب هاسكل للإيجاز، يتم استخدام نفس الرمز () للنوع والمنشئ والقيمة الفردية المقابلة لمجموعة مفردة. هذه هي الوظيفة في هاسكل:
f44::() -> عدد صحيح f44() = 44
يعلن السطر الأول أن f44 سيقوم بتحويل النوع () المسمى "الوحدة" إلى النوع عدد صحيح. يحدد السطر الثاني أن f44 يستخدم مطابقة النمط لتحويل المنشئ الوحيد لواحد، وهو ()، إلى الرقم 44. يمكنك استدعاء هذه الوظيفة عن طريق توفير القيمة ():
f44()
لاحظ أن كل دالة من دالة واحدة تعادل اختيار عنصر واحد من نوع الهدف (هنا، تم تحديد عدد صحيح 44). في الواقع، يمكنك التفكير في f44 كتمثيل آخر للرقم 44. وهذا مثال لكيفية استبدال المرجع المباشر لعناصر المجموعة بوظيفة (سهم). الوظائف من واحد إلى نوع معين A تكون في مراسلات فردية مع عناصر المجموعة A.

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

رياضيًا، تقوم دالة من المجموعة A إلى مجموعة مفردة بتعيين كل عنصر إلى عنصر واحد من تلك المجموعة. لكل A هناك وظيفة واحدة بالضبط. هنا هو لعدد صحيح:
fInt::عدد صحيح -> () fInt x = ()
يمكنك إعطاؤه أي عدد صحيح ويقوم بإرجاع واحد. من باب الإيجاز، يسمح هاسكل باستخدام الشرطة السفلية كوسيطة، وهو ما يتم تجاهله. بهذه الطريقة، ليست هناك حاجة للتوصل إلى اسم لذلك. يمكن إعادة كتابة الكود أعلاه على النحو التالي:
fInt::عدد صحيح -> () fInt_ = ()
لاحظ أن تنفيذ هذه الوظيفة ليس فقط مستقلاً عن القيمة التي تم تمريرها إليها، ولكنه أيضًا مستقل عن نوع الوسيطة.

تسمى الوظائف التي يمكن تعريفها بنفس الصيغة لأي نوع متعددة الأشكال حدودياً. يمكنك تنفيذ مجموعة كاملة من هذه الوظائف بمعادلة واحدة، باستخدام معلمة بدلاً من نوع محدد. كيفية استدعاء دالة متعددة الأشكال من أي نوع إلى نوع واحد؟ وبالطبع سنسميها وحدة:
الوحدة::أ -> () الوحدة _ = ()
في C++ يمكنك تنفيذه على النحو التالي:
نموذج وحدة باطلة (T) ()
(ملاحظة المترجم: من أجل مساعدة المترجم على تحسينه في noop، فمن الأفضل أن يكون هكذا):
نموذج وحدة باطلة (T&&) ()
التالي في "تصنيف الأنواع" هو مجموعة من عنصرين. في لغة C++ يطلق عليه اسم bool، وفي لغة Haskell، ليس من المستغرب أن يطلق عليه اسم Bool. الفرق هو أنه في C++ bool هو نوع مدمج، بينما في Haskell يمكن تعريفه على النحو التالي:
بول البيانات = صحيح | خطأ شنيع
(يجب قراءة هذا التعريف على النحو التالي: يمكن أن يكون Bool إما True أو False.) من حيث المبدأ، سيكون من الممكن وصف هذا النوع في C++:
التعداد المنطقي (صحيح، خطأ)؛
لكن تعداد C++ هو في الواقع عدد صحيح. يمكن للمرء استخدام "تعداد الفئة" C++ 11، ولكن بعد ذلك سيتعين عليه تأهيل القيمة باسم الفئة: bool::true أو bool::false، ناهيك عن الحاجة إلى تضمين الرأس المقابل في كل ملف الذي يستخدمه.

تقوم وظائف Pure Bool ببساطة بتحديد قيمتين من النوع المستهدف، واحدة تتوافق مع True والأخرى تتوافق مع False.

تسمى الوظائف في Bool المسندات. على سبيل المثال، تحتوي مكتبة Data.Char في Haskell على العديد من المسندات، مثل IsAlpha أو isDigit. توجد مكتبة مماثلة في C++ ، والتي تعلن، من بين أمور أخرى، عن الدالتين isalpha وisdigit، ولكنها تُرجع قيمة int بدلاً من قيمة منطقية. يتم تعريف المسندات الحالية في وتسمى ctype::is(alpha, c) وctype::is(digit, c).



 


يقرأ:



كيفية ضبط توقيت ذاكرة الوصول العشوائي (RAM) بشكل صحيح؟

كيفية ضبط توقيت ذاكرة الوصول العشوائي (RAM) بشكل صحيح؟

تعمل ذاكرة الوصول العشوائي (RAM) بناءً على إشارات التحكم الصادرة من وحدة التحكم في الذاكرة، والتي تقع في الجسر الشمالي لمجموعة الشرائح (إنتل) أو مباشرة...

تثبيت Navitel على الملاح والكمبيوتر

تثبيت Navitel على الملاح والكمبيوتر

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

قم بتغيير كلمة المرور على خادم Minecraft من خلال حسابك الشخصي وفي العميل

قم بتغيير كلمة المرور على خادم Minecraft من خلال حسابك الشخصي وفي العميل

لعبة Minecraft يمكن أن تثير اهتمام أي لاعب على الإطلاق، لأنه يمكنك من خلالها إنشاء قصتك الخيالية الفردية و...

ما هو كابل مكبر الصوت

ما هو كابل مكبر الصوت

في العمل الاحترافي مع الصوت، من المهم جدًا فهم المبادئ الأساسية للتبديل بين أنواع مختلفة من المعدات، وهذا يجعل الأمر أسهل وأسرع...

صورة تغذية آر إس إس