Programació funcional en PHP: Funcions d’ordre superior

Si examineu diversos marcs i aplicacions a gran escala, sens dubte veureu una funció d’ordre superior en algun moment. Molts idiomes admeten la idea de funcions d’ordre superior, com ara JavaScript, Java, .NET, Python i fins i tot PHP, per citar-ne alguns.

Però, què és una funció d’ordre superior i per què en voldríem utilitzar una? Quins avantatges ens aporta i podem utilitzar-lo per simplificar el nostre codi? En aquest article, parlarem de les funcions d’ordre superior en PHP específicament, però mostrarem com s’han utilitzat en altres idiomes per a la comparació.

Funcions de primera classe

Abans de poder entendre què és una funció d’ordre superior, primer hem d’entendre que hem de tenir un llenguatge que pugui suportar una característica anomenada funcions de primer ordre (també conegudes com funcions de primer ordre). Això vol dir que la llengua tracta les funcions com a ciutadans de primera classe. En altres paraules, el llenguatge tracta les funcions com les variables. Podeu emmagatzemar una funció en una variable, passar-la a altres funcions com a variable, retornar-les de funcions com variables i fins i tot emmagatzemar-les en estructures de dades com matrius o propietats d’objectes com podeu fer amb les variables. La majoria dels idiomes moderns tenen aquesta característica per defecte. Tot el que necessites saber és que es pot passar una funció i utilitzar-la de la mateixa manera que les variables.

Per als nostres propòsits, ens centrarem principalment en passar funcions com a arguments i en retornar funcions com a resultats, i tocarem breument el concepte de variables i tancaments no locals. (Podeu llegir més sobre aquests conceptes a les seccions 1.1, 1.4 i 1.3 de l’article de la Viquipèdia al qual es va enllaçar al paràgraf anterior.)

Què són les funcions d’ordre superior?

Hi ha dues característiques principals que identifiquen una funció d’ordre superior. Una funció d’ordre superior pot implementar només una o les dues idees següents: una funció que pren una o més funcions com a entrada o retorna una funció com a sortida. A PHP hi ha una paraula clau que indica clarament que una funció és d’ordre superior: la paraula clau callable. Tot i que aquesta paraula clau no ha d’estar present, la paraula clau facilita la seva identificació. Si veieu una funció o mètode que té un paràmetre invocable, vol dir que pren una funció com a entrada. Un altre signe fàcil és si veieu que una funció retorna una funció utilitzant-la return declaració. La declaració de retorn pot ser només el nom de la funció, o fins i tot una funció anònima/en línia. A continuació es mostren alguns exemples de cada tipus.


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


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




higherOrderFunction('echoHelloWorld'); 

Aquests són alguns exemples senzills de funcions d’ordre superior que retornen una funció:


function trimMessage1() {
  return 'trim';
}


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


$trim1 = trimMessage1(); 


echo $trim1('  hello world  ');


$trim2 = trimMessage1(); 


echo $trim2('  hello world  ');

Com us podeu imaginar, podeu incorporar una funció i també utilitzar-la per generar una altra funció que es retorna. Un truc molt bo, oi? Però per què voldríeu fer alguna cosa d’això? Sembla una cosa que simplement complicaria les coses.

Per què utilitzaríeu o crearíeu una funció d’ordre superior?

Hi ha diversos motius pels quals potser voldreu crear funcions d’ordre superior al vostre codi. Tot, des de la flexibilitat de codi, la reutilització de codi, l’extensió de codi o per imitar una solució de codi que veieu en un altre programa. Tot i que els motius són nombrosos, aquí en comentarem alguns.

Afegir flexibilitat de codi

Les funcions d’ordre superior afegeixen molta flexibilitat. A partir dels exemples anteriors, probablement podeu veure alguns usos d’alguna cosa com aquesta. Podeu escriure una única funció que incorpora tot un conjunt de funcions diferents i les utilitza sense haver d’escriure codi per executar-les individualment.

Potser la funció d’ordre superior en si mateixa no sap quin tipus de funció rebrà. Qui va dir que la funció ha de saber alguna cosa sobre la funció que està prenent? Un minut la funció d’entrada podria ser un add() funció, i en el següent podria ser a divide() funció. En qualsevol cas, només funciona.

Amplieu el vostre codi fàcilment

Aquesta funcionalitat també fa que sigui més fàcil afegir funcions d’entrada addicionals més endavant. Diguem que ho tens add() i divide()però més endavant cal afegir un sum() funció. Podeu escriure el sum() funció i canalitzar-la a través de la funció d’ordre superior sense haver de canviar-la mai. En un exemple posterior, demostrarem com podem utilitzar aquesta funcionalitat per crear la nostra calc() funció.

Imita les característiques d’una altra llengua

Una altra raó per la qual potser voldreu utilitzar una funció d’ordre superior en PHP és simular el comportament d’alguna cosa com un decorador a Python. “Embolcalleu” una funció dins d’una altra funció per modificar com es comporta aquesta funció. Per exemple, podeu escriure una funció que multiplica altres funcions. Escriu una funció d’ordre superior que recull una funció, inicia un temporitzador, truca a la funció i després acaba el temporitzador per veure quant de temps ha transcorregut.

Exemples de funcions d’ordre superior existents en PHP

És molt senzill trobar exemples de funcions d’ordre superior en PHP. Mireu la documentació de PHP i trobeu una funció/mètode que prengui a callable paràmetre d’entrada i n’heu trobat un. A continuació es mostren alguns exemples de funcions d’ordre superior d’ús habitual. És possible que fins i tot els hagis utilitzat sense saber mai que eren funcions d’ordre superior.

El duo dinàmic d’ordre superior: array_map i array_filter

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


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

var_dump($doubledNums); 

Vegem com utilitzar-ne un altre, aquesta vegada amb una funció de filtre que hem definit per separat:

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


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


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

var_dump($evenNums); 

array_filter i array_map són dues funcions d’ordre superior molt populars que trobareu en molts projectes de codi. Un altre que podeu utilitzar és call_user_func, que pren una funció i una llista d’arguments i fa una crida a aquesta funció. Aquesta és essencialment una funció personalitzada d’ordre superior. Tot el seu propòsit és cridar altres funcions que l’usuari hagi definit:

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

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

Com crear les vostres pròpies funcions d’ordre superior

Hem mostrat molts exemples de com funcionen les funcions d’ordre superior a PHP, i ja n’hem creat uns quants de personalitzats per demostrar els seus diferents propòsits. Però mostrem una mica més la flexibilitat amb una nova funció que anomenarem calc(). Aquesta funció tindrà dos arguments i una funció que operarà sobre aquests dos arguments per donar-nos un resultat:

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";

Fixeu-vos que el nostre calc() La funció es va escriure de manera molt genèrica per incorporar un conjunt d’altres funcions i les crida amb paràmetres $n1 i $n2. En aquest cas, a la nostra funció de càlcul no li importa què fa la funció que ocupa. Només passa els paràmetres a la funció i retorna el resultat.

Però espera un minut: el nostre cap només ens va demanar que afegim una funció per afegir dues cadenes juntes al nostre sistema existent. Però la concatenació de cadenes no és la vostra operació matemàtica típica. Tanmateix, amb la nostra configuració aquí, només hem d’afegir la concatenació de cadenes i canalitzar-la calc():

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


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

Tot i que aquest exemple és molt artificiós, demostra com podeu utilitzar el concepte en projectes més grans. Potser la funció d’ordre superior determina com es gestionen els esdeveniments. Potser, en funció de l’esdeveniment activat, el pugueu encaminar a través d’una funció de controlador que envieu. Les possibilitats són infinites.

Com es compara això amb altres idiomes que tenen funcions d’ordre superior? Prenguem un exemple senzill de decorador en Python i comparem-lo amb JavaScript i després comparem-lo amb PHP. D’aquesta manera, si coneixeu Python o JS, podreu veure com és equivalent.

Una comparació paral·lela amb Python i JavaScript

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()





Vegem ara com es pot aconseguir això a les funcions d’ordre superior de JavaScript:


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();





Ara, per últim, comparem això amb 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();





Com podeu veure a la comparació paral·lela, la versió PHP és molt semblant a la sintaxi de JavaScript. Però si coneixeu una mica sobre Python i els decoradors, podeu veure com els decoradors es poden traduir a un dels altres dos llenguatges de programació.

Una paraula ràpida sobre Lambdas/funcions anònimes

Moltes vegades, quan treballeu amb funcions d’ordre superior, és possible que vegeu l’ús de funcions anònimes en línia (també conegudes com lambdas). En alguns dels exemples que hem vist anteriorment, he utilitzat aquest format. A més d’això, també he utilitzat la versió més explícita de definir primer la funció i després passar-la a la nostra funció d’ordre superior. Això és perquè us acostumeu a veure les dues versions. Molt sovint, veureu la versió lambda en línia en marcs que fan un gran ús de funcions d’ordre superior. No deixis que aquest format et llence. Només estan creant una funció senzilla, sense nom, i l’utilitzen en lloc d’on hauríeu donat el nom de la funció autònoma.

Conclusió

En aquest article, hem cobert la definició bàsica del que fa que una funció sigui una funció d’ordre superior. Qualsevol funció que pren una funció o retorna una funció (o totes dues) es pot considerar una funció d’ordre superior. També hem parlat una mica de per què potser voldreu crear aquest tipus de funcions i quins són els seus avantatges.

A continuació, vam mostrar alguns exemples de funcions d’ordre superior existents en PHP, com ara array_map i array_filteri després vam crear la nostra pròpia funció d’ordre superior anomenada calc(), que va prendre diverses funcions d’entrada i funcionava amb un parell d’arguments. A continuació, vam fer una comparació paral·lela, mostrant com es veu la solució de PHP en comparació amb un decorador Python i una implementació de JavaScript.
Espero que hàgiu après una mica més sobre les funcions d’ordre superior i per què potser voldreu considerar-les a la vostra propera solució a gran escala. Poden oferir molts avantatges i reduir el codi boiler-plate que pot ser redundant.

Si voleu obtenir més informació sobre la programació funcional en PHP, podeu consultar el nostre tutorial.

Gràcies per llegir!

Leave a Comment

Your email address will not be published. Required fields are marked *