واجهات وظيفية ... في VBA

"... أولئك الذين لا يكرهون التحديق في أحد الهواة يلومون على الملأ علنًا ، اسمح لهم بمراقبة كيف أثبت أن Java و Visual Basic هما توأمان منفصلان عند الولادة ، وأن C ++ ليسا حتى قريبين بعيدين."

Bruce McKinney "Die Hard Basic Basic"

مقدمة


يؤدي الاهتمام المستمر بنُهج البرمجة الوظيفية في الوقت الحالي إلى حقيقة أن لغات البرمجة التقليدية تكتسب بنشاط وسائل وظيفية. على الرغم من أن اللغات الوظيفية الخالصة لا تزال غير شائعة ، إلا أن الوظيفة ثابتة في لغات مثل C ++ و Java و JavaScript و Python وما إلى ذلك. كانت لغة VBA معروفة جيدًا لسنوات عديدة بين جمهور كبير من مستخدمي Microsoft Office. هذه اللغة لا تحتوي على أدوات وظيفية.

دعونا نحاول سد هذه الفجوة - أقترح تطبيقًا كاملاً (على الرغم من أنه قد لا يكون مثاليًا) للواجهات الوظيفية التي تنفذها VBA. يمكن أن يكون التنفيذ بمثابة أساس لعمليات التحسين والتحسين اللاحقة.

مشكلة الحجج الوظيفية


المشكلة الأولى التي سنواجهها في هذا المسار هي مشكلة تمرير الوسائط الوظيفية إلى دالة أو طريقة. لا تحتوي لغة VBA على الأدوات المناسبة (يعمل عامل تشغيل AddressOf فقط لنقل العناوين إلى وظائف Windows API وليس آمنًا تمامًا للاستخدام). يمكن قول الشيء نفسه عن الطريقة المعروفة لوظائف الاستدعاء بواسطة المؤشر (G. Magdanurov Visual Basic في الممارسة العملية سانت بطرسبرغ: "BHV Petersburg" ، 2008). دعونا لا نتحمل المخاطر - نحن نستخدم فقط ميزات اللغة القياسية والمكتبات القياسية في التنفيذ.

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

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

افترض أنك تريد تمرير دالة مجهولة إلى إجراء proc معين يزيد وسيطة من جانب واحد. يمكن كتابة هذه الوظيفة على النحو التالي:

x -> x+1 

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

 r=proc(a,b,”x->x+1”) 

هنا a و b هما معلمتان عاديتان ، والمعلمة الثالثة هي وظيفة غير مسماة ، وهي واضحة جدًا ولا تختلف كثيرًا عن الإدخالات بلغات البرمجة الشائعة.

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

 Private Function prepCode(Code As String) As String k% = InStr(Code, "->") parms$ = Trim$(Left$(Code, k% - 1)) body$ = Mid$(Code, k% + 2) If Left$(parms$, 1) <> "(" Then parms$ = "(" + parms$ + ")" If InStr(body$, "self") = 0 Then body$ = ";self=" & body$ & ";" body$ = Replace(body$, ";", vbCrLf) prepCode = "function self" & parms & vbCrLf & body & _ vbCrLf & "end function" End Function 

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

 function self(x) self=x+1 End function 

من الواضح أنه وفقًا لبناء جملة VBA ، ستعمل هذه الوظيفة تمامًا على ما يجب أن تفعله الوظيفة المجهولة - فهي تزيد من قيمة الوسيطة الخاصة بها بـ 1. صحيحًا ، هذه الوظيفة ليست وظيفة VBA بعد ، ولكن فقط سلسلة تحتوي على الكود المحدد. لتحويل سلسلة إلى دالة ، يمكنك استخدام مكتبة Microsoft القياسية "Msscript.ocx". تتيح لك مكتبة COM هذه تنفيذ كود VBA التعسفي المحدد في نموذج السلسلة. للقيام بذلك ، قم بما يلي:

- إنشاء كائن ScriptControl
- استدعاء طريقة تثبيت اللغة (VBScript) ؛
- استدعاء طريقة تحميل وظيفة.
- استدعاء طريقة eval لإجراء المكالمة.

يبدو كل شيء مثل هذا:

 Set locEv=new ScriptControl locEv.Language = "VBScript" locEv.AddCode prepCode(“x->x+1”) r=locEv.eval(“self(5)”) 

بعد تنفيذ هذا الرمز ، ستكون قيمة المتغير r 6.

يجب أن تكون هنا ثلاث نقاط:

  • يمكن أن يحتوي نص دالة مجهولة على عدة أسطر. البيانات الفردية في هذه الحالة تنتهي بفاصلة منقوطة. من الرمز النهائي ، الأحرف "؛" مستبعدون يسمح لك الجسم متعدد الخطوط بتنفيذ وظائف متقدمة للغاية في وظائف مجهولة ؛
  • حقيقة أن الوظيفة المجهولة "في الواقع" تحمل اسم "الذات" تمنح مكافأة غير متوقعة - وظيفة مجهولة يمكن أن تكون متكررة.
  • نظرًا لأن كائن ScriptControl يدعم لغتين - VBScript و Jscript ، يمكن كتابة الدالة المجهولة (نظريًا) في Jscript (يمكن لأولئك الذين يرغبون في تجربتها).

بعد ذلك ، سيتم وصف نموذج تنفيذ الكائن.

نموذج الكائن


أساس النموذج هي كائنات من نوعين: حاوية ومولد. كائن الحاوية هو مستودع لمجموعة من الأحجام التعسفية ، كائن المولد ، كما يوحي الاسم ، يقوم بتنفيذ منشئ نموذج عام.

يطبق كلا الكائنين واجهة aIter ، الموضحة بمزيد من التفاصيل أدناه. تتضمن الواجهة 19 وظيفة:

اسم الطريقةالمعلماتيؤدي
isGen-إرجاع صحيح إذا كان الكائن هو مولد.
isCont-إرجاع صحيح إذا كان الكائن حاوية.
getCont-إرجاع حاوية صفيف محلي لإرجاع حاوية فارغة
getNext-إرجاع القيمة التالية
hasNext-إرجاع True إذا كانت القيمة التالية موجودة
التهيئةiniVal As Variant ، lambda As String = ""، blankC As Boolean = FalseiniVal - القيمة الأولية ؛
امدا - وظيفة مجهولة المصدر للمولد
blankC - عند التعيين على True ، يتم إنشاء حاوية فارغة
أخذن عدد صحيحإرجاع حاوية تحتوي على قيم n متعاقبة تم الحصول عليها من الكائن المصدر
فلترامدا كسلسلةإرجاع الكائن الذي تم الحصول عليه عن طريق تصفية الأصل وفقًا لوظيفة lambda غير المسماة
خريطةامدا كسلسلةإرجاع الكائن الذي تم الحصول عليه عن طريق تعيين الأصل وفقًا لوظيفة lambda غير المسماة
الحدلجنة التنسيق الإدارية كما البديل ، امدا كما سلسلة ،تُرجع نتيجة الإلتفاف للكائن الحالي مع قيمة البطارية الأولية acc ووظيفة الطي المحددة بواسطة معلمة lambda
takeWhilen كعدد صحيح ،
امدا كسلسلة
إرجاع حاوية تحتوي على قيم n (أو أقل) متتالية تفي بالتقييم المحدد بواسطة وظيفة lambda غير المسماة
dropWhilen كعدد صحيح ،
امدا كسلسلة
إرجاع حاوية تحتوي على n (أو أقل) قيم متتالية تم الحصول عليها من الأصل بعد تخطي القيم التي تفي بالتنبؤ المحدد بواسطة lambda.
الرمز البريديانها بمثابة ،
ن كعدد صحيح = 10
يقبل حاوية أو مولد ، ويعيد حاوية تحتوي على أزواج من القيم - من الحاوية الأساسية ومن حاوية الحاوية. حجم النتيجة الافتراضي هو عشرة.
zipWithانها بمثابة ،
امدا كسلسلة ،
ن كعدد صحيح = 10
يأخذ حاوية ودالة مجهولة من اثنين من الحجج. إرجاع حاوية تحتوي على نتائج تطبيق الوظيفة المحددة على أزواج متتالية - قيمة واحدة من الحاوية الأساسية ، والأخرى من حاوية المعلمة.
حجم-بالنسبة للحاوية ، تُرجع عدد العناصر
الخلاصه-مجموع قيم الحاوية
إنتاج-نتاج قيم الحاوية
أقصى-القيمة القصوى في الحاوية
الحد الأدنى-القيمة الدنيا في الحاوية

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

أمثلة


طباعة أرقام Fibonacci المتتالية:
 Sub Test_1() Dim fibGen As aIter Set fibGen = New Generator fibGen.Init Array(1, 0), "(c,p)->c+p" For i% = 1 To 50 Debug.Print fibGen.getNext() Next i% End Sub 

هنا يتم إنشاء مولد بالقيم الأولية 0 و 1 ووظيفة توليد متوافقة مع تسلسل فيبوناتشي. بعد ذلك ، تتم طباعة أول 50 رقمًا في حلقة.
خريطة وفلتر:

 Sub Test_2() Dim co As aIter Dim Z As aIter Dim w As aIter Set co = New Container co.Init frange(1, 100) Set Z = co.map("x -> 1.0/x"). _ take(20).filter(" x -> (x>0.3) or (x<=0.1)") iii% = 1 Do While Z.hasNext() Debug.Print iii%; " "; Z.getNext() iii% = iii% + 1 Loop End Sub 

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

 Sub Test_4() Dim co As aIter Set co = New Container co.Init frange(1, 100) v = co.reduce(0, "(acc,x)->acc+x") Debug.Print v v = co.reduce(1, "(acc,x)->acc*x") Debug.Print v End Sub 

هنا ، باستخدام الإلتفاف ، يتم اعتبار مجموع ومنتج الأرقام من 1 إلى 100.

 Sub Test_5() Dim co1 As aIter Dim co2 As aIter Dim co3 As aIter Set co1 = New Generator co1.Init Array(123456789), "x -> INT(x/10)" Set co2 = co1.takeWhile(100, "x -> x > 0") Set co3 = co2.map("x -> x mod 10") Debug.Print co3.maximun Debug.Print co3.minimum Debug.Print co3.summa Debug.Print co3.production End Sub 

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

النتائج


النهج المقترح عملي للغاية ويمكن تطبيقه بنجاح لحل المهام اليومية لمبرمج VBA بأسلوب وظيفي. لماذا نحن أسوأ من الجاويين؟

قم بتنزيل الأمثلة هنا

حظا سعيدا !!!

Source: https://habr.com/ru/post/ar455347/


All Articles