Перейти к содержимому

Multiply c что это

  • автор:

Multiply c что это

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

Самым распространенным указателем на функцию является ее имя. С помощью имени функции мы можем вызывать ее и получать результат ее работы.

Но также указатель на функцию можно определять в виде отдельной переменной с помощью следующего синтаксиса:

тип (*имя_указателя) (типы_параметров);

Здесь тип представляет тип возвращаемого функцией значения.

имя_указателя представляет произвольно выбранный идентификатор в соответствии с правилами о наименовании переменных.

После названия указателя в скобках идут через запятую типы параметров. Если функция не принимает параметров, то указывается void .

Например, определим указатель на функцию:

void (*message) (void);

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

Применим этот указатель на функцию:

#include void hello() < printf("Hello, World \n"); >void goodbye() < printf("Good Bye, World \n"); >int main(void) < // определяем указатель на функцию void (*message) (void); message=hello; // указатель указывает на функцию hello message(); // вызываем функцию, на которую указыывет указатель message = goodbye; // указатель указывает на функцию goodbye message(); // вызываем функцию, на которую указыывет указатель return 0; >

Указателю на функцию можно присвоить функцию, которая соответствует указателю по возвращаемому типу и спецификации параметров:

message=hello;

То есть в данном случае указатель message теперь хранит адрес функции hello. И посредством обращения к указателю мы можем вызвать эту функцию:

message();

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

Hello, World Good Bye, World

При определении указателя стоит обратить внимание на скобки вокруг имени. Так, использованное выше определение

void (*message) (void);

НЕ будет аналогично следующему определению:

void *message (void);

Во втором случае определен не указатель на функцию, а прототип функции message, которая возвращает указатель типа void* .

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

Рассмотрим еще один указатель на функцию, которая возвращает значение типа int и принимает два параметра типа int :

#include int add(int x, int y) < return x + y; >int subtract(int x, int y) < return x - y; >int main(void) < int a = 10; int b = 5; int result; int (*operation)(int, int); operation=add; result = operation(a, b); printf("result = %d \n", result); // result=15 operation = subtract; result = operation(a, b); printf("result = %d \n", result); // result=5 return 0; >

Здесь определен указатель operation, который может указывать на функцию с двумя параметрами типа int , возвращающую также значение типа int . Соответственно мы можем присвоить указателю адреса функций add и subtract и вызвать их, передав при вызове в указатель нужные значения для параметров.

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

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

тип (*имя_массива[размер]) (параметры)
double (*actions[]) (int, int)

Здесь actions представляет массив указателей на функции, каждая из которых обязательно должна принимать два параметра типа int и возвращать значение типа double .

Посмотрим применение массива указателей на функции на примере:

#include void add(int x, int y) < printf("x + y = %d \n", x + y); >void subtract(int x, int y) < printf("x + y = %d \n", x - y); >void multiply(int x, int y) < printf("x * y = %d \n", x * y); >int main(void) < int a = 10; int b = 5; void (*operations[3])(int, int) = ; // получаем длину массива int length = sizeof(operations)/sizeof(operations[0]); for(int i=0; i < length; i++) < operations[i](a, b); // вызов функции по указателю >return 0; >

Здесь массив operations содержит три функции add, subtract и multiply, которые последовательно вызываются в цикле через перебор массива в функции main.

Функции в языке программирования C

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

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

#include // подключение заголовка с функцией rand rand() int randomNumber = rand(); // стандартная функция генерации случайных чисел

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

Рассмотрим общий формат для прототипа функций:

returnedDataType functionName ( dataType par1, . dataType parN );

где, returnedDataType — тип данных, возвращаемого функцией, значения;
functionName — имя функции
dataType — тип данных параметра функции, это тот же самый тип данных, что и при объявлении переменной
par1 . parN — параметры функции.

У функции может быть более одного параметра или вообще ни одного, в таком случае круглые скобки пустые. Функции, которые не возвращают значения имеют тип данных возвращаемого значения — void . Давайте посмотрим на прототип функции:

int mult ( int x, int y );

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

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

Давайте рассмотрим пример объявления и использования функции в языке программирования Си:

#include int multiplication( int num1, int num2 ); //прототип функции int main() < int num1; int num2; printf( "Введите два числа для умножения: " ); scanf( "%d", &num1 ); scanf( "%d", &num2 ); printf( "Результат умножения %d\n", multiplication( num1, num2 ) ); // вызов функции getchar(); return 0; >int multiplication(int num1, int num2) // определение функции

Эта программа начинается с включения единственного заголовочного файла, в строке 1. Следующей строкой является прототип функции умножения. Обратите внимание, что в конце объявления прототипа есть точка с запятой! Функция main возвращает целое число, в строке 16. Чтобы соответствовать стандарту функция main всегда должна возвращать некоторое значение. У вас не должно возникнуть проблем с пониманием ввода и вывода значений в функциях, если вы внимательно изучили предыдущие уроки.

Обратите внимание на то как на самом деле функция multiplication() принимает значение. Что же происходит на самом деле? А на самом деле это работает так: функция multiplication принимает два целых значения, умножает их и возвращает произведение. Результат работы этой программы будет точно таким же, как если бы мы сделали так:

printf( "Результат умножения %d\n", num1 * num2 );

Функция multiplication() на самом деле определяется ниже функции main . А так как прототип этой функции объявлен выше главной функции, то при вызове функции multiplication() внутри main() компилятор не выдаст ошибку. Пока прототип присутствует, функция может использоваться даже если нет её фактического определения. Тем не менее, вызов функции не может быть осуществлен ранее, чем будет определена эта функция.

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

Ключевое слово return , используется для того, чтобы заставить функцию возвращать значение. Обратите внимание на то, что вполне успешно можно объявлять функции, которые не возвращают никаких значений. Если функция возвращает значение типа void , значит фактически функция не имеет возвращаемого значения. Другими словами, для функции, которая возвращает значение типа void , утверждение return; является законным, но обычно оно избыточно. (Хотя оно может быть использовано для экстренного выхода из функции.)

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

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

P.S.: если вам нужен хороший сервер, то вы можете воспользоваться арендой серверов в Москве. Также вы можете воспользоваться другими услугами, предоставленными на сайте it-express.ru: развертывание отказоустойчивых серверов, системы хранения данных и др.

К сожалению, для данной темы пока нет подходящих задач. Если у вас есть таковые на примете, отправте их по адресу: admin@cppstudio.com. Мы их опубликуем!

Multiply c что это

Чтобы помочь вам приступить к написанию пользовательских функций для PTC Mathcad , имеется ряд примеров кода. См. файл MULTIPLY.C , который находится в подпапке Custom Functions/multiply папки установки PTC Mathcad . MULTIPLY.C содержит функцию с двумя аргументами, которая умножает скаляр на массив. Когда вы скомпилируете и правильно скомпонуете ее, новая функция multiply(a, M) станет доступной после перезапуска PTC Mathcad .

Подробное описание кода в MULTIPLY.C следует далее. Чтобы увидеть код полностью, откройте файл в Visual Studio или любом текстовом редакторе.

#include «mcadincl.h»

Ваша программа C должна включать файл заголовков mcadincl.h , находящийся в подпапке Custom Functions папки установки PTC Mathcad . Этот файл содержит структуры данных, которые позволяют выполнять следующие действия.

• Определять функции, которые может читать PTC Mathcad .
• Выделять и освобождать скаляры и массивы совместимым с PTC Mathcad способом.

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

Сообщения об ошибках

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

#define INTERRUPTED 1
#define INSUFFICIENT_MEMORY 2
#define MUST_BE_REAL 3
#define NUMBER_OF_ERRORS 3
// table of error messages
// if your function never returns an error — you do not need to create this table
char * myErrorMessageTable[NUMBER_OF_ERRORS] =
«interrupted»,.
«insufficient memory»,
«must be real»
>;

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

Далее следует код, который выполняет ваш алгоритм. Если вы преобразуете код из существующей библиотеки, необходимо привести его к типам COMPLEXARRAY , COMPLEXSCALAR и MCSTRING , передаваемым и ожидаемым PTC Mathcad .

Первый аргумент в алгоритме является указателем на возвращаемое значение, в данном случае Product . Остальные аргументы являются указателями на входные значения, поступающие из PTC Mathcad .

// this code executes the multiplication
LRESULT MultiplyRealArrayByRealScalar( COMPLEXARRAY * const Product,
LPCCOMPLEXSCALAR Scalar, LPCCOMPLEXARRAY Array )
unsigned int row, col;
// check that the scalar argument is real
if ( Scalar->imag != 0.0)
// if it is not, display «must be real» error message
// under the scalar argument ( the 1st argument )
return MAKELRESULT( MUST_BE_REAL, 1)

// check that the array argument is real
if ( Array->hImag != NULL )
// if it is not, display «must be real» error message
// under the array argument ( the 2nd argument )
return MAKELRESULT( MUST_BE_REAL, 2);
// allocate memory for the product
if( !MathcadArrayAllocate( Product, Array-rows,Array-cols,
TRUE, // allocate memory for the real part
FALSE )) // do not allocate memory for the imaginary part

// if allocation is not successful, return with the appropriate error code
return INSUFFICIENT_MEMORY;
// if all is well so far — go ahead and perform the multiplication
for ( col = 0; col < Product->cols; col++ )
// check that a user has not tried to interrupt the calculation
if (isUserInterrupted())
// if user has interrupted — free the allocated memory
MathcadArrayFree( Product );
// and return with an appropriate error code
return INTERRUPTED;
>
for ( row = 0; row < Product->rows; row++ )
Product->hReal[col][row] =
Scalar-> real*Array-> hReal[col][row];
>

// normal return
return 0;
>

Регистрация функции в PTC Mathcad

Заполните структуру FUNCTIONINFO информацией, необходимой для регистрации функции в PTC Mathcad . Эта структура определяет имя функции в PTC Mathcad , а не имя алгоритма MultiplyRealArrayByRealScalar . Кроме того, она определяет параметры и описание, а также указатель на используемый алгоритм.

FUNCTIONINFO multiply =
// name by which Mathcad will recognize the function
«multiply»,

// description of «multiply» parameters to be used
«a,M»,

// description of the function
«returns the product of real scalar a and real array M»,

// pointer to the executable code
// i.e. code that should be executed when a user types «multiply(a,M) wwID0EVABOB» >Выполните динамическую компоновку библиотеки

DLL обозначает библиотеку динамической компоновки. Следующий код делает эту библиотеку доступной для других функций Windows, в частности PTC Mathcad , через DLL-интерфейс Windows. Необходимо также указать ссылку на точку входа через программную среду. Когда DLL загружается, точка входа DLL вызывается операционной системой. PTC Mathcad требует, чтобы вы регистрировали свои пользовательские функции и свою таблицу сообщений об ошибках во время загрузки DLL.

// DLL entry point code
// the _CRT_INIT function is needed if you are using Microsoft’s 32-bit compiler

BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved);
BOOL WINAPI DllEntryPoint (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
switch (dwReason)
<
case DLL_PROCESS_ATTACH:
// DLL is attaching to the address space of the current process
if (!_CRT_INIT(hDLL, dwReason, lpReserved))
return FALSE;

Регистрация таблицы сообщений об ошибках и функции

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

if ( CreateUserErrorMessageTable( hDLL, NUMBER_OF_ERRORS, myErrorMessageTable ) )
// and if the errors register properly, register the user function
CreateUserFunction( hDLL, &multiply );
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
if (!_CRT_INIT(hDLL, dwReason, lpReserved))
return FALSE;
break;
>
return TRUE;
>
#undef INTERRUPTED
#undef INSUFFICIENT_MEMORY
#undef MUST_BE_REAL
#undef NUMBER_OF_ERRORS

Дополнительная информация

• Все значения, передаваемые в и из PTC Mathcad , являются комплексными, имеющими и действительную, и мнимую части в своей структуре (см. mcadincl.h ). Необходимо разделить действительную и мнимую части, если вы намереваетесь обрабатывать их независимо.

• Массивы индексируются сначала по столбцу, а затем по строке в отличие от порядка индексов в PTC Mathcad (сначала по строке, затем по столбцу).

• Все массивы предполагаются двумерными. Если нужно сослаться на вектор, задайте для первого индекса массива (столбец) значение 0.

Каррирование функций в JavaScript

Функциональное программирование — это такой стиль разработки программ, в котором широко используются некоторые специфические возможности по работе с функциями. Речь идёт, в частности, о передаче функций другим функциям в качестве аргументов и о возврате функций из других функций. К функциональному стилю программирования относится и понятие «чистые функции». Выходные данные чистых функций зависят только от входных, они, при выполнении, не влияют на состояние программы.

Принципы функционального программирования поддерживает множество языков. Среди них можно отметить JavaScript, Haskell, Clojure, Erlang. Использование механизмов функционального программирование подразумевает знание, кроме прочих, таких концепций, как чистые функции, каррирование функций, функции высшего порядка.

Материал, перевод которого мы сегодня публикуем, посвящён каррированию. Мы поговорим о том, как работает каррирование, и о том, как знание этого механизма может пригодиться JS-разработчику.

Что такое каррирование?

Каррирование или карринг (currying) в функциональном программирование — это преобразование функции с множеством аргументов в набор вложенных функций с одним аргументом. При вызове каррированной функции с передачей ей одного аргумента, она возвращает новую функцию, которая ожидает поступления следующего аргумента. Новые функции, ожидающие следующего аргумента, возвращаются при каждом вызове каррированной функции — до тех пор, пока функция не получит все необходимые ей аргументы. Ранее полученные аргументы, благодаря механизму замыканий, ждут того момента, когда функция получит всё, что ей нужно для выполнения вычислений. После получения последнего аргумента функция выполняет вычисления и возвращает результат.

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

Арность — это количество аргументов функции. Например — вот объявление пары функций:

function fn(a, b) < //. >function _fn(a, b, c) < //. >

Функция fn принимает два аргумента (это бинарная или 2-арная функция), функция _fn принимает три аргумента (тернарная, 3-арная функция).

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

Рассмотрим пример. У нас имеется следующая функция:

function multiply(a, b, c)

Она принимает три аргумента и возвращает их произведение:

multiply(1,2,3); // 6

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

function multiply(a) < return (b) => < return (c) => < return a * b * c >> > log(multiply(1)(2)(3)) // 6

Как видите, здесь мы преобразовали вызов единственной функции с тремя аргументами — multiply(1,2,3) к вызову трёх функций — multiply(1)(2)(3) .

Оказывается, что одна функция превратилась в несколько функций. При использовании новой конструкции каждая функция, кроме последней, возвращающей результат вычислений, принимает аргумент и возвращает другую функцию, также способную принять аргумент и возвратить другую функцию. Если конструкция вида multiply(1)(2)(3) кажется вам не слишком понятной, давайте, чтобы лучше в этом разобраться, распишем её в таком виде:

const mul1 = multiply(1); const mul2 = mul1(2); const result = mul2(3); log(result); // 6

Теперь построчно разберём то, что здесь происходит.

Сначала мы передаём аргумент 1 функции multiply :

const mul1 = multiply(1);

При работе этой функции срабатывает такая конструкция:

return (b) => < return (c) => < return a * b * c >>

Теперь в mul1 имеется ссылка на функцию, принимающую аргумент b . Вызовем функцию mul1 , передав ей 2 :

const mul2 = mul1(2);

В результате этого вызова выполнится следующий код:

return (c) =>

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

mul2 = (c) =>

Если теперь вызвать функцию mul2 , передав ей 3 , то функция выполнит необходимые вычисления, воспользовавшись аргументами a и b :

const result = mul2(3);

Результатом этих вычислений будет 6 :

log(result); // 6

Функция mul2 , обладающая самым большим уровнем вложенности, имеет доступ к областям видимости, к замыканиям, формируемым функциями multiply и mul1 . Именно поэтому в функции mul2 можно производить вычисления с переменными, объявленными в функциях, выполнение которых уже завершено, которые уже возвратили некие значения и обработаны сборщиком мусора.

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

function volume(l,w,h) < return l * w * h; >const vol = volume(100,20,90) // 180000

Вот как выглядит её каррированный вариант:

function volume(l) < return (w) => < return (h) => < return l * w * h >> > const vol = volume(100)(20)(90) // 180000

Итак, каррирование базируется на следующей идее: на основе некоей функции создают другую функцию, которая возвращает специализированную функцию.

Каррирование и частичное применение функций

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

Особый вариант функции для вычисления объёма, которую мы уже видели, можно сделать и таким:

function volume(l) < return (w, h) => < return l * w * h >>

Здесь применены идеи, очень похожие на рассмотренные выше. Пользоваться этой функцией можно так:

const hV = volume(70); hV(203,142); hV(220,122); hV(120,123);
volume(70)(90,30); volume(70)(390,320); volume(70)(940,340);

Фактически, здесь можно видеть как мы, командой volume(70) , создали специализированную функцию для вычисления объёма тел, одно из измерений которых (а именно — длина, l ), зафиксировано. Функция volume ожидает 3 аргумента и содержит 2 вложенных функции, в отличие от предыдущей версии подобной функции, каррированный вариант которой содержал 3 вложенных функции.

Та функция, которая получилась после вызова volume(70) реализует концепцию частичного применения функции (partial function application). Каррирование и частичное применение функций очень похожи друг на друга, но концепции это разные.

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

Например, имеется такая функция:

function acidityRatio(x, y, z)

Её можно преобразовать в такую:

function acidityRatio(x) < return (y,z) => < return performOp(x,y,z) >>

Реализация функции performOp() здесь не приводится, так как она на рассматриваемые концепции не влияет.

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

Каррированный вариант функции будет выглядеть так:

function acidityRatio(x) < return (y) = > < return (z) = > < return performOp(x,y,z) >> >

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

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

function div(x,y) < return x/y; >

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

function div(x) < return (y) => < return x/y; >>

Точно так же будет выглядеть и результат её каррирования.

О практическом применении концепций каррирования и частичного применения функций

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

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

function discount(price, discount)

Есть определённая категория клиентов, назовём их «любимыми клиентами», которой мы даём скидку в 10%. Например, если такой клиент покупает что-то на $500, мы даём ему скидку размером $50:

const price = discount(500,0.10); // $50 // $500 - $50 = $450

Несложно заметить, что нам, при таком подходе, постоянно придётся вызывать эту функцию с двумя аргументами:

const price = discount(1500,0.10); // $150 // $1,500 - $150 = $1,350 const price = discount(2000,0.10); // $200 // $2,000 - $200 = $1,800 const price = discount(50,0.10); // $5 // $50 - $5 = $45 const price = discount(5000,0.10); // $500 // $5,000 - $500 = $4,500 const price = discount(300,0.10); // $30 // $300 - $30 = $270

Исходную функцию можно привести к такому виду, который позволял бы получать новые функции с заранее заданным уровнем скидки, при вызове которых им достаточно передавать сумму покупки. Функция discount() в нашем примере имеет два аргумента. Вот как выглядит то, во что мы её преобразуем:

function discount(discount) < return (price) => < return price * discount; >> const tenPercentDiscount = discount(0.1);

Функция tenPercentDiscount() представляет собой результат частичного применения функции discount() . При вызове tenPercentDiscount() этой функции достаточно передать цену, а скидка в 10%, то есть — аргумент discount , уже будет задана:

tenPercentDiscount(500); // $50 // $500 - $50 = $450

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

const twentyPercentDiscount = discount(0.2);

Теперь функцию twentyPercentDiscount() можно вызывать для расчёта стоимости товаров с учётом скидки в 20%:

twentyPercentDiscount(500); // 100 // $500 - $100 = $400 twentyPercentDiscount(5000); // 1000 // $5,000 - $1,000 = $4,000 twentyPercentDiscount(1000000); // 200000 // $1,000,000 - $200,000 = $600,000

Универсальная функция для частичного применения других функций

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

function partial(fn, . args) < return (. _arg) => < return fn(. args, . _arg); >>

Функция partial() принимает функцию fn , которую мы хотим преобразовать в частично применённую функцию, и переменное число параметров (. args ). Оператор rest используется для того, чтобы поместить все параметры, идущие после fn , в args .

Эта функция возвращает другую функцию, которая так же принимает переменное число параметров ( _arg ). Эта функция, в свою очередь, вызывает исходную функцию fn , передавай ей параметры . args и . _arg (с использованием оператора spread ). Функция выполняет вычисления и возвращает результат.

Применим эту функцию для создания варианта уже знакомой вам функции volume , предназначенной для расчёта объёма прямоугольных параллелепипедов, одна из сторон которых зафиксирована:

function volume(l,h,w) < return l * h * w >const hV = partial(volume,100); hV(200,900); // 18000000 hV(70,60); // 420000

Здесь можно найти пример универсальной функции для каррирования других функций.

Итоги

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

Уважаемые читатели! Пользуетесь ли вы техниками каррирования и частичного применения функций в своих проектах?

  • Блог компании RUVDS.com
  • Веб-разработка
  • JavaScript

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *