Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску

В компьютерном программировании , функциональный объект [а] представляет собой конструкцию , позволяя объект , который будет вызван или вызываемому , как если бы он был обычным функции , как правило , с таким же синтаксисом (функция параметра , который также может быть функцией). Функциональные объекты часто называют функторами .

Описание [ править ]

Типичное использование объекта функции - написание функций обратного вызова . Обратный вызов на процедурных языках , таких как 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. В последнее время символы (доступные через буквальный унарный индикатор :) также могут быть преобразованы в Procs. Использование унарного &оператора 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 этот термин используется в том же смысле, что и в теории категорий.

В Прологе и родственных ему языках функтор является синонимом символа функции .

См. Также [ править ]

  • Обратный звонок (информатика)
  • Закрытие (информатика)
  • Указатель на функцию
  • Функция высшего порядка
  • Шаблон команды
  • Каррирование

Заметки [ править ]

  1. ^ В C ++ функциональноид - это объект, у которого есть один основной метод, а функтор - это частный случай функциональноида. [1] Они похожи на функциональный объект, но не одинаковы .

Ссылки [ править ]

  1. ^ В чем разница между функционоидом и функтором?
  2. ^ Силан Лю. «Учебник по C ++, часть I - Базовая: 5.10 Указатели функций в основном используются для достижения техники обратного вызова, о которой мы поговорим сразу после» . ШТАТИВ: Учебники по программированию Copyright © Silan Liu 2002 . Проверено 7 сентября 2012 . Указатели на функции в основном используются для реализации техники обратного вызова, о которой мы поговорим сразу после.
  3. ^ Павел Турлейски (2009-10-02). «Учебник по C ++, часть I - Базовая: 5.10 Указатели функций в основном используются для достижения техники обратного вызова, которая будет обсуждаться сразу после» . Всего несколько строк . Проверено 7 сентября 2012 . PHP 5.3, наряду со многими другими функциями, представил закрытие. Итак, теперь мы, наконец, можем делать все классные вещи, которые могут делать ребята из Ruby / Groovy / Scala / any_modern_language, верно? Что ж, можем, но, вероятно, не будем… Вот почему.
  4. ^ Генератор аккумулятора
  5. ^ Документация PHP по магическим методам
  6. ^ Генератор аккумулятора
  7. ^ Справочное руководство по Python - Определения функций

Дальнейшее чтение [ править ]

  • Дэвид Вандевурде и Николай М. Йосаттис (2006). Шаблоны C ++: полное руководство , ISBN 0-201-73484-2 : В частности, глава 22 посвящена функциональным объектам. 

Внешние ссылки [ править ]

  • Описание из репозитория портлендских паттернов
  • Проблемы расширенного проектирования C ++ - Асинхронный C ++ , Кевлин Хенни
  • Учебные пособия по указателям на функции Ларса Хенделя (2000/2001)
  • Статья Херба Саттера " Обобщенные указатели функций "
  • Общие алгоритмы для Java
  • Функторы PHP - объекты функций в PHP
  • Что, черт возьми, такое функционоид, и зачем мне его использовать? (Часто задаваемые вопросы по C ++)