البرمجة الوظيفية هي ما قيل لك (على الأرجح). إذا استمعت

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

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

ما مدى صحة هذا؟

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

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

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

إلى هذه النقطة


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

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

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

الكسل هو محرك التقدم


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

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

fibonacci2 a b = a : (fibonacci2 b (a+b))
fibonacci = fibonacci2 1 1

nfibonacci n = take n fibonacci

شرح للغرباء مع هاسكل
fibonacci2 , , fibonacci2 b (a+b). ( !) :
def fibonacci2(a, b) :
    return [a] + fibonacci2(b, a+b)

def fibonacci() :
    return fibonacci2(1, 1)

def nfibonacci(n) :
    res = []
    data = fibonacci()
    for i in range(n) :
      res.append( data[i] )
    return res

nfibonacci.

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

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

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

def fibonacci() :
    a = 1
    b = 1
    yield a
    yield b
    while True :
      c = a + b
      yield c
      a = b
      b = c
     
def nfibonacci(n) :
    return [e for e in itertools.islice(fibonacci(), n)]

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

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

النظافة هي مفتاح الصحة


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

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

من الطبيعي بالنسبة لنا أن نتوقع أنه إذا حددنا دالة أولاً

def f(x,y) :
  ...

وبعدها
def g(x, y) :
  return f(y, x)

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

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

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

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

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

?


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

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

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

نحن نحسب هذه الحجة ، ونقدم قيمتها f ، بالإضافة إلى حساب كل شيء يمكننا حسابه بدون استخدام قيمة الوسيطة y . ثم يمكن تمثيل بقية الحسابات كدالة جديدة ، مستقلة بالفعل عن x ، على سبيل المثال ، g (y) . ولكن في هذه الحالة ، لا شيء يمنعنا من تقديم f رسميًا ليس كدالة لحجتين ، ولكن كدالة لحجة واحدةf (x) ، والنتيجة هي دالة أخرى g (y) . وبعبارة أخرى ، في إطار النهج الوظيفي ، فإن أي وظيفة من وسيطات N> 1 هي دالة مرتبة أعلى ، حيث يمكن تفسيرها كدالة لحجة واحدة ، والنتيجة هي وظيفة وسيطات N-1 .

هل يمكننا تنفيذ هذا السلوك كجزء من نهج حتمي؟ بالطبع نستطيع. في بايثون ، نكتب شيئًا مثل ما يلي:

def partial(f, x) :
	def g(*args) :
		return f(x, *args)
	return g

من خلال استدعاء الدالة الجزئية ، التي تكون الوسيطة الأولى لها هي دالة الوسيطات N ، والثانية هي قيمة الوسيطة الأولى ، نحصل على الدالة N-1 للوسيطة. الآن يمكننا استخدام الدالة الجديدة أينما يمكننا استخدام دالة الوسيطة N-1 . أي أنهم حصلوا على نفس الشيء في البرنامج الوظيفي. وبالتالي؟ لا ليس هكذا. إذا كنا نتعامل مع برنامج فعال حقًا ، فعندما نطلق على الجزئي ، سنحسب جزءًا من قيمة الحجة الأولى. في بعض الحالات ، قد يتبين أن g هي قيمة ثابتة. ماذا لدينا في التناظرية الحتمية؟ القيمة التي تم تمريرها للوسيطة xتذكرت للتو (أضيفت إلى سياق الوظيفة ز ). عندما نسمي g ، سيتم أخذ قيمة x من الصناديق واستبدالها ببساطة في f . أي أنه لا يوجد فرق في الشكل ، ولكن في المحتوى - مهم.

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

فمن الذي يخون من؟


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

  • الفرق الرئيسي بين البرمجة الوظيفية والضرورية ليس خاصية نقاء الوظائف ، وليس وجود وظائف مجهولة الهوية ، أو وظائف أوامر أعلى ، أو موناد ، أو تعدد الأشكال البارامترية ، أو أي شيء آخر. والفرق الرئيسي هو استخدام نموذج حساب مختلف. كل شيء آخر ليس أكثر من عواقب.
  • , , . . , «» «» . , . .
  • , , , . . — .
  • , . , «» ; , . , - , - . .
  • , . , . , , , . .

البرمجة الوظيفية هي ما قيل لك (على الأرجح) عنه. وهي عبارة عن تقليل بيتا ، ومركب نقطة ثابتة ، و monads ، وكتابة Hindley-Milner ، والمزيد. لا تخلط بين الغلاف والمحتوى. لا تعتمد FP على أبسط الرياضيات ؛ لا يمكن إتقانها لبضع أمسيات مع كوب من الشاي ؛ من غير المحتمل أن يتم عرضك مباشرة على مشكلاتك ومشاريعك الملحة ؛ فلن تحصل على ربح مضمون وسريع من هذه المعرفة. ولكن العديد من عناصر ما هو في النهج الوظيفي يتم استعارتها ومعالجتها وتنفيذها في نهاية المطاف في لغات البرمجة الموجهة نحو تطوير المشاريع الكبيرة. نعم ، يتم ترتيبهم بشكل مختلف عن أسلافهم الوظيفيين ، لكن هذا لا يجعلهم أقل فائدة. فقط أحمق سريري سينشر رسالة جادة بأن هاسكل هي لغة سيئة ،لأنه من الصعب كتابة برنامج لأي نوع من المحاسبة. إن الشخص المثقل بحضور الفكر ، حتى من وجهة نظر نشاطه المهني ، دون الانغماس العميق في تعقيدات النظرية ، قادر تمامًا على فهم أي ممارسات من البرمجة الوظيفية التي يجب اعتمادها من أجل تحسين شفرته. لمظاهرة مقنعة أعبر عن الامتنان لهاPsyhast.

تعلم البرمجة الوظيفية. باسم نفسك.

All Articles