В чем преимущество каррирования?

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

В качестве тривиального примера я использую функцию, которая добавляет два значения (написано в ML). Версия без currying будет

fun add(x, y) = x + y

и будет называться

add(3, 5)

, в то время как версия с картой

fun add x y = x + y 
(* short for val add = fn x => fn y=> x + y *)

и будет называться

add 3 5

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

Является ли чуть более простой синтаксис единственной мотивацией для currying, или я пропустил некоторые другие преимущества, которые не очевидны на моем очень простом примере? Является ли currying просто синтаксическим сахаром?

144 голоса | спросил Mad Scientist 1 FebruaryEurope/MoscowbFri, 01 Feb 2013 23:36:02 +0400000000pmFri, 01 Feb 2013 23:36:02 +040013 2013, 23:36:02

15 ответов


123

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

add x y = x + y

и что вы хотите добавить 2 к каждому члену списка. В Haskell вы сделаете следующее:

map (add 2) [1, 2, 3] -- gives [3, 4, 5]
-- actually one could just do: map (2+) [1, 2, 3], but that may be Haskell specific

Здесь синтаксис легче, чем если бы вам нужно было создать функцию add2

add2 y = add 2 y
map add2 [1, 2, 3]

, или если вам нужно сделать анонимную функцию лямбда:

map (\y -> 2 + y) [1, 2, 3]

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

lookup1 :: [(Key, Value)] -> Key -> Value -- or perhaps it should be Maybe Value
lookup2 :: Map Key Value -> Key -> Value

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

myFunc :: (Key -> Value) -> .....

В заключение: currying хорош, поскольку он позволяет вам специализировать /частично применять функции с использованием легкого синтаксиса, а затем передавать эти частично прикладные функции в функции более высокого порядка, такие как map или filter. Функции более высокого порядка (которые выполняют функции в качестве параметров или дают их в качестве результатов) - это хлеб и масло функционального программирования, а currying и частично применяемые функции позволяют использовать функции более высокого порядка гораздо эффективнее и лаконично.

ответил Boris 2 FebruaryEurope/MoscowbSat, 02 Feb 2013 00:23:46 +0400000000amSat, 02 Feb 2013 00:23:46 +040013 2013, 00:23:46
52

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

map (add 1) [1..10]
map (\ x -> add 1 x) [1..10]

Если у вас есть уродливый синтаксис лямбда, это еще хуже. (Я смотрю на вас, JavaScript, Scheme и Python.)

Это становится все более полезным, поскольку вы используете все больше и больше функций более высокого порядка. Хотя я использую more функции более высокого порядка в Haskell, чем в других языках, я обнаружил, что на самом деле я использую синтаксис лямбда less , потому что что-то вроде двух третей времени, лямбда будет просто частично примененной функцией. (И большую часть другого времени я извлекаю его в именованную функцию.)

Более принципиально, не всегда очевидно, какая версия функции «каноническая». Например, возьмите map. Тип map можно записать двумя способами:

map :: (a -> b) -> [a] -> [b]
map :: (a -> b) -> ([a] -> [b])

Какой из них «правильный»? На самом деле это трудно сказать. На практике большинство языков используют первый вариант - карта принимает функцию и список и возвращает список. Однако, в принципе, что на самом деле делает карта, являются нормальные функции карты для перечисления функций - она ​​принимает функцию и возвращает функцию. Если карта карри, вам не нужно отвечать на этот вопрос: она делает обе очень элегантным способом.

Это становится особенно важным, если вы обобщите map для типов, отличных от списка.

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

Конечно, языки ML-стиля не имеют понятия нескольких аргументов в карри или в неопознанной форме. Синтаксис f(a, b, c) фактически соответствует передаче в кортеже (a, b, c) в f, поэтому f по-прежнему принимает только аргумент. Это действительно очень полезное различие, которое я желаю другим языкам, потому что это делает очень естественным написать что-то вроде:

f

Вы не могли бы легко сделать это с помощью языков, на которых есть идея нескольких аргументов, испеченных прямо в!

ответил Tikhon Jelvis 2 FebruaryEurope/MoscowbSat, 02 Feb 2013 00:31:43 +0400000000amSat, 02 Feb 2013 00:31:43 +040013 2013, 00:31:43
19

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

Код для этого будет проще, если вам нужно сначала собрать все параметры.

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

ответил psr 2 FebruaryEurope/MoscowbSat, 02 Feb 2013 00:07:16 +0400000000amSat, 02 Feb 2013 00:07:16 +040013 2013, 00:07:16
14

(Я приведу примеры в Haskell.)

  1. При использовании функциональных языков очень удобно, что вы можете частично применить функцию. Как в Haskell (== x) - это функция, которая возвращает True, если ее аргумент равен заданному термину x:

    mem :: Eq a => a -> [a] -> Bool
    mem x lst = any (== x) lst
    

    без currying, у нас будет несколько менее читаемый код:

    mem x lst = any (\y -> y == x) lst
    
  2. Это связано с неявным программированием (см. также Pointfree style на вики Haskell). Этот стиль фокусируется не на значениях, представленных переменными, а на составлении функций и способах передачи информации через цепочку функций. Мы можем преобразовать наш пример в форму, которая вообще не использует переменные:

    mem = any . (==)
    

    Здесь мы рассматриваем == как функцию от a до a -> Bool и any как функция из a -> Bool - [a] -> Bool. Просто составив их, мы получим результат. Это все благодаря карри.

  3. Обратное, не-каррирование также полезно в некоторых ситуациях. Например, предположим, что мы хотим разбить список на две части - элементы, которые меньше 10, а остальные, а затем объединить эти два списка. Разделение списка выполняется partition (< 10) (здесь мы также используем curried <). Результат имеет тип ([Int],[Int]). Вместо того, чтобы извлекать результат в свою первую и вторую часть и объединять их с помощью ++, мы можем сделать это непосредственно, распутывая ++ как

    uncurry (++) . partition (< 10)
    

Действительно, (uncurry (++) . partition (< 10)) [4,12,11,1] оценивается как [4,1,12,11]

Существуют также важные теоретические преимущества:

  1. Currying необходим для языков, которым не хватает данных, и имеет только функции, такие как лямбда-калькуляция . Хотя эти языки не полезны для практического использования, они очень важны с теоретической точки зрения.
  2. Это связано с существенным свойством функциональных языков - функции являются объектами первого класса. Как мы видели, преобразование из (a, b) -> c до a -> (b -> c) означает, что результат последней функции имеет тип b -> c. Другими словами, результатом является функция.
  3. (Un) currying тесно связан с декартовыми закрытыми категориями , что является категорическим способом просмотра типизированных lambda-исчислений.
ответил Petr Pudlák 2 FebruaryEurope/MoscowbSat, 02 Feb 2013 01:14:48 +0400000000amSat, 02 Feb 2013 01:14:48 +040013 2013, 01:14:48
13

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

ответил Alex R 2 FebruaryEurope/MoscowbSat, 02 Feb 2013 18:12:08 +0400000000pmSat, 02 Feb 2013 18:12:08 +040013 2013, 18:12:08
9

Каррирование - это не просто синтаксический сахар!

Рассмотрим сигнатуры типа add1 (uncurried) и add2 (curried):

add1 : (int * int) -> int
add2 : int -> (int -> int)

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

add1 - это функция, которая берет 2-кортеж int и int и возвращает int. add2 - это функция, которая принимает int и возвращает другую функцию , которая, в свою очередь, принимает int и возвращает Int.

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

int

Теперь мы можем видеть разницу между apply(f, b) = f b и add1 более четко. add2 вызывается с помощью 2-х кортежей:

add1

, но apply(add1, (3, 5)) вызывается с помощью add2 , а затем его возвращаемое значение вызывается с другим int :

int

РЕДАКТИРОВАТЬ: Существенное преимущество currying заключается в том, что вы получаете бесплатное бесплатное приложение. Допустим, вам нужна функция типа apply(apply(add2, 3), 5) (скажем, int -> int по списку), который добавил 5 к его параметру. Вы можете написать map, или вы могли бы сделать эквивалент с встроенной лямбда, но вы могли бы также намного легче (особенно в случаях, менее простых, чем этот) написать addFiveToParam x = x+5

ответил Ptharien's Flame 1 FebruaryEurope/MoscowbFri, 01 Feb 2013 23:51:49 +0400000000pmFri, 01 Feb 2013 23:51:49 +040013 2013, 23:51:49
9

Currying - это просто синтаксический сахар, но вы слегка недопонимаете, что делает сахар, я думаю. Взяв ваш пример,

fun add x y = x + y

- фактически синтаксический сахар для

fun add x = fn y => x + y

То есть (add x) возвращает функцию, которая принимает аргумент y и добавляет x к y.

fun addTuple (x, y) = x + y

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

Если вы хотите добавить 2 ко всем числам в списке:

(* add 2 to all numbers using the uncurried function *)
map (fn x => addTuple (x, 2)) [1,2,3]
(* using the curried function *)
map (add 2) [1,2,3]

Результатом будет [3,4,5].

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

(* Sum each tuple using the uncurried function *)
map addTuple [(10,2), (10,3), (10,4)]    
(* sum each tuple using curried function *)
map (fn (a,b) => add a b) [(10,2), (10,3), (10,4)]

Результатом будет [12,13,14].

Curried функции великолепны там, где полезно частичное приложение - например, map, fold, app, filter. Рассмотрим эту функцию, которая возвращает наибольшее положительное число в указанном списке, или 0, если нет положительных чисел:

- val highestPositive = foldr Int.max 0;   
val highestPositive = fn : int list -> int 
ответил gnud 2 FebruaryEurope/MoscowbSat, 02 Feb 2013 18:43:14 +0400000000pmSat, 02 Feb 2013 18:43:14 +040013 2013, 18:43:14
8

Еще одна вещь, о которой я еще не упоминал, - это то, что currying позволяет (ограниченную) абстракцию по arity.

Рассмотрим эти функции, которые являются частью библиотеки Haskell

(.) :: (b -> c) -> (a -> b) -> a -> c
either :: (a -> c) -> (b -> c) -> Either a b -> c
flip :: (a -> b -> c) -> b -> a -> c
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c

В каждом случае переменная типа c может быть типом функции, чтобы эти функции работали над некоторым префиксом списка параметров своего аргумента. Без currying вам понадобится специальная языковая функция для абстрактного над функцией arity или у вас будет много разных версий этих функций, специализированных для разных явлений.

ответил Geoff Reedy 3 FebruaryEurope/MoscowbSun, 03 Feb 2013 07:01:41 +0400000000amSun, 03 Feb 2013 07:01:41 +040013 2013, 07:01:41
5

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

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

Вместо (lambda (x) (+ 3 x)), который дает нам функцию, которая добавляет 3 к ее аргументу, вы можете написать что-то вроде (op + 3), и поэтому для добавления 3 к каждому элементу некоторого списка будет (mapcar (op + 3) some-list), а не (mapcar (lambda (x) (+ 3 x)) some-list). Этот макрос op сделает вам функцию, которая принимает некоторые аргументы x y z ... и вызывает (+ a x y z ...).

Во многих чисто функциональных языках частичное приложение укореняется в синтаксис, поэтому нет оператора op. Чтобы вызвать частичное приложение, вы просто вызываете функцию с меньшим количеством аргументов, чем требуется. Вместо того, чтобы генерировать ошибку "insufficient number of arguments", результат является функцией оставшихся аргументов.

ответил Kaz 2 FebruaryEurope/MoscowbSat, 02 Feb 2013 01:48:46 +0400000000amSat, 02 Feb 2013 01:48:46 +040013 2013, 01:48:46
5

Мое ограниченное понимание таково:

1) Применение частичной функции

Partial Function Application - это процесс возврата функции, которая принимает меньшее количество аргументов. Если вы предоставите 2 из 3 аргументов, он вернет функцию, которая принимает 3-2 = 1 аргумент. Если вы предоставите 1 из 3 аргументов, он вернет функцию, которая принимает 3-1 = 2 аргумента. Если бы вы захотели, вы могли бы даже частично применить 3 из 3 аргументов и вернуть функцию, не принимающую никаких аргументов.

Итак, задайте следующую функцию:

f(x,y,z) = x + y + z;

При привязке 1 к x и частично применяя это к указанной выше функции f(x,y,z), вы получите:

f(1,y,z) = f'(y,z);

Где: f'(y,z) = 1 + y + z;

Теперь, если вам нужно привязать y к 2 и z к 3 и частично применить f'(y,z), вы получите:

f'(2,3) = f''();

Где: f''() = 1 + 2 + 3;

Теперь в любой момент вы можете выбрать f, f' или f''. Поэтому я могу сделать:

print(f''()) // and it would return 6;

или

print(f'(1,1)) // and it would return 3;

2) Currying

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

Таким образом, для той же функции:

f(x,y,z) = x + y + z;

Если вы его заработали, вы получите цепочку из трех функций:

f'(x) -> f''(y) -> f'''(z)

Где:

f'(x) = x + f''(y);

f''(y) = y + f'''(z);

f'''(z) = z;

Теперь, если вы вызываете f'(x) с помощью x = 1:

f'(1) = 1 + f''(y);

Вам возвращается новая функция:

g(y) = 1 + f''(y);

Если вы вызываете g(y) с помощью y = 2:

g(2) = 1 + 2 + f'''(z);

Вам возвращается новая функция:

h(z) = 1 + 2 + f'''(z);

Наконец, если вы вызываете h(z) с помощью z = 3:

h(3) = 1 + 2 + 3;

Вам возвращается 6.

3) Закрытие

Наконец, Closure - это процесс захвата функции и данных вместе как единое целое. Закрытие функции может принимать от 0 до бесконечного числа аргументов, но также известно о не переданных ему данных.

Опять же, учитывая ту же функцию:

f(x,y,z) = x + y + z;

Вместо этого вы можете написать закрытие:

f(x) = x + f'(y, z);

Где:

f'(y,z) = x + y + z;

f' закрывается на x. Это означает, что f' может считывать значение x внутри f.

Итак, если вы должны были вызвать f с помощью x = 1:

f(1) = 1 + f'(y, z);

Вы получите закрытие:

closureOfF(y, z) =
                   var x = 1;
                   f'(y, z);

Теперь, если вы вызвали closureOfF с помощью y = 2 и z = 3:

closureOfF(2, 3) = 
                   var x = 1;
                   x + 2 + 3;

Что бы вернуть 6

Заключение

Каррирование, частичное применение и закрытие все несколько схожи, поскольку они разлагают функцию на несколько частей.

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

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

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

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

Раскрытие информации

Я никоим образом не специалист по этой теме, я только недавно начал изучать их, и поэтому я предоставляю свое нынешнее понимание, но у меня могут быть ошибки, которые я предлагаю вам указать, и я исправлю как /если я найду что-нибудь.

ответил Didier A. 16 AMpThu, 16 Apr 2015 08:44:29 +030044Thursday 2015, 08:44:29
4

Для функции

fun add(x, y) = x + y

Он имеет вид f': 'a * 'b -> 'c

Чтобы оценить один, выполните

add(3, 5)
val it = 8 : int

Для функции curried

fun add x y = x + y

Чтобы оценить один, выполните

add 3
val it = fn : int -> int

Если это частичное вычисление, в частности (3 + y), то тогда можно завершить вычисление с помощью

it 5
val it = 8 : int

add во втором случае имеет вид f: 'a -> 'b -> 'c

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

  

Зачем это нужно?

Скажите x в RHS не просто регулярный int, а вместо этого сложное вычисление, которое занимает некоторое время, для увеличения, ради, две секунды.

x = twoSecondsComputation(z)

Итак, теперь функция выглядит как

fun add (z:int) (y:int) : int =
    let
        val x = twoSecondsComputation(z)
    in
        x + y
    end;

Тип add : int * int -> int

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

val result1 = map (fn x => add (20, x)) [3, 5, 7];

В приведенном выше результате результат twoSecondsComputation оценивается каждый раз. Это означает, что для этого вычисления требуется 6 секунд.

Используя комбинацию постановки и каррирования, вы можете избежать этого.

fun add (z:int) : int -> int =
    let
        val x = twoSecondsComputation(z)
    in
        (fn y => x + y)
    end;

Из карновой формы add : int -> int -> int

Теперь можно сделать,

val add' = add 20;
val result2 = map add' [3, 5, 7, 11, 13];

twoSecondsComputation нужно только оценить один раз. Чтобы увеличить масштаб, замените две секунды на 15 минут или на любой час, а затем на карту на 100 номеров.

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

ответил phwd 2 FebruaryEurope/MoscowbSat, 02 Feb 2013 19:22:58 +0400000000pmSat, 02 Feb 2013 19:22:58 +040013 2013, 19:22:58
3

Currying позволяет гибкую функциональную композицию.

Я создал функцию «curry». В этом контексте мне все равно, какой регистратор я получаю или откуда он. Меня не волнует, что такое действие или откуда оно происходит. Все, о чем я забочусь, это обработка моего ввода.

var builder = curry(function(input, logger, action) {
     logger.log("Starting action");
     try {
         action(input);
         logger.log("Success!");
     }
     catch (err) {
         logger.logerror("Boo we failed..", err);
     }
});
var x = "My input.";
goGatherArgs(builder)(x); // Supplies action first, then logger somewhere.

Переменная builder - это функция, которая возвращает функцию, возвращающую функцию, которая принимает мои данные, которые выполняют мою работу. Это простой полезный пример, а не объект в поле зрения.

ответил mortalapeman 2 FebruaryEurope/MoscowbSat, 02 Feb 2013 00:53:51 +0400000000amSat, 02 Feb 2013 00:53:51 +040013 2013, 00:53:51
2

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

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

Например, при использовании функций, которые принимают функции в качестве аргументов, вы часто оказываетесь в ситуациях, когда вам нужны такие функции, как «добавление 3 к вводу» или «сравнение ввода с переменной v». С currying эти функции легко записываются: add 3 и (== v). Без currying вы должны использовать лямбда-выражения: x => add 3 x и x => x == v. Лямбда-выражения в два раза длиннее и имеют небольшую занятость, связанную с выбором имени, кроме x, если в области уже есть x.

Побочное преимущество языков, основанных на currying, заключается в том, что при написании общего кода для функций вы не получаете сотни вариантов, основанных на количестве параметров. Например, в C # для метода «карри» нужны варианты для Func <R & gt ;, Func <A, R & gt ;, Func <A1, A2, R & gt ;, Func <A1, A2, A3, R & gt ;, и так далее навсегда. В Haskell эквивалент Func <A1, A2, R> больше похож на Func <A1, A2 & gt ;, R> или Func <A1, Func <A2, R>> (и Func R более похож на Func <Unit, R>), поэтому все варианты соответствуют одному Func <A, R> случай.

ответил Craig Gidney 2 FebruaryEurope/MoscowbSat, 02 Feb 2013 02:39:45 +0400000000amSat, 02 Feb 2013 02:39:45 +040013 2013, 02:39:45
2

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

ответил Craig Gidney 2 FebruaryEurope/MoscowbSat, 02 Feb 2013 02:39:45 +0400000000amSat, 02 Feb 2013 02:39:45 +040013 2013, 02:39:45
0

Каррирование в решающей степени (окончательно четное) зависит от способности вернуть функцию.

Рассмотрим этот (надуманный) псевдокод.

var f = (m, x, b) => ... вернуть что-то ...

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

var g = f (0, 1); //это возвращает функцию, связанную с 0 и 1 (m и x), которая принимает еще один аргумент (b).

var y = g (42); //вызывать g с отсутствующим третьим аргументом, используя 0 и 1 для m и x

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

ответил Rick O'Shea 6 J000000Sunday14 2014, 20:27:23

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132