اگر چندین فریم‌ورک و برنامه‌های کاربردی در مقیاس بزرگ را بررسی کنید، مطمئناً در مقطعی عملکردی با مرتبه بالاتر خواهید دید. بسیاری از زبان ها از ایده توابع درجه بالاتر پشتیبانی می کنند، از جمله جاوا اسکریپت، جاوا، دات نت، پایتون و حتی پی اچ پی.

اما تابع مرتبه بالاتر چیست و چرا می خواهیم از آن استفاده کنیم؟ چه مزایایی به ما می دهد و آیا می توانیم از آن برای ساده سازی کد خود استفاده کنیم؟ در این مقاله، ما به طور خاص در مورد توابع درجه بالاتر در PHP صحبت خواهیم کرد، اما نشان خواهیم داد که چگونه آنها در زبان های دیگر برای مقایسه استفاده شده اند.

توابع درجه یک

قبل از اینکه بتوانیم به این موضوع بپردازیم که یک تابع مرتبه بالاتر چیست، ابتدا باید درک کنیم که باید زبانی داشته باشیم که بتواند از ویژگی به نام پشتیبانی کند. توابع درجه یک (با نام مستعار توابع مرتبه اول). این بدان معنی است که زبان با عملکردها به عنوان شهروندان درجه یک رفتار می کند. به عبارت دیگر، زبان با توابع مانند متغیرها رفتار می کند. شما می توانید یک تابع را در یک متغیر ذخیره کنید، آن را به توابع دیگر به عنوان یک متغیر منتقل کنید، آنها را از توابعی مانند متغیرها برگردانید و حتی آنها را در ساختارهای داده مانند آرایه ها یا ویژگی های شی ذخیره کنید، مانند شما با متغیرها. اکثر زبان های مدرن این روزها این ویژگی را به طور پیش فرض دارند. تنها چیزی که واقعاً باید بدانید این است که یک تابع را می توان مانند متغیرها منتقل کرد و از آن استفاده کرد.

برای اهداف ما، ما بیشتر بر انتقال توابع به عنوان آرگومان و برگرداندن توابع به عنوان نتیجه تمرکز خواهیم کرد و به طور خلاصه به مفهوم متغیرهای غیرمحلی و بسته شدن اشاره خواهیم کرد. (شما می توانید در مورد این مفاهیم در بخش ها بیشتر بخوانید 1.1، 1.4 و 1.3 از مقاله ویکی پدیا که در پاراگراف قبلی به آن پیوند داده شده بود.)

توابع مرتبه بالاتر چیست؟

دو ویژگی اصلی وجود دارد که یک تابع درجه بالاتر را مشخص می کند. یک تابع مرتبه بالاتر می تواند فقط یک یا هر دو ایده زیر را اجرا کند: تابعی که یک یا چند تابع را به عنوان ورودی می گیرد یا یک تابع را به عنوان خروجی برمی گرداند. در PHP یک کلمه کلیدی وجود دارد که به وضوح نشان می دهد که یک تابع درجه بالاتر است: کلمه کلیدی callable. در حالی که این کلمه کلیدی لازم نیست وجود داشته باشد، کلمه کلیدی شناسایی آنها را آسان می کند. اگر تابع یا متدی را مشاهده کردید که دارای پارامتر قابل فراخوانی است، به این معنی است که تابعی را به عنوان ورودی می گیرد. یکی دیگر از نشانه های آسان این است که اگر می بینید یک تابع با استفاده از آن یک تابع را برمی گرداند return بیانیه. دستور بازگشت ممکن است فقط نام تابع باشد یا حتی می تواند یک تابع ناشناس/در خط باشد. در زیر چند نمونه از هر نوع آورده شده است.


function echoHelloWorld() {
  echo "Hello World!";
}


function higherOrderFunction(callable $func) {
  $func();
}




higherOrderFunction('echoHelloWorld'); 

در اینجا چند مثال ساده از توابع مرتبه بالاتر که یک تابع را برمی گرداند آورده شده است:


function trimMessage1() {
  return 'trim';
}


function trimMessage2() {
    return function($text) {
        return trim($text);
    };
}


$trim1 = trimMessage1(); 


echo $trim1('  hello world  ');


$trim2 = trimMessage1(); 


echo $trim2('  hello world  ');

همانطور که می توانید تصور کنید، می توانید یک تابع را دریافت کنید و همچنین از آن برای تولید تابع دیگری که برگردانده شده است استفاده کنید. ترفند بسیار شسته و رفته، درست است؟ اما چرا می خواهید هر یک از این کارها را انجام دهید؟ به نظر می رسد چیزی است که به سادگی همه چیز را پیچیده تر می کند.

چرا از یک تابع مرتبه بالاتر استفاده می کنید یا ایجاد می کنید؟

دلایل مختلفی وجود دارد که ممکن است بخواهید توابع درجه بالاتری در کد خود ایجاد کنید. همه چیز از انعطاف پذیری کد، استفاده مجدد از کد، پسوند کد یا تقلید از راه حل کدی که در برنامه دیگری دیده اید. در حالی که دلایل متعدد است، ما در اینجا به تعدادی از آنها خواهیم پرداخت.

افزودن انعطاف پذیری کد

عملکردهای درجه بالاتر انعطاف پذیری زیادی را اضافه می کنند. بر اساس مثال‌های قبلی، احتمالاً می‌توانید چند کاربرد برای چیزی شبیه به این ببینید. شما می توانید یک تابع بنویسید که مجموعه کاملی از توابع مختلف را در بر می گیرد و از آنها بدون نیاز به نوشتن کد برای اجرای جداگانه استفاده می کند.

شاید خود تابع مرتبه بالاتر نداند چه نوع تابعی را دریافت خواهد کرد. چه کسی گفته است که تابع باید چیزی در مورد عملکردی که دارد بداند؟ یک دقیقه تابع ورودی می تواند یک باشد add() تابع، و در بعدی می تواند یک باشد divide() عملکرد. در هر صورت فقط کار می کند.

به راحتی کد خود را گسترش دهید

این عملکرد همچنین اضافه کردن توابع ورودی اضافی را در آینده آسان تر می کند. بیایید بگوییم شما دارید add() و divide()، اما در ادامه مسیر باید یک را اضافه کنید sum() عملکرد. می توانید بنویسید sum() عملکرد و آن را از طریق تابع درجه بالاتر بدون نیاز به تغییر آن لوله کنید. در مثال بعدی، نشان خواهیم داد که چگونه می توانیم از این قابلیت برای ایجاد عملکرد خود استفاده کنیم calc() عملکرد.

از ویژگی های زبان دیگر تقلید کنید

دلیل دیگری که ممکن است بخواهید از یک تابع درجه بالاتر در PHP استفاده کنید، شبیه سازی رفتار چیزی مانند دکوراتور در پایتون است. شما یک تابع را در داخل یک تابع دیگر “پیچید” می کنید تا نحوه رفتار آن تابع را تغییر دهید. به عنوان مثال، می توانید تابعی بنویسید که بر توابع دیگر ضرب شود. شما یک تابع مرتبه بالاتر می نویسید که تابعی را دریافت می کند، تایمر را شروع می کند، تابع را فراخوانی می کند، سپس تایمر را پایان می دهد تا ببیند چقدر زمان سپری شده است.

نمونه هایی از توابع مرتبه بالاتر موجود در PHP

پیدا کردن نمونه هایی از توابع درجه بالاتر در PHP بسیار ساده است. نگاه کن به مستندات PHP و تابع/روشی را پیدا کنید که a را بگیرد callable پارامتر ورودی و شما یکی را پیدا کرده اید. در زیر چند نمونه از توابع مرتبه بالاتر معمولاً مورد استفاده قرار می گیرند. حتی ممکن است از آنها استفاده کرده باشید بدون اینکه بدانید آنها توابع درجه بالاتری هستند.

دوتایی پویا درجه بالاتر: array_map و array_filter

$arrayOfNums = [1,2,3,4,5];


$doubledNums = array_map(function($num) {
  return $num * 2;
}, $arrayOfNums);

var_dump($doubledNums); 

بیایید ببینیم که چگونه از یکی دیگر استفاده کنیم، این بار با یک تابع فیلتر که به طور جداگانه تعریف کرده ایم:

$arrayOfNums = [1,2,3,4,5];


function isEven($num) {
  return ($num % 2) === 0;
}


$evenNums = array_filter($arrayOfNums, 'isEven');

var_dump($evenNums); 

array_filter و array_map دو تابع بسیار محبوب درجه بالاتر هستند که در بسیاری از پروژه های کد پیدا خواهید کرد. یکی دیگر از مواردی که ممکن است استفاده کنید این است call_user_func، که یک تابع و لیستی از آرگومان ها را می گیرد و آن تابع را فراخوانی می کند. این اساساً یک تابع سفارشی بالاتر است. تمام هدف آن فراخوانی توابع دیگری است که کاربر تعریف کرده است:

function printCustomMessage($message) {
  echo "$message";
}

call_user_func('printCustomMessage', 'Called custom message through the use of call_user_func!');

چگونه توابع مرتبه بالاتر خود را ایجاد کنید

ما نمونه‌های زیادی از نحوه عملکرد توابع درجه بالاتر در PHP نشان داده‌ایم، و قبلاً چند مورد سفارشی برای نشان دادن اهداف مختلف آنها ایجاد کرده‌ایم. اما اجازه دهید انعطاف پذیری را با عملکرد جدیدی که فراخوانی می کنیم، کمی بیشتر نشان دهیم calc(). این تابع دارای دو آرگومان و تابعی است که بر روی آن دو آرگومان عمل می کند تا نتیجه ای به ما بدهد:

function add($a, $b) {
  return $a + $b;
}

function subtract($a, $b) {
  return $a - $b;
}

function multiply($a, $b) {
  return $a * $b;
}


function calc($n1, $n2, $math_func) {
  return $math_func($n1, $n2);
}

$addedNums = calc(1, 5, 'add');
$subtractedNums = calc(1, 5, 'subtract');
$multipliedNums = calc(1, 5, 'multiply');


echo "Added numbers: $addedNumsn";


echo "Subtracted numbers: $subtractedNumsn";


echo "Multiplied numbers: $multipliedNumsn";

توجه داشته باشید که ما calc() تابع به طور کلی نوشته شده بود تا مجموعه ای از توابع دیگر را بگیرد و آنها را با پارامترها فراخوانی کند $n1 و $n2. در این مورد، تابع محاسباتی ما اهمیتی نمی‌دهد که تابعی که می‌گیرد چه می‌کند. فقط در امتداد پارامترها به تابع منتقل می شود و نتیجه را برمی گرداند.

اما یک دقیقه صبر کنید: رئیس ما فقط از ما خواسته است که یک تابع برای اضافه کردن دو رشته به هم در سیستم موجود خود اضافه کنیم. اما به هم پیوستن رشته ها عملیات ریاضی معمولی شما نیست. با این حال، با راه اندازی ما در اینجا، فقط باید الحاق رشته را اضافه کنیم و آن را لوله کنیم calc():

function addTwoStrings($a, $b) {
  return $a . " " . $b;
}


$concatenatedStrings = calc('Hello', 'World!', 'addTwoStrings');

در حالی که این مثال بسیار ساختگی است، نشان می دهد که چگونه می توانید از این مفهوم در پروژه های بزرگتر استفاده کنید. شاید تابع مرتبه بالاتر نحوه مدیریت رویدادها را تعیین کند. ممکن است، بر اساس رویدادی که راه اندازی شده است، بتوانید آن را از طریق یک تابع کنترل کننده که از آن عبور می کنید، مسیریابی کنید. امکانات بی پایان هستند.

این در مقایسه با زبان های دیگر که عملکردهای مرتبه بالاتری دارند چگونه است؟ بیایید یک مثال ساده دکوراتور در پایتون بگیریم و آن را با جاوا اسکریپت و سپس با PHP مقایسه کنیم. به این ترتیب، اگر پایتون یا JS را می‌شناسید، می‌توانید معادل آن را ببینید.

مقایسه ای کنار هم با پایتون و جاوا اسکریپت

def say_message(func):
    def wrapper():
        print('Print before function is called')
        func()
        print('Print after function is called')
    return wrapper

def say_hello_world():
    print("Hello World!")




say_hello_world = say_message(say_hello_world)


say_hello_world()





حال بیایید ببینیم که چگونه می توان این کار را در توابع مرتبه بالاتر جاوا اسکریپت به دست آورد:


function say_message(func) {
  return function() {
      console.log("Print before function is called");
      func();
      console.log("Print after function is called");
  }
}

function say_hello_world() {
  console.log("Hello World!");
}



say_hello = say_message(say_hello_world);


say_hello();





حالا در آخر، بیایید این را با PHP مقایسه کنیم:


function say_message($func) {
  
  return function() use ($func) {
      echo "Print before function is called";
      $func();
      echo "Print after function is called";
  };
}

function say_hello_world() {
  echo "Hello World!";
}



$say_hello = say_message('say_hello_world');


$say_hello();





همانطور که از مقایسه کنار هم می بینید، نسخه PHP بسیار شبیه به نحو جاوا اسکریپت است. اما اگر کمی در مورد پایتون و دکوراتورها بدانید، می توانید ببینید که چگونه دکوراتورها ممکن است به یکی از دو زبان برنامه نویسی دیگر ترجمه شوند.

یک کلمه سریع در مورد لامبداس / توابع ناشناس

بسیاری اوقات، هنگامی که با توابع درجه بالاتر کار می کنید، ممکن است استفاده از توابع ناشناس درون خطی (معروف به lambdas) را مشاهده کنید. در برخی از نمونه هایی که در بالا دیدیم، من از این قالب استفاده کرده ام. علاوه بر آن، من همچنین از نسخه صریح تر تعریف تابع و سپس انتقال آن به تابع درجه بالاتر خود استفاده کرده ام. این برای این است که شما به دیدن هر دو نسخه عادت کنید. اغلب اوقات، نسخه لامبدای درون خطی را در چارچوب هایی خواهید دید که از توابع درجه بالاتر استفاده زیادی می کنند. اجازه ندهید این قالب شما را پرتاب کند. آنها فقط یک تابع ساده بدون نام ایجاد می کنند و از آن به جای جایی که شما نام تابع مستقل را داده اید استفاده می کنند.

نتیجه

در این مقاله، ما به تعریف اولیه از آنچه که یک تابع را به یک تابع درجه بالاتر تبدیل می کند، پرداختیم. هر تابعی که یک تابع را دریافت کند یا یک تابع (یا هر دو) را برمی گرداند، می تواند یک تابع درجه بالاتر در نظر گرفته شود. ما همچنین کمی در مورد اینکه چرا ممکن است بخواهید این نوع توابع را ایجاد کنید و مزایای آنها چیست صحبت کردیم.

سپس چند نمونه از توابع مرتبه بالاتر موجود در PHP را نشان دادیم، مانند array_map و array_filter، و سپس به ایجاد تابع مرتبه بالاتر خود به نام ادامه دادیم calc()، که چندین تابع ورودی را گرفت و بر روی چند آرگومان عمل کرد. سپس به مقایسه کنار هم رفتیم و نشان دادیم که راه حل PHP در مقایسه با دکوراتور پایتون و پیاده سازی جاوا اسکریپ چگونه به نظر می رسد.
امیدوارم در مورد توابع درجه بالاتر و اینکه چرا ممکن است بخواهید آنها را در راه حل بعدی خود در مقیاس بزرگ در نظر بگیرید، کمی بیشتر یاد گرفته باشید. آنها می توانند مزایای زیادی ارائه دهند و کد صفحه دیگ بخار را که ممکن است زائد باشد کاهش دهند.

اگر می خواهید در مورد برنامه نویسی تابعی در PHP اطلاعات بیشتری کسب کنید، می توانید با آموزش ما مشورت کنید.

با تشکر برای خواندن!