ترکیب تابع (علوم کامپیوتر) کیست؟ توضیح بده.
در علوم کامپیوتر ، ترکیب تابع یک عمل یا مکانیزم برای ترکیب توابع ساده برای ساخت توابع پیچیده تر است. مانند ترکیب معمول توابع در ریاضیات ، نتیجه هر تابع به عنوان آرگومان تابع بعدی و حاصل آخرین تابع نتیجه کل است.برنامه نویسان اغلب توابعی را برای نتایج سایر توابع اعمال می کنند و تقریباً همه زبان های برنامه نویسی این امکان را دارند. در برخی موارد، ترکیب توابع به عنوان یک تابع در نوع خود جالب است که بعدا مورد استفاده قرار می گیرد. چنین تابعی همیشه قابل تعریف است، اما زبان هایی با توابع درجه یک آن را آسان تر می کنند.توانایی نوشتن آسان توابع، توابع فاکتورگیری (تجزیه) را برای نگهداری و استفاده مجدد از کد تشویق می کند. به طور کلی، سیستم های بزرگ ممکن است با نوشتن برنامه های کامل ساخته شوند.به بیان محدود، ترکیب تابع برای توابعی اعمال می شود که بر روی مقدار محدودی از داده ها کار می کنند، هر مرحله قبل از تحویل به مرحله بعدی، آن را به صورت متوالی پردازش می کند. توابعی که بر روی داده‌های بالقوه بی‌نهایت کار می‌کنند (یک جریان یا دیگر داده‌های کد ) به عنوان فیلتر شناخته می‌شوند و در عوض در یک خط لوله متصل می‌شوند که مشابه ترکیب تابع است و می‌تواند همزمان اجرا شود.تنظیم فراخوانی تابع.برای مثال، فرض کنید دو تابع ' و ' داریم، مانند و . نوشتن آنها به این معنی است که ابتدا را محاسبه می کنیم, و سپس "" برای محاسبه استفاده می کنیم . این مثال در زبان C است:foo←f∘gاگر به نتیجه میانی نامی ندهیم، مراحل را می توان ترکیب کرد:z = f(g(x));با وجود تفاوت در طول، این دو پیاده سازی نتیجه یکسانی را محاسبه می کنند. اجرای دوم تنها به یک خط کد نیاز دارد و به صورت محاوره ای به عنوان فرم "خیلی ترکیب شده" نامیده می شود. خوانایی و در نتیجه قابلیت نگهداری یکی از مزیت‌های فرم‌های بسیار ترکیبی است، زیرا به خطوط کد کمتری نیاز دارند و «مساحت سطح» برنامه را به حداقل می‌رسانند. DeMarco و Lister به طور تجربی رابطه معکوس بین سطح و قابلیت نگهداری را تأیید می کنند. از سوی دیگر، ممکن است استفاده بیش از حد از فرم های با ترکیب بالا امکان پذیر باشد. تودرتویی از توابع بیش از حد ممکن است اثر معکوس داشته باشد و کد را کمتر قابل نگهداری کند.در یک زبان مبتنی بر پشته ، ترکیب عملکردی حتی طبیعی تر است: این ترکیب توسط الحاق انجام می شود و معمولاً روش اصلی طراحی برنامه است. مثال بالا در Forth :g fکه هر آنچه قبلاً روی پشته بود را می گیرد، g و سپس f را اعمال کرده و نتیجه را روی پشته بگذارید. برای نماد ریاضی مربوطه، نماد ترکیب پسوند را ببینید.نامگذاری ترکیب توابع.حال فرض کنید ترکیب فراخوانی f() بر روی نتیجه g() اغلب مفید است و می‌خواهیم آن را foo() نامگذاری کنیم تا به تنهایی به عنوان یک تابع استفاده شود.در اکثر زبان ها، ما می توانیم یک تابع جدید را تعریف کنیم که توسط ترکیب پیاده سازی شده است. مثال در C :float foo(float x) { return f(g(x));(شکل طولانی با واسطه ها نیز کار می کند.) مثال در Forth :در زبان هایی مانند C ، تنها راه برای ایجاد یک تابع جدید، تعریف آن در منبع برنامه است، به این معنی که توابع را نمی توان در زمان اجرا ترکیب کرد. با این حال، ارزیابی یک ترکیب دلخواه از توابع "از پیش تعریف شده" ممکن است:typedef int FXN(int);int eval(FXN *fs[], int size, int x) for (int i=0; i&lt;size; i++) x = (*fs[i])(x); return x;int main() // ((6+1)*2)-3 = 11 FXN *arr[] = {f,g,h}; printf("%d\n", eval(arr, 3, 6)); // ((6-3)*2)+1 = 7 arr[2] = f; arr[0] = h; printf("%d\n", eval(arr, 3, 6));ترکیب بندی درجه یک.در زبان های برنامه نویسی تابعی ، ترکیب تابع را می توان به طور طبیعی به عنوان یک تابع یا عملگر درجه بالاتر بیان کرد. در سایر زبان های برنامه نویسی می توانید مکانیسم های خود را برای انجام ترکیب تابع بنویسید.هاسکل.در Haskell ، مثال داده شده در بالا تبدیل می شود:foo = f . gبا استفاده از عملگر ترکیب داخلی (.) که می تواند به صورت "f بعد از g" یا "g تشکیل شده با f" خوانده شود.عملگر ترکیب خود را می توان در Haskell با استفاده از عبارت lambda تعریف کرد:(.) :: (b -&gt; c) -&gt; (a -&gt; b) -&gt; a -&gt; cf . g = \x -&gt; f (g x)خط اول نوع (.) را توصیف می کند - یک جفت تابع را می گیرد و یک تابع را برمی گرداند (عبارت لامبدا در خط دوم). توجه داشته باشید که Haskell نیازی به مشخصات دقیق انواع ورودی و خروجی f و g ندارد. a، b، c و x جایگاه‌بان‌ها هستند. فقط رابطه بین مهم است (f باید آنچه را که g برمی گرداند بپذیرد). این باعث می شود (.) یک عملگر چند شکلی باشد .لیسپ.انواع Lisp ، به ویژه Scheme ، قابلیت تعویض کد و داده به همراه پردازش توابع، خود را بسیار مناسب برای تعریف بازگشتی از یک عملگر ترکیبی متغیر می‌سازد. (if (null? fs) (lambda (x) x) ; if no argument is given, evaluates to the identity function (lambda (x) ((car fs) ((apply compose (cdr fs)) x))))) (string-append str "!"))(define givebang (compose string-&gt;symbol add-a-bang symbol-&gt;string))(givebang 'set) ; ===&gt; set!((compose sqrt - sqr) 5) ; ===&gt; 0+5iAPL.بسیاری از گویش‌های APL در ترکیب تابع با استفاده از نماد codice_1 ساخته شده‌اند. این تابع مرتبه بالاتر ترکیب تابع را به کاربرد دوتایی تابع سمت چپ گسترش می دهد به طوری که codice_2 codice_3 باشد.foo←f∘gعلاوه بر این، می توانید ترکیب تابع را تعریف کنید:در لهجه ای که از تعریف درون خطی با استفاده از پرانتز پشتیبانی نمی کند، تعریف سنتی موجود است:∇ r←(f o g)x r←f g xراکو.Raku مانند Haskell دارای یک عملگر ترکیب تابع داخلی است، تفاوت اصلی این است که به صورت codice_1 یا codice_5 نوشته می شود.my &amp;foo = &amp;f ∘ &amp;g;همچنین مانند Haskell شما می توانید اپراتور را خودتان تعریف کنید. در واقع کد Raku مورد استفاده برای تعریف آن در پیاده سازی Rakudo است.Nim.Nim از نحو فراخوانی تابع یکنواخت پشتیبانی می کند که امکان ترکیب تابع دلخواه را از طریق نحو متد فراهم می کند codice_6 اپراتور &lt;ref&gt;&lt;/ref&gt;&lt;syntaxhighlight lang="nim"&gt;func foo(a: int): string = $afunc bar(a: string, count: int): seq[string] = for i in 0 ..&lt; count: result.add(a)func baz(a: seq[string]) = for i in a: echo iecho foo(5).bar(6).baz()echo baz(bar(6, foo(5)))پایتون.در پایتون ، راهی برای تعریف ترکیب برای هر گروه از توابع، استفاده از تابع کاهش است (از functools.reduce در پایتون استفاده کنید. 3):from functools import reducefrom typing import Callabledef compose(*funcs) -&gt; Callable[[int], int]: """Compose a group of functions (f(g(h(...)))) into a single composite func.""" return reduce(lambda f, g: lambda x: f(g(x)), funcs)f = lambda x: x + 1g = lambda x: x * 2h = lambda x: x - 3print(compose(f, g, h)(10))جاوا اسکریپت.در [[جاوااسکریپت|جاوا اسکریپت]] می توانیم آن را به عنوان تابعی تعریف کنیم که دو تابع f و g را می گیرد و یک تابع تولید می کند:function o(f, g) { return function(x) { return f(g(x));// Alternatively, using the rest operator and lambda expressions in ES2015const compose = (...fs) =&gt; (x) =&gt; fs.reduceRight((acc, f) =&gt; f(acc), x)سی شارپ.در [[سی‌شارپ|سی شارپ]] می توانیم آن را به عنوان یک متد Extension تعریف کنیم که Funcs f و g را می گیرد و یک Func جدید تولید می کند:// Call example:// var c = f.ComposeWith(g);// Func&lt;int, bool&gt; g = _ =&gt; ...// Func&lt;bool, string&gt; f = _ =&gt; ...public static Func&lt;T1, T3&gt; ComposeWith&lt;T1, T2, T3&gt;(this Func&lt;T2, T3&gt; f, Func&lt;T1, T2&gt; g) =&gt; x =&gt; f(g(x));روبی.زبان‌هایی مانند [[روبی (زبان برنامه‌نویسی)|Ruby]] به شما امکان می‌دهند خودتان یک عملگر باینری بسازید:class Proc def compose(other_fn) end alias_method :+, :composeend(f + g).call(12) # =&gt; 13824با این حال، یک عملگر ترکیب تابع بومی در Ruby 2.6 معرفی شد: نظرسنجی پژوهشی.مفاهیم ترکیب، از جمله [[Principle of compositionality|اصل ترکیب‌پذیری]] و [[Composability|ترکیب‌پذیری]] ، آنقدر در همه جا وجود دارند که رشته‌های تحقیقاتی متعددی به طور جداگانه تکامل یافته‌اند. در زیر نمونه‌ای از پژوهش‌هایی است که مفهوم ترکیب در آن محوریت دارد.ترکیب در مقیاس بزرگ.کل برنامه‌ها یا سیستم‌ها را می‌توان به‌عنوان توابعی در نظر گرفت که در صورتی که ورودی‌ها و خروجی‌های آن‌ها به خوبی تعریف شده باشند، می‌توانند به راحتی ترکیب شوند. [[خط لوله (نرم‌افزار)|خطوط لوله ای]] که امکان ترکیب آسان [[Filter (software)|فیلترها را]] فراهم می کردند، چنان موفق بودند که به [[خط لوله (نرم‌افزار)|الگوی طراحی]] سیستم عامل تبدیل شدند.[[برنامه‌نویسی دستوری|رویه‌های ضروری]] با عوارض جانبی [[Referential transparency|شفافیت ارجاعی را]] نقض می‌کنند و بنابراین کاملاً قابل ترکیب نیستند. با این حال، اگر "وضعیت جهان" قبل و بعد از اجرای کد را به عنوان ورودی و خروجی آن در نظر بگیریم، یک تابع تمیز دریافت می‌کنیم. ترکیب چنین توابعی با اجرای رویه ها یکی پس از دیگری مطابقت دارد. فرمالیسم [[موناد]] از این ایده برای ترکیب عوارض جانبی و ورودی/خروجی (I/O) در زبان های کاربردی استفاده می کند.