Функциональная композиция (информатика) - Function composition (computer science)

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

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

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

Грубо говоря, композиция функций применяется к функциям, которые работают с конечным объемом данных, причем каждый шаг последовательно обрабатывает их перед передачей следующему. Функции, которые работают с потенциально бесконечными данными (a транслировать или другой кодата ) известны как фильтры, а вместо этого связаны в трубопровод, который аналогичен композиции функций и может выполнять одновременно.

Составление вызовов функций

Например, предположим, что у нас есть два функции ж и грамм, как в z = ж(у) и у = грамм(Икс). Их составление означает, что мы сначала вычисляем у = грамм(Икс), а затем используйте у вычислить z = ж(у). Вот пример в Язык C:

плавать Икс, у, z;// ...у = грамм(Икс);z = ж(у);

Шаги можно объединить, если мы не дадим название промежуточному результату:

z = ж(грамм(Икс));

Несмотря на различия в длине, эти две реализации вычисляют один и тот же результат. Вторая реализация требует только одной строки кода и в просторечии называется «хорошо составленной» формой. Читаемость и, следовательно, ремонтопригодность - одно из преимуществ хорошо составленных форм, поскольку они требуют меньшего количества строк кода, что минимизирует «площадь поверхности» программы.[1] Демарко и Листер эмпирически подтверждают обратную зависимость между площадью поверхности и ремонтопригодностью.[2] С другой стороны, возможно чрезмерное использование сложных форм. Вложение слишком большого количества функций может иметь противоположный эффект, делая код менее удобным для сопровождения.

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

g f

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

Назовите состав функций

Теперь предположим, что комбинация вызова f () для результата g () часто бывает полезной и которую мы хотим назвать foo (), чтобы использовать ее как самостоятельную функцию.

В большинстве языков мы можем определить новую функцию, реализованную путем композиции. Пример в C:

плавать фу(плавать Икс) {    возвращаться ж(грамм(Икс));}

(полная форма с промежуточными продуктами также подойдет.) Пример в Четвертый:

  : foo g f;

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

#включают <stdio.h>typedef int FXN(int);int ж(int Икс) { возвращаться Икс+1; }int грамм(int Икс) { возвращаться Икс*2; }int час(int Икс) { возвращаться Икс-3; }int оценка(FXN *фс[], int размер, int Икс){   за (int я=0; я<размер; я++) Икс = (*фс[я])(Икс);   возвращаться Икс;}int главный(){   // ((6+1)*2)-3 = 11   FXN *обр[] = {ж,грамм,час};   printf("% d п", оценка(обр, 3, 6));   // ((6-3)*2)+1 = 7   обр[2] = ж;  обр[0] = час;   printf("% d п", оценка(обр, 3, 6));}

Первоклассный состав

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

Haskell

В Haskell, приведенный выше пример становится:

foo = f. грамм

используя встроенный оператор композиции (.), который можно читать как f после g или же g в составе с f.

Сам оператор композиции может быть определен в Haskell с помощью лямбда-выражение:

(.) :: (б -> c) -> (а -> б) -> а -> cж . грамм = \Икс -> ж (грамм Икс)

Первые строки описывают тип (.) - он принимает пару функций и возвращает функцию. Обратите внимание, что Haskell не требует указания точных типов ввода и вывода f и g, только отношения между ними (f должен принять то, что возвращает g). Это делает (.) полиморфный оператор.

Лисп

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

(определять (сочинять . фс)  (если (ноль? фс) (лямбда (Икс) Икс) ; если аргумент не указан, вычисляется функция идентификации      (лямбда (Икс) ((машина фс) ((подать заявление сочинять (CDR фс)) Икс))))); Примеры(определять (добавить-а-взрыва ул)  (добавление строки ул "!"))(определять дать  (сочинять строка-> символ добавить-а-взрыва символ-> строка))(дать 'набор) ; ===> установить!; анонимная композиция((сочинять sqrt отрицать квадрат) 5) ; ===> 0 + 5i

APL

Многие диалекты APL функция, встроенная в композицию функций с использованием символа . Эта функция высшего порядка расширяет функциональную композицию до диадический применение левой функции так, что A f∘g B является А ж ж Б.

фужграмм

Дополнительно вы можете определить состав функций:

о{⍺⍺ ⍵⍵ }

В диалекте, который не поддерживает встроенное определение с использованием фигурных скобок, доступно традиционное определение:

 р(ж о грамм)Икс  рж грамм Икс

Раку

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

мой &фу = &ж  &грамм;

Так же как Haskell вы можете определить оператора самостоятельно. Фактически, следующий код Raku используется для его определения в Ракудо выполнение.

# у реализации здесь немного другая строка, потому что она обманываетпрото суб инфикс: <∘> (& ?, &?) Эквивалентно (& [~]) соответствует  {*}мульти суб инфикс:<∘> () { *.себя } # разрешает `[∘] @ array` работать, когда` @ array` пустмульти суб инфикс: <∘> (& f) { &ж }   # позволяет `[∘] @ array` работать, когда` @ array` имеет один элементмульти суб инфикс: <∘> (& f, & g -> Заблокировать) {    (&ж).считать > 1    ?? -> |аргументы { ж |грамм |аргументы }    !! -> |аргументы { ж грамм |аргументы }}# используйте псевдоним "Техас" (все больше, и ASCII в Техасе)мой &инфикс:<o> := &инфикс:<∘>;

Python

В Python, способ определить состав для любой группы функций, использует уменьшать функция (используйте functools.reduce в Python 3):

# Доступно с Python v2.6из functools импорт уменьшатьdef сочинять(*функции) -> int:    "" "Составьте группу функций (f (g (h (...)))) в одну составную функцию." ""    возвращаться уменьшать(лямбда ж, грамм: лямбда Икс: ж(грамм(Икс)), функции)# Примерж = лямбда Икс: Икс + 1грамм = лямбда Икс: Икс * 2час = лямбда Икс: Икс - 3# Вызов функции x = 10: ((x-3) * 2) +1 = 15Распечатать(сочинять(ж, грамм, час)(10))

JavaScript

В JavaScript мы можем определить его как функцию, которая принимает две функции f и g и производит функцию:

функция о(ж, грамм) {    возвращаться функция(Икс) {        возвращаться ж(грамм(Икс));    }}// Как вариант, используя оператор rest и лямбда-выражения в ES2015const сочинять = (...фс) => (Икс) => фс.reduceRight((соотв, ж) => ж(соотв), Икс)

C #

В C # мы можем определить его как Func, который принимает две Func f и g и производит Func:

// Пример вызова:// var c = Compose (f, g);//// Func  g = _ => ...// Func  f = _ => ...Func<Банка, TOut> Сочинять<Банка, TMid, TOut>(Func<TMid, TOut> ж, Func<Банка, TMid> грамм) => _ => ж(грамм(_));

Рубин

Такие языки, как Рубин позвольте вам создать бинарный оператор самостоятельно:

учебный класс Proc  def сочинять(other_fn)    ->(*в качестве) { other_fn.вызов(вызов(*в качестве)) }  конец  alias_method :+, : composeконецж = ->(Икс) { Икс * 2 }грамм = ->(Икс) { Икс ** 3 }(ж + грамм).вызов(12) # => 13824

Однако в Ruby 2.6 был введен собственный оператор композиции функций:[3]

ж = proc{|Икс| Икс + 2}грамм = proc{|Икс| Икс * 3}(ж << грамм).вызов(3) # -> 11; идентично f (g (3))(ж >> грамм).вызов(3) # -> 15; идентично g (f (3))

Исследовательский опрос

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

Масштабная композиция

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

Императивные процедуры с побочными эффектами нарушают ссылочная прозрачность и поэтому не могут быть полностью составлены. Однако, если вы рассматриваете «состояние мира» до и после выполнения кода как его ввод и вывод, вы получите чистую функцию. Состав таких функций соответствует запуску процедур один за другим. В монады формализм использует эту идею для включения побочных эффектов и ввода-вывода в функциональные языки.

Смотрите также

Примечания

  1. ^ Кокс (1986), стр. 15–17
  2. ^ Демарко и Листер (1995) С. 133–135.
  3. ^ «Выпущен Ruby 2.6.0». www.ruby-lang.org. Получено 2019-01-04.
  4. ^ Раймонд (2003)

Рекомендации