Jump to content

Функциональный объект

(Перенаправлено из объектов Function )

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

Описание

[ редактировать ]

Типичное использование функционального объекта — написание функций обратного вызова . Обратный вызов в процедурных языках , таких как C , может выполняться с использованием указателей на функции . [2] Однако может быть сложно или неудобно передавать состояние в функцию обратного вызова или из нее. Это ограничение также препятствует более динамичному поведению функции. Функциональный объект решает эти проблемы, поскольку функция на самом деле является фасадом полноценного объекта, несущего свое собственное состояние.

Многие современные (и некоторые более старые) языки, например C++ , Eiffel , Groovy , Lisp , Smalltalk , Perl , PHP , Python , Ruby , Scala и многие другие, поддерживают первоклассные функциональные объекты и могут даже широко использовать их. [3] Языки функционального программирования дополнительно поддерживают замыкания , то есть первоклассные функции, которые могут «закрывать» переменные в окружающей среде во время создания. Во время компиляции преобразование, известное как лямбда-лифтинг, преобразует замыкания в функциональные объекты.

Рассмотрим пример процедуры сортировки, которая использует функцию обратного вызова для определения отношения порядка между парой элементов. Следующая программа C/C++ использует указатели на функции:

#include <stdlib.h>

/* qsort() callback function, returns < 0 if a < b, > 0 if a > b, 0 if a == b */
int compareInts(const void* a, const void* b)
{
    return ( *(int *)a - *(int *)b );
}
...
// prototype of qsort is
// void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
...
int main(void)
{
    int items[] = { 4, 3, 1, 2 };
    qsort(items, sizeof(items) / sizeof(items[0]), sizeof(items[0]), compareInts);
    return 0;
}

В C++ объект функции можно использовать вместо обычной функции, определив класс, который перегружает оператор вызова функции , определяя operator() функция-член. В C++ это может выглядеть следующим образом:

// comparator predicate: returns true if a < b, false otherwise
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());
    return 0;
}

Обратите внимание, что синтаксис предоставления обратного вызова std::sort() Функция идентична, но вместо указателя на функцию передается объект. При вызове функция обратного вызова выполняется так же, как и любая другая функция-член, и поэтому имеет полный доступ к другим членам (данным или функциям) объекта. Конечно, это всего лишь тривиальный пример. Чтобы понять, какую мощность функтор обеспечивает больше, чем обычная функция, рассмотрим распространенный вариант сортировки объектов по определенному полю. В следующем примере функтор используется для сортировки простой базы данных сотрудников по идентификационному номеру каждого сотрудника.

struct CompareBy
{
    const std::string SORT_FIELD;
    CompareBy(const std::string& sort_field="name")
      : SORT_FIELD(sort_field)
    {
        /* validate sort_field */
    }
    
    bool operator()(const Employee& a, const Employee& b) const
    {
        if (SORT_FIELD == "name")
            return a.name < b.name;
        else if (SORT_FIELD == "age")
            return a.age < b.age;
        else if (SORT_FIELD == "idnum")
            return a.idnum < b.idnum;
        else
            /* throw exception or something */
    }
};

int main()
{
    std::vector<Employee> emps;
    
    /* code to populate database */
    
    // Sort the database by employee ID number
    std::sort(emps.begin(), emps.end(), CompareBy("idnum"));
    
    return 0;
}

В C++11 лямбда-выражение обеспечивает более краткий способ сделать то же самое.

int main()
{
    std::vector<Employee> emps;
    /* code to populate database */
    const std::string sort_field = "idnum";
    std::sort(emps.begin(), emps.end(), [&sort_field](const Employee& a, const Employee& b) const { /* code to select and compare field */ });
    return 0;
}


Функциональные объекты можно использовать не только в качестве функций обратного вызова. В этом случае сокращенный термин- функтор обычно не используется для функционального объекта. Продолжая пример,

IntComparator cpm;
bool result = cpm(a, b);

Помимо функторов типа класса, в C++ возможны и другие виды функциональных объектов. C++ Они могут воспользоваться возможностями указателей-членов или шаблонов . Выразительность шаблонов позволяет функционального программирования использовать некоторые методы , такие как определение функциональных объектов в терминах других функциональных объектов (например, композиция функций ). Большая часть стандартной библиотеки шаблонов C++ (STL) интенсивно использует объекты функций на основе шаблонов.

Другой способ создать объект функции в C++ — определить функцию неявного преобразования в тип указателя функции, тип ссылки на функцию или ссылку на тип указателя функции. Предполагая, что преобразование не отбрасывает cv-квалификаторы , это позволяет использовать объект этого типа как функцию с той же сигнатурой , что и тип, в который он преобразуется. Изменив предыдущий пример для использования этого, мы получим следующий класс, экземпляры которого можно вызывать как указатели на функции: [4]

// comparator predicate: returns true if a < b, false otherwise
struct IntComparator
{
  static bool compare(const int &a, const int &b)
  {
    return a < b;
  }
  using T = decltype(compare);
  operator T*() const { return compare; }
};

int main()
{
    std::vector<int> items { 4, 3, 1, 2 };
    std::sort(items.begin(), items.end(), IntComparator());
    return 0;
}

Поддержание состояния

[ редактировать ]

Еще одним преимуществом функциональных объектов является их способность поддерживать состояние, влияющее на operator() между звонками. Например, следующий код определяет генератор , считающий от 10 и выше, который вызывается 11 раз.

#include <algorithm>
#include <iostream>
#include <iterator>

class CountFrom {
 public:
  CountFrom(int count) : count_(count) {}
  
  int operator()() { return count_++; }

 private:
  int count_;
};

int main() {
  const int state(10);
  std::generate_n(std::ostream_iterator<int>(std::cout, "\n"), 11,
                  CountFrom(state));
}

В C++14 или более поздних версиях приведенный выше пример можно переписать так:

#include <algorithm>
#include <iostream>
#include <iterator>

int main() {
  std::generate_n(std::ostream_iterator<int>(std::cout, "\n"), 11,
                  [count=10]() mutable { return count++; });
}

В C# объекты функций объявляются с помощью делегатов . Делегат можно объявить с помощью именованного метода или лямбда-выражения . Вот пример использования именованного метода.

using System;
using System.Collections.Generic;

public class ComparisonClass1
{
    public static int CompareFunction(int x, int y)
    {
        return x - y;
    }

    public static void Main()
    {
        var items = new List<int> { 4, 3, 1, 2 };
        Comparison<int> del = CompareFunction;
        items.Sort(del);
    }
}

Вот пример использования лямбда-выражения.

using System;
using System.Collections.Generic;

public class ComparisonClass2
{
    public static void Main()
    {
        var items = new List<int> { 4, 3, 1, 2 };
        items.Sort((x, y) => x - y);
    }
}

D предоставляет несколько способов объявления функциональных объектов: в стиле Lisp/Python через замыкания или в стиле C# через делегаты соответственно:

bool find(T)(T[] haystack, bool delegate(T) needle_test) {
  foreach (straw; haystack) {
    if (needle_test(straw))
      return true;
  }
  return false;
}

void main() {
    int[] haystack = [345, 15, 457, 9, 56, 123, 456];
    int   needle = 123;
    bool needleTest(int n) {
      return n == needle;
    }
    assert(find(haystack, &needleTest));
}

Разница между делегатом и замыканием в D автоматически и консервативно определяется компилятором. D также поддерживает функциональные литералы, которые допускают определение в стиле лямбда:

void main() {
    int[] haystack = [345, 15, 457, 9, 56, 123, 456];
    int   needle = 123;
    assert(find(haystack, (int n) { return n == needle; }));
}

Чтобы компилятор мог встроить код (см. выше), функциональные объекты также можно указать в стиле C++ посредством перегрузки операторов :

bool find(T, F)(T[] haystack, F needle_test) {
  foreach (straw; haystack) {
    if (needle_test(straw))
      return true;
  }
  return false;
}

void main() {
    int[] haystack = [345, 15, 457, 9, 56, 123, 456];
    int   needle = 123;
    class NeedleTest {
      int needle;
      this(int n) { needle = n; }
      bool opCall(int n) {
        return n == needle;
      }
    }
    assert(find(haystack, new NeedleTest(needle)));
}

В Эйфелевой

[ редактировать ]

В методе и языке разработки программного обеспечения Eiffel операции и объекты всегда рассматриваются как отдельные концепции. Однако механизм агента облегчает моделирование операций как объектов времени выполнения. Агенты удовлетворяют диапазону приложений, приписываемых функциональным объектам, например, передаются в качестве аргументов в процедурных вызовах или указываются как процедуры обратного вызова. Конструкция агентного механизма в Eiffel пытается отразить объектно-ориентированную природу метода и языка. Агент — это объект, который обычно является прямым экземпляром одного из двух библиотечных классов, моделирующих два типа подпрограмм в Eiffel: PROCEDURE и FUNCTION. Эти два класса происходят от более абстрактного ROUTINE.

В тексте программного обеспечения ключевое слово языка agent позволяет конструировать агентов в компактной форме. В следующем примере цель состоит в том, чтобы добавить действие по перемещению шкалы вперед в список действий, которые будут выполняться в случае нажатия кнопки.

my_button.select_actions.extend (agent my_gauge.step_forward)

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

В других классах библиотеки агенты используются для разных целей. Например, в библиотеке, поддерживающей структуры данных, класс, моделирующий линейные структуры, выполняет количественную оценку универсальности с помощью функции for_all типа BOOLEAN который принимает агент, экземпляр FUNCTION, как аргумент. Итак, в следующем примере my_action выполняется только в том случае, если все члены my_list содержать символ '!':

    my_list: LINKED_LIST [STRING]
        ...
            if my_list.for_all (agent {STRING}.has ('!')) then
                my_action
            end
        ...

При создании агентов аргументы подпрограмм, которые они моделируют, и даже целевой объект, к которому они применяются, можно либо закрыть , либо оставить открытым . Закрытые аргументы и цели получают значения во время создания агента. Присвоение значений открытым аргументам и целям откладывается до некоторого момента после создания агента. рутина for_all ожидает в качестве аргумента агент, представляющий функцию с одним открытым аргументом или целью, которая соответствует фактическому универсальному параметру для структуры ( STRING в этом примере.)

Когда цель агента остается открытой, имя класса ожидаемой цели, заключенное в фигурные скобки, заменяется ссылкой на объект, как показано в тексте. agent {STRING}.has ('!') в примере выше. Если аргумент остается открытым, символ вопросительного знака ('?') кодируется как заполнитель для открытого аргумента.

Возможность закрывать или оставлять открытыми цели и аргументы предназначена для повышения гибкости механизма агента. Рассмотрим класс, который содержит следующую процедуру для вывода строки в стандартный вывод после новой строки:

    print_on_new_line (s: STRING)
            -- Print `s' preceded by a new line
        do
            print ("%N" + s)
        end

Следующий фрагмент, предположительно принадлежащий тому же классу, использует print_on_new_line чтобы продемонстрировать смешивание открытых аргументов и открытых целей у агентов, используемых в качестве аргументов одной и той же процедуры.

    my_list: LINKED_LIST [STRING]
        ...
            my_list.do_all (agent print_on_new_line (?))
            my_list.do_all (agent {STRING}.to_lower)
            my_list.do_all (agent print_on_new_line (?))
        ...

В этом примере используется процедура do_all для линейных структур, который выполняет процедуру, смоделированную агентом для каждого элемента структуры.

Последовательность трех инструкций печатает строки в my_list, преобразует строки в нижний регистр, а затем снова печатает их.

Процедура do_all выполняет итерацию по структуре, выполняя подпрограмму, заменяя текущий элемент либо открытым аргументом (в случае агентов, основанных на print_on_new_line), или открытую цель (в случае агента на основе to_lower).

Открытые и закрытые аргументы и цели также позволяют использовать подпрограммы, которые вызывают больше аргументов, чем требуется, закрывая все аргументы, кроме необходимого:

my_list.do_all (agent my_multi_arg_procedure (closed_arg_1, ?, closed_arg_2, closed_arg_3)

Механизм агента Eiffel подробно описан в стандартном документе Eiffel ISO/ECMA .

В Java нет функций первого класса , поэтому функциональные объекты обычно выражаются интерфейсом с одним методом (чаще всего Callable интерфейс), обычно реализация которого представляет собой анонимный внутренний класс или, начиная с Java 8, лямбда-выражение .

Для примера из стандартной библиотеки Java: java.util.Collections.sort() берет List и функтор, роль которого заключается в сравнении объектов в списке. Без первоклассных функций функция является частью интерфейса Comparator. Это можно использовать следующим образом.

List<String> list = Arrays.asList("10", "1", "20", "11", "21", "12");
		
Comparator<String> numStringComparator = new Comparator<String>() {
    public int compare(String str1, String str2) {
        return Integer.valueOf(str1).compareTo(Integer.valueOf(str2));
    }
};

Collections.sort(list, numStringComparator);

В Java 8+ это можно записать так:

List<String> list = Arrays.asList("10", "1", "20", "11", "21", "12");
		
Comparator<String> numStringComparator = (str1, str2) -> Integer.valueOf(str1).compareTo(Integer.valueOf(str2));

Collections.sort(list, numStringComparator);

В JavaScript функции являются объектами первого класса. JavaScript также поддерживает замыкания.

Сравните следующий пример с последующим примером Python.

function Accumulator(start) {
  var current = start;
  return function (x) {
    return current += x;
  };
}

Пример использования:

var a = Accumulator(4);
var x = a(5);   // x has value 9
x = a(2);       // x has value 11

var b = Accumulator(42);
x = b(7);       // x has value 49 (current = 49 in closure b)
x = a(7);       // x has value 18 (current = 18 in closure a)

В Julia методы связаны с типами, поэтому можно сделать любой произвольный объект Julia «вызываемым», добавив методы к его типу. (Такие «вызываемые» объекты иногда называют «функторами».)

Примером может служить эта изменяемая структура аккумулятора (основанная на исследовании Пола Грэма синтаксиса и ясности языков программирования): [5]

julia> mutable struct Accumulator
           n::Int
       end

julia> function (acc::Accumulator)(n2)
           acc.n += n2
       end

julia> a = Accumulator(4)
Accumulator(4)

julia> a(5)
9

julia> a(2)
11

julia> b = Accumulator(42)
Accumulator(42)

julia> b(7)
49

Такой аккумулятор также можно реализовать с помощью замыкания:

julia> function Accumulator(n0)
           n = n0
           function(n2)
               n += n2
           end
       end
Accumulator (generic function with 1 method)

julia> a = Accumulator(4)
(::#1) (generic function with 1 method)

julia> a(5)
9

julia> a(2)
11

julia> b = Accumulator(42)
(::#1) (generic function with 1 method)

julia> b(7)
49

В Лиспе и Схеме

[ редактировать ]

В языках семейства Лисп, таких как Common Lisp , Scheme и других, функции являются объектами, точно так же, как строки, векторы, списки и числа. Оператор построения замыкания создает объект функции из части программы: часть кода, переданная в качестве аргумента оператору, является частью функции, как и лексическое окружение: привязки лексически видимых переменных фиксируются и хранится в объекте функции, который чаще называют замыканием . Захваченные привязки играют роль переменных-членов , а часть кода замыкания — роль анонимной функции-члена , точно так же, как оператор() в C++.

Конструктор замыкания имеет синтаксис (lambda (parameters ...) code ...). (parameters ...) часть позволяет объявить интерфейс, чтобы функция принимала объявленные параметры. code ... часть состоит из выражений, которые вычисляются при вызове функтора.

Многие варианты использования функторов в таких языках, как C++, представляют собой просто эмуляцию отсутствующего конструктора замыкания. Поскольку программист не может напрямую создать замыкание, он должен определить класс, который имеет все необходимые переменные состояния, а также функцию-член. Затем вместо этого создайте экземпляр этого класса, гарантируя, что все переменные-члены инициализируются через его конструктор. Значения извлекаются именно из тех локальных переменных, которые должны быть зафиксированы непосредственно замыканием.

Объект-функция, использующая систему классов Common Lisp, без использования замыканий:

(defclass counter ()
  ((value :initarg :value :accessor value-of)))

(defmethod functor-call ((c counter))
  (incf (value-of c)))

(defun make-counter (initial-value)
  (make-instance 'counter :value initial-value))

;;; use the counter:
(defvar *c* (make-counter 10))
(functor-call *c*) --> 11
(functor-call *c*) --> 12

Поскольку в Common Lisp не существует стандартного способа создания функционально вызываемых объектов, мы имитируем его, определив общую функцию под названием FUNCTOR-CALL. Это может быть специализировано для любого класса. Стандартная функция FUNCALL не является универсальной; он принимает только функциональные объекты.

Именно эта универсальная функция FUNCTOR-CALL дает нам функциональные объекты, которые представляют собой конструкцию компьютерного программирования, позволяющую вызывать или вызывать объект, как если бы это была обычная функция, обычно с тем же синтаксисом. Синтаксис у нас почти одинаковый: FUNCTOR-CALL вместо FUNCALL. Некоторые Lisp предоставляют функциональные объекты в качестве простого расширения. Сделать объекты вызываемыми с использованием того же синтаксиса, что и функции, — довольно тривиальная задача. Заставить оператор вызова функции работать с различными типами функций , будь то объекты классов или замыкания, не сложнее, чем создать оператор +, который работает с различными типами чисел, такими как целые, действительные или комплексные числа.

Теперь счетчик реализован с использованием замыкания. Это гораздо более кратко и прямо. Аргумент INITIAL-VALUE фабричной функции MAKE-COUNTER захватывается и используется напрямую. Его не обязательно копировать в какой-либо объект вспомогательного класса через конструктор. Это счетчик . Создаётся вспомогательный объект, но это происходит «за кулисами» .

(defun make-counter (value)
  (lambda () (incf value)))

;;; use the counter
(defvar *c* (make-counter 10))
(funcall *c*) ; --> 11
(funcall *c*) ; --> 12

Scheme делает замыкания еще проще, а код Scheme имеет тенденцию использовать такое программирование более высокого порядка несколько более идиоматично.

(define (make-counter value)
  (lambda () (set! value (+ value 1)) value))
;;; use the counter
(define c (make-counter 10))
(c) ; --> 11
(c) ; --> 12

В одной и той же лексической среде может быть создано более одного замыкания. Вектор замыканий, каждое из которых реализует определенный тип операции, может вполне точно имитировать объект, имеющий набор виртуальных операций. Этот тип объектно-ориентированного программирования с единой диспетчеризацией может быть полностью реализован с помощью замыканий.

Таким образом, существует своего рода туннель, вырытый с обеих сторон пресловутой горы. Программисты, использующие языки ООП, обнаруживают функциональные объекты, ограничивая объекты одной основной функцией, выполняющей функциональное назначение этого объекта, и даже исключают ее имя, чтобы оно выглядело так, как будто объект вызывается! Хотя программисты, использующие замыкания, не удивляются тому, что объект вызывается как функция, они обнаруживают, что несколько замыканий, использующих одну и ту же среду, могут предоставлять полный набор абстрактных операций, таких как виртуальная таблица для ООП с одним типом диспетчеризации .

В Objective-C объект функции можно создать из NSInvocation сорт. Для создания объекта функции требуется сигнатура метода, целевой объект и целевой селектор. Вот пример создания вызова текущего объекта. myMethod:

// Construct a function object
SEL sel = @selector(myMethod);
NSInvocation* inv = [NSInvocation invocationWithMethodSignature:
                     [self methodSignatureForSelector:sel]];
[inv setTarget:self];
[inv setSelector:sel];

// Do the actual invocation
[inv invoke];

Преимущество NSInvocation заключается в том, что целевой объект можно изменить после создания. Одиночный NSInvocation может быть создан и затем вызван для каждой из любого количества целей, например, из наблюдаемого объекта. Ан NSInvocation может быть создан только из протокола, но это не так просто. Смотрите здесь .

В Perl объект функции может быть создан либо из конструктора класса, возвращающего функцию, закрытую над данными экземпляра объекта, благословленными в класс:

package Acc1;
sub new {
    my $class = shift;
    my $arg = shift;
    my $obj = sub {
        my $num = shift;
        $arg += $num;
    };
    bless $obj, $class;
}
1;

или перегрузив &{} оператор, чтобы объект можно было использовать как функцию:

package Acc2;
use overload
    '&{}' =>
        sub {
            my $self = shift;
            sub {
                my $num = shift;
                $self->{arg} += $num;
            }
        };

sub new {
    my $class = shift;
    my $arg = shift;
    my $obj = { arg => $arg };
    bless $obj, $class;
}
1;

В обоих случаях объект функции можно использовать либо с помощью синтаксиса стрелки разыменования $ref->(@arguments) :

use Acc1;
my $a = Acc1->new(42);
print $a->(10), "\n";    # prints 52
print $a->(8), "\n";     # prints 60

или используя синтаксис разыменования coderef &$ref(@arguments) :

use Acc2;
my $a = Acc2->new(12);
print &$a(10), "\n";     # prints 22
print &$a(8), "\n";      # prints 30

PHP 5.3+ имеет первоклассные функции , которые можно использовать, например, в качестве параметра функции usort():

$a = array(3, 1, 4);
usort($a, function ($x, $y) { return $x - $y; });

PHP 5.3+ поддерживает также лямбда-функции и замыкания.

function Accumulator($start)
{
    $current = $start;
    return function($x) use(&$current)
    {
        return $current += $x;
    };
}

Пример использования:

$a = Accumulator(4);
$x = $a(5);
echo "x = $x<br/>";	// x = 9
$x = $a(2);
echo "x = $x<br/>";	// x = 11

В PHP 5.3+ также возможно сделать объекты вызываемыми, добавив в их класс магический метод __invoke(): [6]

class Minus
{
    public function __invoke($x, $y)
    {
        return $x - $y;
    }
}

$a = array(3, 1, 4);
usort($a, new Minus());

В языке Windows PowerShell блок сценария — это набор операторов или выражений, которые можно использовать как единое целое. Блок сценария может принимать аргументы и возвращаемые значения. Блок сценария — это экземпляр типа Microsoft .NET Framework System.Management.Automation.ScriptBlock.

Function 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:\> & $b 10
42

В Python функции являются объектами первого класса, такими же, как строки, числа, списки и т. д. Эта функция во многих случаях устраняет необходимость писать объект-функцию. Любой объект с __call__() метод можно вызвать с использованием синтаксиса вызова функции.

Примером может служить следующий класс аккумулятора (основанный на исследовании Пола Грэма синтаксиса и ясности языков программирования): [7]

class Accumulator:
    def __init__(self, n) -> None:
        self.n = n

    def __call__(self, x):
        self.n += x
        return self.n

Пример использования (с использованием интерактивного интерпретатора):

>>> a = Accumulator(4)
>>> a(5)
9
>>> a(2)
11
>>> b = Accumulator(42)
>>> b(7)
49

Поскольку функции являются объектами, они также могут быть определены локально с заданными атрибутами и возвращены другими функциями. [8] как показано в следующем примере:

def Accumulator(n):
    def inc(x):
        nonlocal n
        n += x
        return n
    return inc

В Рубине

[ редактировать ]

В Ruby несколько объектов можно рассматривать как функциональные объекты, в частности объекты Method и Proc. В Ruby также есть два типа объектов, которые можно рассматривать как полуфункциональные объекты: UnboundMethod и блок. UnboundMethods сначала должен быть привязан к объекту (таким образом становясь методом), прежде чем их можно будет использовать в качестве объекта функции. Блоки можно вызывать как функциональные объекты, но для использования в любом другом качестве объекта (например, передачи в качестве аргумента) их необходимо сначала преобразовать в Proc. Совсем недавно символы (доступ к которым осуществляется через буквальный унарный индикатор :) также можно преобразовать в Procс. Использование унарного алгоритма Ruby & оператор — эквивалент вызова to_proc на объекте и предполагая, что этот метод существует проект Ruby Extensions создал простой хак.

class Symbol
  def to_proc
    proc { |obj, *args| obj.send(self, *args) }
  end
end

Теперь метод foo может быть объектом функции, т.е. Proc, с помощью &:foo и используется через takes_a_functor(&:foo). Symbol.to_proc был официально добавлен в Ruby 11 июня 2006 года во время RubyKaigi2006. [1]

Из-за разнообразия форм термин «Функтор» обычно не используется в Ruby для обозначения объекта Function. Просто тип делегирования отправки , представленный проектом Ruby Facets , называется Functor. Самое основное определение которого таково:

class Functor
  def initialize(&func)
    @func = func
  end
  def method_missing(op, *args, &blk)
    @func.call(op, *args, &blk)
  end
end

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

Другие значения

[ редактировать ]

В более теоретическом контексте объектом функции можно считать любой экземпляр класса функций, особенно в таких языках, как Common Lisp, в которых функции являются объектами первого класса .

Семейство ML функциональных языков программирования использует термин «функтор» для обозначения отображения модулей в модули или типов в типы и представляет собой метод повторного использования кода. Функторы, используемые таким образом, аналогичны исходному математическому значению функтора в теории категорий или использованию общего программирования на C++, Java или Ada .

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

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

См. также

[ редактировать ]

Примечания

[ редактировать ]
  1. ^ В C++ функционалоид — это объект, имеющий один основной метод, а функтор — это частный случай функционалоида. [1] Они похожи на функциональный объект, но не совпадают .
  1. ^ В чем разница между функционалоидом и функтором?
  2. ^ Силан Лю. «Урок C++, часть I. Основы: 5.10. Указатели на функции в основном используются для реализации техники обратного вызова, которая будет обсуждаться сразу после» . TRIPOD: Учебные пособия по программированию © Silan Liu, 2002 . Проверено 7 сентября 2012 г. Указатели на функции в основном используются для реализации техники обратного вызова, которая будет обсуждаться сразу после этого.
  3. ^ Павел Турлейский (2 октября 2009 г.). «Урок C++, часть I. Основы: 5.10. Указатели на функции в основном используются для реализации техники обратного вызова, которая будет обсуждаться сразу после» . Всего несколько строк . Проверено 7 сентября 2012 г. В PHP 5.3, наряду со многими другими функциями, появились замыкания. Итак, теперь мы наконец можем делать все те классные вещи, которые могут делать ребята из Ruby/Groovy/Scala/any_modern_language, верно? Ну, можем, но, наверное, не будем… И вот почему.
  4. ^ «Разрешение перегрузки§Вызов объекта класса» . cppreference.com .
  5. ^ Аккумулятор-генератор
  6. ^ Документация PHP по магическим методам
  7. ^ Аккумулятор-генератор
  8. ^ Справочное руководство Python — Определения функций

Дальнейшее чтение

[ редактировать ]
  • Дэвид Вандевурде и Николай М. Джосуттис (2006). Шаблоны C++: Полное руководство , ISBN   0-201-73484-2 : В частности, глава 22 посвящена функциональным объектам.
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: c0c8027830b51ba8bc241b466c243b84__1715696520
URL1:https://arc.ask3.ru/arc/aa/c0/84/c0c8027830b51ba8bc241b466c243b84.html
Заголовок, (Title) документа по адресу, URL1:
Function object - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)