Эта статья требует дополнительных ссылок для проверки . ( февраль 2009 г. ) ( Узнайте, как и когда удалить это сообщение-шаблон ) |
В компьютерном программировании , функциональный объект [а] представляет собой конструкцию , позволяя объект , который будет вызван или вызываемому , как если бы он был обычным функции , как правило , с таким же синтаксисом (функция параметра , который также может быть функцией). Функциональные объекты часто называют функторами .
Описание [ править ]
Типичное использование объекта функции - написание функций обратного вызова . Обратный вызов на процедурных языках , таких как C , может выполняться с помощью указателей на функции . [2] Однако может быть сложно или неудобно передавать состояние в функцию обратного вызова или из нее. Это ограничение также препятствует более динамичному поведению функции. Функциональный объект решает эти проблемы, поскольку функция на самом деле является фасадом для полного объекта, несущего собственное состояние.
Многие современные (и некоторые более старые) языки, например C ++ , Eiffel , Groovy , Lisp , Smalltalk , Perl , PHP , Python , Ruby , Scala и многие другие, поддерживают первоклассные функциональные объекты и могут даже значительно их использовать. [3] Функциональные языки программирования дополнительно поддерживают замыкания , то есть первоклассные функции, которые могут «закрывать» переменные в окружающей среде во время создания. Во время компиляции преобразование, известное как лямбда-лифтинг преобразует замыкания в функциональные объекты.
В C и C ++ [ править ]
Рассмотрим пример процедуры сортировки, которая использует функцию обратного вызова для определения отношения упорядочения между парой элементов. Следующая программа на C использует указатели на функции:
#include <stdlib.h>/ * функция обратного вызова qsort (), возвращает <0, если a <b,> 0, если a> b, 0, если a == b * / int compareInts ( const void * a , const void * b ) { return ( * ( int * ) а - * ( int * ) б )); } ... // прототипом qsort является // void qsort (void * base, size_t nel, size_t width, int (* compare) (const void *, const void *)); ... int main ( void ) { int items [] = { 4 , 3 , 1 , 2 }; qsort ( элементы , sizeof ( элементы ) / sizeof ( элементы [ 0 ]), sizeof ( элементы [ 0 ]), compareInts ); возврат 0 ; }
В C ++, функциональный объект может быть использован вместо обычной функции, определив класс , который перегружает оператор вызова функции путем определения operator()
функции - члена. В C ++ это может выглядеть следующим образом:
// предикат компаратора: возвращает true, если a <b, иначе false struct IntComparator { bool operator () ( const int & a , const int & b ) const { return a < b ; } };int main () { std :: vector < int > items { 4 , 3 , 1 , 2 }; std :: sort ( items . begin (), items . end (), IntComparator ()); возврат 0 ; }
Обратите внимание, что синтаксис для обеспечения обратного вызова std::sort()
функции идентичен, но вместо указателя функции передается объект. При вызове функция обратного вызова выполняется так же, как и любая другая функция-член, и поэтому имеет полный доступ к другим членам (данным или функциям) объекта. Конечно, это просто банальный пример. Чтобы понять, какие возможности функтор предоставляет больше, чем обычная функция, рассмотрим общий вариант использования сортировки объектов по определенному полю. В следующем примере функтор используется для сортировки простой базы данных сотрудников по идентификатору каждого сотрудника.
struct CompareBy { const std :: string SORT_FIELD ; CompareBy ( const std :: string & sort_field = "name" ) : SORT_FIELD ( sort_field ) { / * проверка поля sort_field * / } bool operator () ( const Employee & a , const Employee & b ) { if ( SORT_FIELD == "name" ) вернуть a . имя < б . имя ; еще если ( SORT_FIELD == «возраст» ) вернуть . возраст < б . возраст ; иначе, если ( SORT_FIELD == "idnum" ) вернуть а . idnum < b . idnum ; else / * выбросить исключение или что-то в этом роде * / } };int main () { std :: vector < Сотрудник > emps ; / * код для заполнения базы данных * / // Сортировка базы данных по идентификационному номеру сотрудника STD :: сортировать ( эМп . Начать (), EMPS . Конец (), CompareBy ( "idnum" )); возврат 0 ; }
В C ++ 11 лямбда-выражение предоставляет более лаконичный способ сделать то же самое.
int main () { std :: vector < Сотрудник > emps ; / * код для заполнения базы данных * / const std :: string sort_field = "idnum" ; std :: sort ( emps . begin (), emps . end (), [ & sort_field ] ( const Employee & a , const Employee & b ) { / * код для выбора и сравнения поля * / }); возврат 0 ; }
Функциональные объекты можно использовать не только как функции обратного вызова, но и в других ситуациях. В этом случае сокращенный термин « функтор» обычно не используется в отношении функционального объекта. Продолжая пример,
IntComparator cpm ; bool result = cpm ( a , b );
В дополнение к функторам типов классов в C ++ возможны и другие виды функциональных объектов. Они могут воспользоваться функциями указателя на член или шаблонов C ++ . Выразительность шаблонов позволяет использовать некоторые методы функционального программирования , такие как определение функциональных объектов в терминах других функциональных объектов (например, композиция функций ). Большая часть стандартной библиотеки шаблонов (STL) C ++ активно использует функциональные объекты на основе шаблонов.
Поддержание состояния [ править ]
Еще одним преимуществом функциональных объектов является их способность поддерживать состояние, которое влияет operator()
между вызовами. Например, следующий код определяет генератор, отсчитывающий от 10 и более, который вызывается 11 раз.
#include <алгоритм>#include <iostream>#include <iterator>class CountFrom { public : CountFrom ( int count ) : count_ ( count ) {} int operator () () { return count_ ++ ; } частный : int count_ ; };int main () { const int состояние ( 10 ); std :: generate_n ( std :: ostream_iterator < int > ( std :: cout , " \ n " ), 11 , CountFrom ( состояние )); }
В C ++ 14 или новее приведенный выше пример можно переписать как:
#include <алгоритм>#include <iostream>#include <iterator>int main () { std :: generate_n ( std :: ostream_iterator < int > ( std :: cout , " \ n " ), 11 , [ count = 10 ] () mutable { return count ++ ; }); }
В C # [ править ]
В C # функциональные объекты объявляются через делегатов . Делегат может быть объявлен с использованием именованного метода или лямбда-выражения . Вот пример использования именованного метода.
используя Систему ; using System.Collections.Generic ;открытый класс ComparisonClass1 { общедоступный статический int CompareFunction ( int x , int y ) { return x - y ; } public static void Main () { var items = new List < int > { 4 , 3 , 1 , 2 }; Сравнение < int > del = CompareFunction ; предметы . Сортировка ( удаление ); } }
Вот пример использования лямбда-выражения.
используя Систему ; using System.Collections.Generic ;открытый класс ComparisonClass2 { public static void Main () { var items = new List < int > { 4 , 3 , 1 , 2 }; предметы . Сортировать (( x , y ) => x - y ); } }
In D [ править ]
D предоставляет несколько способов объявления объектов функций: в стиле Lisp / Python с помощью замыканий или в стиле C # с помощью делегатов , соответственно:
bool find ( T ) ( T [] haystack , bool delegate ( T ) Need_test ) { foreach ( солома ; стог сена ) { if ( Need_test ( солома )) return true ; } return false ; }void main () { int [] haystack = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; int Needle = 123 ; bool NeedleTest ( int n ) { return n == игла ; } assert ( найти ( стог сена , & NeedleTest )); }
Разница между делегатом и замыканием в D автоматически и консервативно определяется компилятором. D также поддерживает функциональные литералы, которые позволяют определять лямбда-стиль:
void main () { int [] haystack = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; int Needle = 123 ; assert ( найти ( стог сена , ( int n ) { return n == игла ; })); }
Чтобы компилятор мог встроить код (см. Выше), объекты функций также могут быть указаны в стиле C ++ с помощью перегрузки оператора :
bool find ( T , F ) ( T [] стог сена , F Need_test ) { foreach ( солома ; стог сена ) { if ( Need_test ( солома )) return true ; } return false ; }void main () { int [] haystack = [ 345 , 15 , 457 , 9 , 56 , 123 , 456 ]; int Needle = 123 ; Класс NeedleTest { INT иглы ; это ( int п ) { игла = п ; } bool opCall ( int n ) { return n == игла ; } } assert ( найти ( стог сена , новый NeedleTest ( игла ))); }
В Эйфеле [ править ]
В методе и языке разработки программного обеспечения Eiffel операции и объекты всегда рассматриваются как отдельные концепции. Однако механизм агента облегчает моделирование операций как объектов среды выполнения. Агенты удовлетворяют диапазону применения, приписываемому объектам функций, например, передаются в качестве аргументов в процедурных вызовах или указываются как процедуры обратного вызова. Дизайн механизма агента в Eiffel пытается отразить объектно-ориентированную природу метода и языка. Агент - это объект, который обычно является прямым экземпляром одного из двух библиотечных классов, которые моделируют два типа подпрограмм в Eiffel: PROCEDURE
и FUNCTION
. Эти два класса происходят от более абстрактных ROUTINE
.
В программном тексте ключевое слово language agent
позволяет создавать агенты в компактной форме. В следующем примере цель состоит в том, чтобы добавить действие по перемещению датчика вперед в список действий, которые будут выполняться в случае нажатия кнопки.
my_button . select_actions . расширить ( агент my_gauge . step_forward )
Подпрограмма, extend
указанная в приведенном выше примере, является функцией класса в библиотеке графического пользовательского интерфейса (GUI), обеспечивающей возможности программирования, управляемого событиями .
В других классах библиотеки агенты используются для разных целей. Например, в библиотеке, поддерживающей структуры данных, класс, моделирующий линейные структуры, осуществляет универсальную количественную оценку с функцией for_all
типа, BOOLEAN
которая принимает FUNCTION
в качестве аргумента агент, экземпляр объекта . Итак, в следующем примере my_action
выполняется, только если все члены my_list
содержат символ '!':
my_list : LINKED_LIST [ STRING ] ... если my_list . for_all ( агент { STRING }. имеет ( '!' )), затем my_action end ...
Когда агенты созданы, аргументы моделей, которые они моделируют, и даже целевой объект, к которому они применяются, могут быть либо закрыты, либо оставлены открытыми . Закрытым аргументам и целям присваиваются значения во время создания агента. Присвоение значений открытым аргументам и целям откладывается до некоторой точки после создания агента. Подпрограмма for_all
ожидает в качестве аргумента агента, представляющего функцию с одним открытым аргументом или целью, который соответствует фактическому универсальному параметру для структуры ( STRING
в этом примере).
Когда цель агента остается открытой, имя класса ожидаемой цели, заключенное в фигурные скобки, заменяется ссылкой на объект, как показано в тексте agent {STRING}.has ('!')
в примере выше. Когда аргумент остается открытым, знак вопроса ('?') Кодируется как заполнитель для открытого аргумента.
Возможность закрывать или оставлять открытыми цели и аргументы предназначена для повышения гибкости механизма агента. Рассмотрим класс, который содержит следующую процедуру для вывода строки на стандартный вывод после новой строки:
print_on_new_line ( s : STRING ) - Печать `s 'перед новой строкой do print ( "% N " + s ) end
Следующий фрагмент, предположительно принадлежащий к тому же классу, используется print_on_new_line
для демонстрации смешивания открытых аргументов и открытых целей в агентах, используемых в качестве аргументов одной и той же процедуры.
мой_лист : LINKED_LIST [ STRING ] ... мой_лист . do_all ( агент print_on_new_line ( ? )) my_list . do_all ( агент { STRING }. to_lower ) my_list . do_all ( агент print_on_new_line ( ? )) ...
В этом примере используется процедура do_all
для линейных структур, которая выполняет процедуру, смоделированную агентом, для каждого элемента в структуре.
Последовательность из трех инструкций печатает строки my_list
, преобразует их в нижний регистр, а затем печатает их снова.
Процедура do_all
выполняет итерацию по структуре, выполняя подпрограмму, заменяя текущий элемент либо на открытый аргумент (в случае агентов на основе print_on_new_line
), либо на открытую цель (в случае агента на основе to_lower
).
Открытые и закрытые аргументы и цели также позволяют использовать подпрограммы, которые вызывают больше аргументов, чем требуется, путем закрытия всех аргументов, кроме необходимого:
мой_лист . do_all ( агент my_multi_arg_procedure ( closed_arg_1 , ? , closed_arg_2 , closed_arg_3 )
Механизм агента Eiffel подробно описан в стандартном документе Eiffel ISO / ECMA .
В Java [ править ]
В Java нет функций первого класса , поэтому объекты функций обычно выражаются интерфейсом с помощью одного метода (чаще всего Callable
интерфейса), обычно реализацией которого является анонимный внутренний класс или, начиная с Java 8, лямбда .
В качестве примера из стандартной библиотеки Java java.util.Collections.sort()
принимает a List
и функтор, роль которого заключается в сравнении объектов в списке. Без функций первого класса функция является частью интерфейса Comparator. Это можно было бы использовать следующим образом.
Список < Строка > list = Массивы . asList ( «10» , «1» , «20» , «11» , «21» , «12» );Comparator < String > numStringComparator = new Comparator < String > () { public int compare ( String str1 , String str2 ) { return Integer . значениеOf ( str1 ). CompareTo ( Целое число . valueOf ( str2 )); } };Коллекции . sort ( список , numStringComparator );
В Java 8+ это можно записать как:
Список < Строка > list = Массивы . asList ( «10» , «1» , «20» , «11» , «21» , «12» );Компаратор < String > numStringComparator = ( str1 , str2 ) -> Integer . значениеOf ( str1 ). CompareTo ( Целое число . valueOf ( str2 ));Коллекции . sort ( список , numStringComparator );
В JavaScript [ править ]
В JavaScript функции - это объекты первого класса. JavaScript также поддерживает замыкания.
Сравните следующее с последующим примером Python.
функция Аккумулятор ( старт ) { var current = start ; return function ( x ) { вернуть текущий + = x ; }; }
Пример использования:
var a = Аккумулятор ( 4 ); var x = a ( 5 ); // x имеет значение 9 x = a ( 2 ); // x имеет значение 11var b = Накопитель ( 42 ); х = Ь ( 7 ); // x имеет значение 49 (current = 49 в замыкании b) x = a ( 7 ); // x имеет значение 18 (current = 18 в замыкании a)
В Юлии [ править ]
В Julia методы связаны с типами, поэтому любой произвольный объект Julia можно сделать «вызываемым», добавив методы к его типу. (Такие «вызываемые» объекты иногда называют «функторами».)
Примером может служить эта изменяемая структура аккумулятора (основанная на исследовании Пола Грэма о синтаксисе и ясности языка программирования): [4]
julia > изменяемая структура Accumulator n :: Int endjulia > function ( acc :: Accumulator ) ( n2 ) в соотв . n + = n2 конецjulia > a = Накопитель ( 4 ) Накопитель ( 4 )Юлия > а ( 5 ) 9Юлия > а ( 2 ) 11julia > b = Накопитель ( 42 ) Накопитель ( 42 )Юлия > B ( 7 ) 49
Такой аккумулятор также можно реализовать с помощью замыкания:
джулия > Функция Накопитель ( n0 ) п = n0 функция ( п2 ) п + = п2 конец конец Накопитель ( общая функция с 1 методом )julia > a = Accumulator ( 4 ) ( :: # 1) (общая функция с 1 методом)Юлия > а ( 5 ) 9Юлия > а ( 2 ) 11julia > b = Accumulator ( 42 ) ( :: # 1) (общая функция с 1 методом)Юлия > B ( 7 ) 49
В Лиспе и Схеме [ править ]
В языках семейства Lisp, таких как Common Lisp , Scheme и других, функции являются объектами, такими же, как строки, векторы, списки и числа. Оператор конструирования замыкания создает объект функции из части программы: часть кода, переданная в качестве аргумента оператору, является частью функции, как и лексическая среда: привязки лексически видимых переменных фиксируются и хранится в объекте функции, который чаще называют закрытием . Захваченные привязки играют роль переменных-членов , а часть кода замыкания играет роль анонимной функции-члена , как и operator () в C ++.
Конструктор закрытия имеет синтаксис (lambda (parameters ...) code ...)
. Эта (parameters ...)
часть позволяет объявить интерфейс, так что функция принимает объявленные параметры. code ...
Часть состоит из выражений, которые оцениваются , когда функтор называется.
Многие виды использования функторов в таких языках, как C ++, представляют собой просто эмуляцию отсутствующего конструктора замыкания. Поскольку программист не может напрямую построить замыкание, он должен определить класс, который имеет все необходимые переменные состояния, а также функцию-член. Затем вместо этого создайте экземпляр этого класса, убедившись, что все переменные-члены инициализированы через его конструктор. Значения берутся именно из тех локальных переменных, которые должны фиксироваться непосредственно замыканием.
Функциональный объект, использующий систему классов, без использования замыканий:
( defclass counter () (( значение : initarg : значение : значение доступа )))( defmethod functor-call (( c counter )) ( incf ( value-of c )))( defun make-counter ( начальное-значение ) ( make-instance 'counter : value initial-value ));;; используйте счетчик: ( defvar * c * ( make-counter 10 )) ( functor-call * c * ) -> 11 ( functor-call * c * ) -> 12
Поскольку в Лиспе нет стандартного способа создания функционально вызываемых объектов, мы подделываем его, определяя универсальную функцию под названием FUNCTOR-CALL. Это может быть специализировано для любого класса. Стандартная функция FUNCALL не является универсальной; он принимает только функциональные объекты.
Именно эта универсальная функция FUNCTOR-CALL дает нам объекты функций, которые представляют собой конструкцию компьютерного программирования, позволяющую вызывать или вызывать объект, как если бы это была обычная функция, обычно с тем же синтаксисом. У нас почти такой же синтаксис: FUNCTOR-CALL вместо FUNCALL. Некоторые Лиспы предоставляют функциональные объекты как простое расширение. Сделать объекты вызываемыми с использованием того же синтаксиса, что и функции, - довольно тривиальное дело. Заставить оператор вызова функции работать с разными типами функций , будь то объекты класса или замыкания, не сложнее, чем создание оператора +, который работает с разными типами чисел, такими как целые, действительные или комплексные числа.
Теперь счетчик реализован с помощью замыкания. Это гораздо более кратко и прямо. Аргумент INITIAL-VALUE фабричной функции MAKE-COUNTER фиксируется и используется напрямую. Его не нужно копировать в какой-то вспомогательный объект класса через конструктор. Это является счетчиком. Вспомогательный объект создается, но происходит это за кадром .
( defun make-counter ( значение ) ( lambda () ( incf value )));;; используйте счетчик ( defvar * c * ( make-counter 10 )) ( funcall * c * ) ; -> 11 ( funcall * c * ) ; -> 12
Scheme делает замыкания еще проще, а код Scheme имеет тенденцию использовать такое программирование более высокого порядка несколько идиоматично.
( define ( make-counter value ) ( lambda () ( set! value ( + value 1 )) value )) ;;; использовать счетчик ( определить c ( make-counter 10 )) ( c ) ; -> 11 ( в ) ; -> 12
В одной лексической среде может быть создано несколько замыканий. Вектор замыканий, каждое из которых реализует определенный тип операции, может вполне точно имитировать объект, имеющий набор виртуальных операций. Этот тип объектно-ориентированного программирования с однократной отправкой может быть выполнен полностью с помощью замыканий.
Таким образом, существует своего рода туннель, который вырывают с обеих сторон пресловутой горы. Программисты на языках ООП обнаруживают функциональные объекты, ограничивая объекты одной основной функцией для выполнения функционального назначения этого объекта, и даже удаляют его имя, чтобы оно выглядело так, как будто объект вызывается! Хотя программисты, использующие замыкания, не удивляются тому, что объект вызывается как функция, они обнаруживают, что несколько замыканий, использующих одну и ту же среду, могут предоставить полный набор абстрактных операций, таких как виртуальная таблица, для ООП с одним типом диспетчеризации .
В Objective-C [ править ]
В Objective-C объект функции может быть создан из NSInvocation
класса. Для создания функционального объекта требуется сигнатура метода, целевой объект и целевой селектор. Вот пример создания вызова текущего объекта myMethod
:
// Создание объекта функции SEL sel = @selector ( myMethod ); NSInvocation * inv = [ NSInvocation invocationWithMethodSignature : [ self methodSignatureForSelector : sel ]]; [ inv setTarget : self ]; [ inv setSelector : sel ];// Выполняем фактический вызов [ и Invoke ];
Преимущество NSInvocation
заключается в том, что целевой объект может быть изменен после создания. Один NSInvocation
может быть создан, а затем вызван для каждой из любого количества целей, например, из наблюдаемого объекта. Объект NSInvocation
может быть создан только на основе протокола, но это не так просто. Смотрите здесь .
На Perl [ править ]
В Perl объект функции может быть создан либо из конструктора класса, возвращающего функцию, закрытую над данными экземпляра объекта, благословленную в класс:
пакет Acc1 ; sub новый { мой $ class = shift ; мой $ arg = сдвиг ; мой $ obj = sub { мой $ num = shift ; $ arg + = $ num ; }; благослови $ obj , $ class ; } 1 ;
или путем перегрузки &{}
оператора, чтобы объект можно было использовать как функцию:
пакет Acc2 ; используйте перегрузку '& {}' => sub { my $ self = shift ; sub { my $ num = shift ; $ self -> { аргумент } + = $ num ; } };sub новый { мой $ class = shift ; мой $ arg = сдвиг ; мой $ obj = { arg => $ arg }; благослови $ obj , $ class ; } 1 ;
В обоих случаях объект функции можно использовать либо с помощью синтаксиса стрелки разыменования $ ref -> (@ arguments) :
используйте Acc1 ; мой $ a = Acc1 -> новый ( 42 ); напечатать $ a -> ( 10 ), "\ n" ; # выводит 52 print $ a -> ( 8 ), "\ n" ; # отпечатков 60
или используя синтаксис разыменования coderef & $ ref (@arguments) :
используйте Acc2 ; мой $ a = Acc2 -> новый ( 12 ); напечатать & $ a ( 10 ), "\ n" ; # выводит 22 print & $ a ( 8 ), "\ n" ; # отпечатков 30
В PHP [ править ]
В PHP 5.3+ есть первоклассные функции, которые можно использовать, например, в качестве параметра функции usort ():
$ a = массив ( 3 , 1 , 4 ); usort ( $ a , function ( $ x , $ y ) { return $ x - $ y ; });
PHP 5.3+ также поддерживает лямбда-функции и замыкания.
функция Аккумулятор ( $ start ) { $ current = $ start ; функция возврата ( $ x ) использование ( & $ current ) { return $ current + = $ x ; }; }
Пример использования:
$ a = Накопитель ( 4 ); $ x = $ a ( 5 ); echo "x = $ x <br/>" ; // x = 9 $ x = $ a ( 2 ); echo "x = $ x <br/>" ; // x = 11
В PHP 5.3+ также возможно сделать объекты вызываемыми, добавив к их классу волшебный метод __invoke (): [5]
class Minus { публичная функция __invoke ( $ x , $ y ) { return $ x - $ y ; } }$ a = массив ( 3 , 1 , 4 ); usort ( $ a , новый минус ());
В PowerShell [ править ]
В языке Windows PowerShell блок скрипта - это набор инструкций или выражений, которые можно использовать как единое целое. Блок скрипта может принимать аргументы и возвращаемые значения. Блок скрипта - это экземпляр Microsoft .NET Framework типа System.Management.Automation.ScriptBlock.
Функция Get-Accumulator ( $ x ) { { param ( $ y ) return $ x + = $ y }. GetNewClosure () }
PS C: \> $ a = Get-Accumulator 4 PS C: \> & $ a 5 9 PS C: \> & $ a 2 11 PS C: \> $ b = Get-Accumulator 32 PS C: \> & $ млрд 10 42
В Python [ править ]
В Python функции - это первоклассные объекты, такие как строки, числа, списки и т. Д. Эта функция во многих случаях избавляет от необходимости писать объект функции. Любой объект с __call__()
методом может быть вызван с использованием синтаксиса вызова функции.
Примером может служить этот класс аккумулятора (на основе исследования Пола Грэма о синтаксисе и ясности языка программирования): [6]
class Accumulator : def __init__ ( self , n ) -> None : self . п = п def __call__ ( self , x ): себя . n + = x вернуть себя . п
Пример этого в использовании (с использованием интерактивного интерпретатора):
>>> a = Накопитель ( 4 ) >>> a ( 5 ) 9 >>> a ( 2 ) 11 >>> b = Накопитель ( 42 ) >>> b ( 7 ) 49
Поскольку функции являются объектами, они также могут быть определены локально, с заданными атрибутами и возвращены другими функциями [7], как показано в следующем примере:
def Accumulator ( n ): def inc ( x ): нелокальный n n + = x return n return inc
В Ruby [ править ]
В Ruby несколько объектов могут считаться функциональными объектами, в частности, объектами Method и Proc. В Ruby также есть два типа объектов, которые можно рассматривать как полуфункциональные объекты: UnboundMethod и block. UnboundMethods должен быть сначала привязан к объекту (таким образом, становясь методом), прежде чем их можно будет использовать в качестве объекта функции. Блоки могут вызываться как функциональные объекты, но для использования в любом другом качестве в качестве объекта (например, переданного в качестве аргумента) они должны быть сначала преобразованы в Proc. В последнее время символы (доступные через буквальный унарный индикатор :
) также могут быть преобразованы в Proc
s. Использование унарного &
оператора Ruby - эквивалент вызова to_proc
объекта и при условии, что этот метод существует - проект расширений Ruby создал простой хак.
символ класса def to_proc proc { | obj , * args | OBJ . send ( self , * args ) } конец конец
Теперь метод foo
может быть функциональным объектом, то есть Proc
переходным отверстием &:foo
и использоваться через takes_a_functor(&:foo)
. Symbol.to_proc
был официально добавлен в Ruby 11 июня 2006 г. во время RubyKaigi2006. [1]
Из-за разнообразия форм термин Functor обычно не используется в Ruby для обозначения объекта Function. Просто тип диспетчерского делегирования, представленный проектом Ruby Facets, называется Functor. Самое основное определение этого:
class Functor def initialize ( & func ) @func = func end def method_missing ( op , * args , & blk ) @func . вызов ( op , * args , & blk ) конец конец
Это использование больше похоже на использование языков функционального программирования, таких как ML , и исходной математической терминологии.
Другие значения [ править ]
В более теоретическом контексте функциональный объект может рассматриваться как любой экземпляр класса функций, особенно в таких языках, как Common Lisp, в которых функции являются объектами первого класса .
В семействе языков функционального программирования ML термин функтор используется для обозначения отображения модулей на модули или от типов к типам и представляет собой метод повторного использования кода. Функторы, используемые таким образом, аналогичны исходному математическому значению функтора в теории категорий или использованию общего программирования в C ++, Java или Ada .
В Haskell этот термин используется в том же смысле, что и в теории категорий.
В Прологе и родственных ему языках функтор является синонимом символа функции .
См. Также [ править ]
- Обратный звонок (информатика)
- Закрытие (информатика)
- Указатель на функцию
- Функция высшего порядка
- Шаблон команды
- Каррирование
Заметки [ править ]
- ^ В C ++ функциональноид - это объект, у которого есть один основной метод, а функтор - это частный случай функциональноида. [1] Они похожи на функциональный объект, но не одинаковы .
Ссылки [ править ]
- ^ В чем разница между функционоидом и функтором?
- ^ Силан Лю. «Учебник по C ++, часть I - Базовая: 5.10 Указатели функций в основном используются для достижения техники обратного вызова, о которой мы поговорим сразу после» . ШТАТИВ: Учебники по программированию Copyright © Silan Liu 2002 . Проверено 7 сентября 2012 .
Указатели на функции в основном используются для реализации техники обратного вызова, о которой мы поговорим сразу после.
- ^ Павел Турлейски (2009-10-02). «Учебник по C ++, часть I - Базовая: 5.10 Указатели функций в основном используются для достижения техники обратного вызова, которая будет обсуждаться сразу после» . Всего несколько строк . Проверено 7 сентября 2012 .
PHP 5.3, наряду со многими другими функциями, представил закрытие.
Итак, теперь мы, наконец, можем делать все классные вещи, которые могут делать ребята из Ruby / Groovy / Scala / any_modern_language, верно?
Что ж, можем, но, вероятно, не будем… Вот почему.
- ^ Генератор аккумулятора
- ^ Документация PHP по магическим методам
- ^ Генератор аккумулятора
- ^ Справочное руководство по Python - Определения функций
Дальнейшее чтение [ править ]
- Дэвид Вандевурде и Николай М. Йосаттис (2006). Шаблоны C ++: полное руководство , ISBN 0-201-73484-2 : В частности, глава 22 посвящена функциональным объектам.
Внешние ссылки [ править ]
- Описание из репозитория портлендских паттернов
- Проблемы расширенного проектирования C ++ - Асинхронный C ++ , Кевлин Хенни
- Учебные пособия по указателям на функции Ларса Хенделя (2000/2001)
- Статья Херба Саттера " Обобщенные указатели функций "
- Общие алгоритмы для Java
- Функторы PHP - объекты функций в PHP
- Что, черт возьми, такое функционоид, и зачем мне его использовать? (Часто задаваемые вопросы по C ++)