Преразглеждане на къри и частично приложение в PHP

частично
Неотдавнашна статия предложи внедряване наcurrying(къри) ичастично приложение(частично функционално приложение) в PHP. Неговият основен недостатък е, че резултатът от карирането не е функция, а обект. Вече не може да се предава като параметър за обратно извикване и трябва да използвате специален синтаксис за заместване на аргументи. Този текст предлага нова, прозрачна реализация на тези конструкции за PHP 5.3 и по-нови версии.

Терминъткъриидва от името на американския математик Хаскел Къри. Второто значение на думатакърие обработка на дъбена кожа.

Концепциите за къри и частично приложение идват от функционалните езици за програмиране, в рамките на които намират най-широко приложение. Съвременният PHP има тенденция да заема някои елементи от функционалното програмиране (функции като първокласни обекти, анонимни функции и затваряния), така че обсъжданите концепции вече не са напълно чужди за него.

Емулирането на къри и частично приложение в PHP е един пример за това, което McConnell в Perfect Code (глава 4.3) нарича програмиране наизползване наезика, а не навезика.

Кратка образователна програма

Карирането и частичното приложение се използват за изграждане на функционални фабрики. Тази техника е особено полезна, ако трябва да генерирате функция с даден интерфейс, която да предадете като аргумент на друга функция, за да извършите персонализирано филтриране, сортиране, трансформация и т.н. Да предположим, че имаме функция с много параметри и искаме да изградим масово функции, които съответстват на дадения, когато определени аргументи са фиксирани.

Например, да кажем, че имаме "черна кутия" - функциятаsolve(f,x0,ε), който решава уравнениетоf(x) = 0 в близост до началната точкаx0 с точностε. След това, чрез извикване на частично приложение, можем да конструираме функцията solve1(x0,ε) ≡ solve(x− tgx,x0,ε). Или дори функция solve2(ε), която би решила някакво фиксирано уравнение в близост до фиксирана начална точка с променлива точност.

Разбира се, във всеки отделен случай можем да напишем обвиваща функция като

но би било по-удобно да има универсален механизъм, който е частично приложение.

Curry е процедура, която трансформира функция отnпроменливи във верига отnфункции на една променлива чрез извършване на заместване на аргументи един по един. Например нека add(a,b) =a+bи curry_add е curry резултатът от функцията add. Тогава извикването на curry_add(a) за всяко a ще генерира функции от един аргумент, които добавятaкъм него, т.е. curry_add(a)(b) = add(a,b). Още примери ще бъдат дадени по-долу.

Повече подробности за карирането и частичното приложение можете да намерите в голямата статия на Е. Кирпичев "Елементи на функционалните езици" (раздел 5).

къри функция

Така че слайдове. Всичко, от което се нуждаем, е следният код, който замества оригиналната функция с нейната преработена версия.

Класически пример

Да кажем, че имаме дефинирана функция за добавяне.

Можем да създадем негова версия с къри.

Нека проверим дали получената функция се държи по същия начин като оригиналната.

Сега нека заместим само първия аргумент, за да генерираме функциите за увеличаване и намаляване.

Още примери

Можем съвсем прозрачно да заменимпроизволен брой първоначални аргументи и да получите пълноценна функция, в която не всички аргументи могат да бъдат заместени отново. Например,

Резултатът от карирането може лесно да бъде предаден като параметър за обратно извикване към друга функция. Да предположим например, че трябва да изчислим масите на кубовете от плътността и масива от дължини на страните. Можем да го направим по следния начин.

Бележки

  1. От гледна точка на PHP интерпретатора, резултатът от нашето къри не е функция, а обект от класа Closure, тъй като е изграден като анонимно затваряне. Въпреки това, от гледна точка на синтаксиса, заместването е напълно прозрачно.
  2. По очевидни причини функции с променлив брой аргументи като printf() не могат да бъдат карирани. В нашата реализация всички аргументи на функцията стават задължителни, дори ако са били маркирани като незадължителни в оригиналния подпис. Също така имайте предвид, че когато се опитвате да преброите броя на аргументите на функция с изкривяване, getNumberOfParameters() ще върне 0.
  3. Строго погледнато, функцията с къри трябва да приема аргументи един по един, т.е. вместо $add(2, 5) трябва да напишете $add(2)(5). Въпреки това текущата версия на PHP интерпретатора счита записи като func(arg1)(arg2) за синтактична грешка, дори ако са семантично правилни. Следователно, за удобство, нашата реализация ви позволява да посочите няколко аргумента наведнъж, разделени със запетаи, което го доближава до частичното приложение.

Hardcore conf в C++. Каним само професионалисти.