Currying هي تقنية برمجة وظيفية تقوم بتحويل دالة ذات وسائط متعددة إلى سلسلة من الوظائف، كل منها يأخذ وسيطة واحدة. يعد هذا الأسلوب مفيدًا بشكل خاص لإنشاء المزيد من الوظائف المعيارية والقابلة لإعادة الاستخدام، مما يسمح بالتطبيق الجزئي للوسائط. في TypeScript، يتطلب تنفيذ دالة الكاري الفعالة إدارة الكتابة بعناية، خاصة عند التعامل مع عدد متغير من الوسائط.
في هذه المقالة، سنستكشف تطبيقين مختلفين لوظيفة الكاري في TypeScript. يستخدم الأول واجهات ذات أنواع ثابتة، بينما يتبنى الثاني نهجًا أكثر مرونة باستخدام واجهة واحدة مع أنواع متنوعة. سنقوم بتحليل الاختلافات بين هذين التطبيقين ومناقشة مزايا النهج الأكثر تحسينًا.
في التنفيذ الأول، قمت بتحديد سلسلة من الواجهات للتعامل مع الوظائف المتجانسة بأعداد مختلفة من الوسائط. تتوافق كل واجهة مع دالة بعدد محدد من الوسائط:
interface CurryFunction1{ (arg1: T1): R; } interface CurryFunction2 { (arg1: T1): CurryFunction1 ; } interface CurryFunction3 { (arg1: T1): CurryFunction2 ; } interface CurryFunction4 { (arg1: T1): CurryFunction3 ; } interface CurryFunction5 { (arg1: T1): CurryFunction4 ; } interface CurryFunction6 { (arg1: T1): CurryFunction5 ; }
يتم تعريف وظيفة الكاري لاستخدام هذه الواجهات لوظائف الكاري مع ما يصل إلى ست وسائط:
function curry(fn: (arg1: T1, arg2: T2) => R): CurryFunction2 ; function curry (fn: (arg1: T1, arg2: T2, arg3: T3) => R): CurryFunction3 ; function curry (fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => R): CurryFunction4 ; function curry (fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => R): CurryFunction5 ; function curry (fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) => R): CurryFunction6 ; function curry(fn: Function) { return function curried(...args: any[]) { if (args.length >= fn.length) { return fn(...args); } else { return (...args2: any[]) => curried(...args, ...args2); } }; }
يتم بعد ذلك اختبار هذه الوظيفة للتأكد من أنها تعمل بشكل صحيح مع أعداد مختلفة من الوسائط:
function testCurry() { const add = (a: number, b: number) => a b; const curriedAdd = curry(add); assert(curriedAdd(1)(2) === 3, 'Test curry function with 2 arguments'); const add3Args = (a: number, b: number, c: number) => a b c; const curriedAdd3Args = curry(add3Args); assert(curriedAdd3Args(1)(2)(3) === 6, 'Test curry function with 3 arguments'); }
على الرغم من أن هذا التنفيذ واضح ونموذجي لـ TypeScript، إلا أنه يحتوي على بعض القيود. والجدير بالذكر أنه يتطلب تعريف واجهات متعددة لكل عدد ممكن من الوسائط، مما يجعل التعليمات البرمجية زائدة عن الحاجة ويصعب صيانتها. بالإضافة إلى ذلك، فإن التعامل مع أكثر من ست وسيطات يتطلب إضافة المزيد من الواجهات، مما يزيد من التعقيد.
لتحسين وظيفة الكاري، اعتمدت نهجًا أكثر ديناميكية باستخدام واجهة عامة واحدة مع أنواع متنوعة. يسمح هذا الأسلوب بمعالجة عدد عشوائي من الوسائط دون الحاجة إلى تحديد واجهة منفصلة لكل حالة.
في هذا الإصدار المحسّن، يتم تنفيذ وظيفة الكاري باستخدام واجهة عامة واحدة تستفيد من الأنواع المتنوعة لـ TypeScript للتعامل مع عدد عشوائي من الوسائط:
type CurryFunction= T extends [infer A, ...infer Rest] ? (arg: A) => CurryFunction : R; function curry (fn: (...args: T) => R): CurryFunction { return function curried(...args: unknown[]): unknown { if (args.length >= fn.length) { return fn(...args as T); } else { return (...args2: unknown[]) => curried(...([...args, ...args2] as unknown[])); } } as CurryFunction ; }
تقليل التعقيد : باستخدام واجهة عامة واحدة CurryFunction، يلغي هذا التنفيذ الحاجة إلى إنشاء واجهات متعددة لكل عدد ممكن من الوسائط. وهذا يجعل الكود أكثر إيجازًا وأسهل في الصيانة.
دعم عدد عشوائي من الوسائط : الاستفادة من الأنواع المتغيرة تسمح لهذه الوظيفة بدمج الوظائف مع أي عدد من الوسائط دون تعديل التنفيذ. وبالتالي فإن الوظيفة أكثر مرونة وقدرة على التكيف مع السيناريوهات المختلفة.
تحسين الكتابة : تتيح الكتابة الديناميكية لـ TypeScript استنتاج أنواع الوسائط بدقة، مما يوفر فحصًا أقوى للنوع أثناء التطوير، ويقلل من مخاطر الأخطاء، ويحسن إكمال التعليمات البرمجية.
اختبار الوظيفةCurry() {
const add = (a: number, b: number) => a b;
const curriedAdd = curry(add);
تأكيد(curriedAdd(1)(2) === 3, 'اختبار دالة الكاري باستخدام وسيطتين');
const add3Args = (a: number, b: number, c: number) => a b c;
const curriedAdd3Args = curry(add3Args);
تأكيد(curriedAdd3Args(1)(2)(3) === 6, 'اختبار دالة الكاري بثلاث وسائط');
const add4Args = (a: number, b: number, c: number, d: number) => a b c d;
const curriedAdd4Args = curry(add4Args);
تأكيد(curriedAdd4Args(1)(2)(3)(4) === 10, 'اختبار دالة الكاري بأربع وسائط');
}
function testCurry() { const add = (a: number, b: number) => a b; const curriedAdd = curry(add); assert(curriedAdd(1)(2) === 3, 'Test curry function with 2 arguments'); const add3Args = (a: number, b: number, c: number) => a b c; const curriedAdd3Args = curry(add3Args); assert(curriedAdd3Args(1)(2)(3) === 6, 'Test curry function with 3 arguments'); const add4Args = (a: number, b: number, c: number, d: number) => a b c d; const curriedAdd4Args = curry(add4Args); assert(curriedAdd4Args(1)(2)(3)(4) === 10, 'Test curry function with 4 arguments'); }
يعد الانتقال من بنية ذات واجهات متعددة إلى واجهة عامة واحدة مثالًا رائعًا لكيفية فهم وتطبيق مفاهيم TypeScript المتقدمة يمكن أن يؤدي إلى حلول أكثر أناقة وكفاءة.
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3