Eu já vi referências a funções ao curry em vários artigos e blogs, mas não consigo encontrar uma boa explicação (ou pelo menos uma que faça sentido!)
add x y = x+y
(curry) é diferente add (x, y)=x+y
(uncurried)
Eu já vi referências a funções ao curry em vários artigos e blogs, mas não consigo encontrar uma boa explicação (ou pelo menos uma que faça sentido!)
add x y = x+y
(curry) é diferente add (x, y)=x+y
(uncurried)
Respostas:
Currying é quando você divide uma função que leva vários argumentos em uma série de funções, cada uma tendo apenas um argumento. Aqui está um exemplo em JavaScript:
function add (a, b) {
return a + b;
}
add(3, 4); // returns 7
Esta é uma função que recebe dois argumentos, aeb, e retorna sua soma. Vamos agora curry esta função:
function add (a) {
return function (b) {
return a + b;
}
}
Esta é uma função que recebe um argumento, ae retorna uma função que recebe outro argumento, b, e essa função retorna sua soma.
add(3)(4);
var add3 = add(3);
add3(4);
A primeira instrução retorna 7, como a instrução add (3, 4). A segunda instrução define uma nova função chamada add3 que adicionará 3 ao seu argumento. É o que algumas pessoas podem chamar de fechamento. A terceira instrução usa a operação add3 para adicionar 3 a 4, produzindo novamente 7 como resultado.
[1, 2, 3, 4, 5]
que deseja multiplicar por um número arbitrário. Em Haskell, eu posso escrever map (* 5) [1, 2, 3, 4, 5]
para multiplicar a lista inteira 5
e, assim, gerar a lista [5, 10, 15, 20, 25]
.
map
deve ser uma função que requer apenas 1 argumento - um elemento da lista. A multiplicação - como conceito matemático - é uma operação binária; são necessários 2 argumentos. No entanto, em Haskell *
é uma função ao curry, semelhante à segunda versão add
desta resposta. O resultado de (* 5)
é uma função que pega um único argumento e o multiplica por 5, e que nos permite usá-lo com o mapa.
Em uma álgebra de funções, lidar com funções que recebem vários argumentos (ou um argumento equivalente que é uma N-tupla) é um tanto deselegante - mas, como Moses Schönfinkel (e, independentemente, Haskell Curry) provou, não é necessário: tudo o que você precisa. necessidade são funções que levam um argumento.
Então, como você lida com algo que naturalmente expressa como, digamos f(x,y)
,? Bem, você considera isso como equivalente a f(x)(y)
- f(x)
, chame-o g
, é uma função e aplica essa função a y
. Em outras palavras, você só possui funções que usam um argumento - mas algumas dessas funções retornam outras funções (que também usam um argumento ;-).
Como de costume, a wikipedia tem uma boa entrada resumida sobre isso, com muitos indicadores úteis (provavelmente incluindo os que falam sobre seus idiomas favoritos ;-), além de um tratamento matemático um pouco mais rigoroso.
div :: Integral a => a -> a -> a
- observe essas múltiplas setas? "Mapear a para a função mapear de a para" é uma leitura ;-). Você pode usar um argumento de tupla (único) para o div
& c, mas isso seria realmente anti-idiomático em Haskell.
Aqui está um exemplo concreto:
Suponha que você tenha uma função que calcula a força gravitacional que atua sobre um objeto. Se você não conhece a fórmula, pode encontrá-la aqui . Esta função aceita os três parâmetros necessários como argumentos.
Agora, estando na Terra, você só deseja calcular forças para objetos neste planeta. Em uma linguagem funcional, você pode passar a massa da Terra para a função e depois avaliar parcialmente. O que você recuperaria é outra função que usa apenas dois argumentos e calcula a força gravitacional dos objetos na Terra. Isso é chamado de curry.
O curry é uma transformação que pode ser aplicada às funções para permitir que elas tomem um argumento a menos do que anteriormente.
Por exemplo, em F #, você pode definir uma função assim: -
let f x y z = x + y + z
Aqui a função f pega os parâmetros x, ye z e os soma juntos para que: -
f 1 2 3
Retorna 6.
De nossa definição, podemos, portanto, definir a função curry para f: -
let curry f = fun x -> f x
Onde 'fun x -> fx' é uma função lambda equivalente a x => f (x) em C #. Esta função insere a função que você deseja curry e retorna uma função que recebe um único argumento e retorna a função especificada com o primeiro argumento definido no argumento de entrada.
Usando nosso exemplo anterior, podemos obter um curry de f assim: -
let curryf = curry f
Podemos então fazer o seguinte: -
let f1 = curryf 1
O que nos fornece uma função f1 que é equivalente a f1 yz = 1 + y + z. Isso significa que podemos fazer o seguinte: -
f1 2 3
O que retorna 6.
Este processo é frequentemente confundido com 'aplicação parcial da função', que pode ser definida assim:
let papply f x = f x
Embora possamos estendê-lo a mais de um parâmetro, ou seja: -
let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.
Um aplicativo parcial pega a função e o (s) parâmetro (s) e retorna uma função que requer um ou mais parâmetros a menos, e como os dois exemplos anteriores mostram é implementado diretamente na definição da função F # padrão, para que pudéssemos obter o resultado anterior assim: -
let f1 = f 1
f1 2 3
O que retornará um resultado de 6.
Em conclusão:-
A diferença entre aplicação de função de curry e parcial é que: -
A currying pega uma função e fornece uma nova função, aceitando um único argumento e retornando a função especificada com seu primeiro argumento definido para esse argumento. Isso nos permite representar funções com vários parâmetros como uma série de funções de argumento único . Exemplo:-
let f x y z = x + y + z
let curryf = curry f
let f1 = curryf 1
let f2 = curryf 2
f1 2 3
6
f2 1 3
6
O aplicativo de função parcial é mais direto - ele pega uma função e um ou mais argumentos e retorna uma função com os primeiros n argumentos definidos para os n argumentos especificados. Exemplo:-
let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6
Pode ser uma maneira de usar funções para criar outras funções.
Em javascript:
let add = function(x){
return function(y){
return x + y
};
};
Nos permitiria chamá-lo assim:
let addTen = add(10);
Quando isso é executado, o 10
é passado como x
;
let add = function(10){
return function(y){
return 10 + y
};
};
o que significa que retornamos esta função:
function(y) { return 10 + y };
Então, quando você liga
addTen();
você está realmente ligando:
function(y) { return 10 + y };
Então, se você fizer isso:
addTen(4)
é o mesmo que:
function(4) { return 10 + 4} // 14
Portanto, nosso addTen()
sempre adiciona dez ao que quer que seja transmitido. Podemos fazer funções semelhantes da mesma maneira:
let addTwo = add(2) // addTwo(); will add two to whatever you pass in
let addSeventy = add(70) // ... and so on...
Agora, a pergunta óbvia de acompanhamento é por que diabos você gostaria de fazer isso? Ele transforma o que era uma operação ansiosa x + y
em uma que pode ser percorrida preguiçosamente, o que significa que podemos fazer pelo menos duas coisas: 1. operações caras em cache; 2. obter abstrações no paradigma funcional.
Imagine nossa função ao curry parecida com esta:
let doTheHardStuff = function(x) {
let z = doSomethingComputationallyExpensive(x)
return function (y){
z + y
}
}
Poderíamos chamar essa função uma vez e depois passar o resultado a ser usado em muitos lugares, o que significa que só fazemos o material computacionalmente caro uma vez:
let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)
Podemos obter abstrações de maneira semelhante.
Uma função ao curry é uma função de vários argumentos reescritos, de forma que aceita o primeiro argumento e retorna uma função que aceita o segundo argumento e assim por diante. Isso permite que funções de vários argumentos tenham alguns de seus argumentos iniciais parcialmente aplicados.
map
uma função f
em uma lista de listas, xss
poderá fazê-lo map (map f) xss
.
Aqui está um exemplo de brinquedo em Python:
>>> from functools import partial as curry
>>> # Original function taking three parameters:
>>> def display_quote(who, subject, quote):
print who, 'said regarding', subject + ':'
print '"' + quote + '"'
>>> display_quote("hoohoo", "functional languages",
"I like Erlang, not sure yet about Haskell.")
hoohoo said regarding functional languages:
"I like Erlang, not sure yet about Haskell."
>>> # Let's curry the function to get another that always quotes Alex...
>>> am_quote = curry(display_quote, "Alex Martelli")
>>> am_quote("currying", "As usual, wikipedia has a nice summary...")
Alex Martelli said regarding currying:
"As usual, wikipedia has a nice summary..."
(Basta usar a concatenação via + para evitar distrações para programadores que não são Python.)
Edição para adicionar:
Veja http://docs.python.org/library/functools.html?highlight=partial#functools.partial , que também mostra a distinção parcial de objeto versus função na maneira como o Python implementa isso.
Currying está traduzindo uma função de callable f(a, b, c)
para callable as f(a)(b)(c)
.
Caso contrário, o curry é quando você divide uma função que leva vários argumentos em uma série de funções que fazem parte dos argumentos.
Literalmente, currying é uma transformação de funções: de uma maneira de chamar para outra. Em JavaScript, geralmente criamos um wrapper para manter a função original.
O curry não chama uma função. Apenas o transforma.
Vamos criar a função curry que executa o curry para funções de dois argumentos. Em outras palavras, curry(f)
para dois argumentos o f(a, b)
traduz emf(a)(b)
function curry(f) { // curry(f) does the currying transform
return function(a) {
return function(b) {
return f(a, b);
};
};
}
// usage
function sum(a, b) {
return a + b;
}
let carriedSum = curry(sum);
alert( carriedSum(1)(2) ); // 3
Como você pode ver, a implementação é uma série de wrappers.
curry(func)
é um invólucro function(a)
.sum(1)
, o argumento é salvo no Ambiente Lexical e um novo wrapper é retornado function(b)
.sum(1)(2)
, finalmente, chama function(b)
fornecendo 2, e passa a chamada para a soma multi-argumento original.Se você entende partial
que está no meio do caminho. A idéia de partial
é aplicar previamente argumentos a uma função e devolver uma nova função que deseja apenas os argumentos restantes. Quando essa nova função é chamada, ela inclui os argumentos pré-carregados, juntamente com os argumentos que foram fornecidos a ela.
Em Clojure +
é uma função, mas para esclarecer as coisas:
(defn add [a b] (+ a b))
Você pode estar ciente de que a inc
função simplesmente adiciona 1 a qualquer número que seja passado.
(inc 7) # => 8
Vamos construí-lo usando partial
:
(def inc (partial add 1))
Aqui retornamos outra função que carregou 1 no primeiro argumento de add
. Como add
leva dois argumentos, a nova inc
função deseja apenas o b
argumento - não 2 argumentos como antes, pois 1 já foi parcialmente aplicado. Assim, partial
é uma ferramenta a partir da qual criar novas funções com os valores padrão pré-fornecidos. É por isso que, em uma linguagem funcional, as funções geralmente ordenam argumentos de geral para específico. Isso facilita a reutilização de tais funções a partir das quais construir outras funções.
Agora imagine se a linguagem fosse inteligente o suficiente para entender introspectivamente que add
desejavam dois argumentos. Quando passamos para um argumento, em vez de recusar, e se a função aplicou parcialmente o argumento, passamos em nosso nome, entendendo que provavelmente pretendíamos fornecer o outro argumento posteriormente? Poderíamos então definir inc
sem usar explicitamente partial
.
(def inc (add 1)) #partial is implied
É assim que alguns idiomas se comportam. É excepcionalmente útil quando se deseja compor funções em transformações maiores. Isso levaria um a transdutores.
Encontrei este artigo e o artigo que ele faz referência útil para entender melhor o curry: http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx
Como os outros mencionados, é apenas uma maneira de ter uma função de um parâmetro.
Isso é útil porque você não precisa assumir quantos parâmetros serão passados; portanto, você não precisa de 2, 3 e 4 funções de parâmetro.
Como todas as outras respostas, o curry ajuda a criar funções parcialmente aplicadas. Javascript não fornece suporte nativo para currying automático. Portanto, os exemplos fornecidos acima podem não ajudar na codificação prática. Há um exemplo excelente no código de vida (que basicamente compila com js) http://livescript.net/
times = (x, y) --> x * y
times 2, 3 #=> 6 (normal use works as expected)
double = times 2
double 5 #=> 10
No exemplo acima, quando você forneceu menos argumentos, o livescript gera uma nova função ao curry para você (duplo)
O Curry pode simplificar seu código. Esse é um dos principais motivos para usar isso. Currying é um processo de conversão de uma função que aceita n argumentos em n funções que aceitam apenas um argumento.
O princípio é passar os argumentos da função passada, usando a propriedade encerramento (fechamento), para armazená-los em outra função e tratá-la como um valor de retorno, e essas funções formam uma cadeia e os argumentos finais são passados para concluir a operação.
O benefício disso é que ele pode simplificar o processamento de parâmetros, lidando com um parâmetro de cada vez, o que também pode melhorar a flexibilidade e a legibilidade do programa. Isso também torna o programa mais gerenciável. Também dividir o código em partes menores tornaria o uso reutilizável.
Por exemplo:
function curryMinus(x)
{
return function(y)
{
return x - y;
}
}
var minus5 = curryMinus(1);
minus5(3);
minus5(5);
Eu também posso fazer ...
var minus7 = curryMinus(7);
minus7(3);
minus7(5);
Isso é ótimo para tornar o código complexo limpo e manipular métodos não sincronizados, etc.
Uma função ao curry é aplicada a várias listas de argumentos, em vez de apenas uma.
Aqui está uma função regular, sem curry, que adiciona dois parâmetros Int, xey:
scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int,y: Int)Int
scala> plainOldSum(1, 2)
res4: Int = 3
Aqui está uma função semelhante que é curry. Em vez de uma lista de dois parâmetros Int, você aplica esta função a duas listas de um parâmetro Int cada:
scala> def curriedSum(x: Int)(y: Int) = x + y
curriedSum: (x: Int)(y: Int)Intscala> second(2)
res6: Int = 3
scala> curriedSum(1)(2)
res5: Int = 3
O que está acontecendo aqui é que, quando você invoca curriedSum
, você recebe duas invocações de funções tradicionais consecutivas. A primeira chamada de função usa um único parâmetro Int chamado x
e retorna um valor de função para a segunda função. Esta segunda função aceita o parâmetro Int
y
.
Aqui está uma função chamada first
que faz em espírito o que a primeira chamada de função tradicional curriedSum
faria:
scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int
A aplicação de 1 à primeira função - em outras palavras, invocando a primeira função e passando em 1 - produz a segunda função:
scala> val second = first(1)
second: (Int) => Int = <function1>
A aplicação de 2 à segunda função produz o resultado:
scala> second(2)
res6: Int = 3
Um exemplo de curry seria ao ter funções que você conhece apenas um dos parâmetros no momento:
Por exemplo:
func aFunction(str: String) {
let callback = callback(str) // signature now is `NSData -> ()`
performAsyncRequest(callback)
}
func callback(str: String, data: NSData) {
// Callback code
}
func performAsyncRequest(callback: NSData -> ()) {
// Async code that will call callback with NSData as parameter
}
Aqui, como você não conhece o segundo parâmetro para retorno de chamada ao enviá- performAsyncRequest(_:)
lo, seria necessário criar outro lambda / encerramento para enviá-lo à função.
func callback
voltando? Ele está sendo chamado @ callback(str)
assim let callback = callback(str)
, callback é apenas o valor de retornofunc callback
func callback(_:data:)
aceita dois parâmetros, aqui eu só dou um, o String
, então ele está aguardando o próximo ( NSData
), é por isso que agora let callback
existe outra função aguardando a
Aqui está o exemplo da versão genérica e a mais curta para currying de funções com n não. de params.
const add = a => b => b ? add(a + b) : a;
const add = a => b => b ? add(a + b) : a;
console.log(add(1)(2)(3)(4)());
Aqui você pode encontrar uma explicação simples sobre a implementação de currying em C #. Nos comentários, tentei mostrar como o curry pode ser útil:
public static class FuncExtensions {
public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
{
return x1 => x2 => func(x1, x2);
}
}
//Usage
var add = new Func<int, int, int>((x, y) => x + y).Curry();
var func = add(1);
//Obtaining the next parameter here, calling later the func with next parameter.
//Or you can prepare some base calculations at the previous step and then
//use the result of those calculations when calling the func multiple times
//with different input parameters.
int result = func(1);
O curry é uma das funções de ordem superior do Java Script.
Currying é uma função de muitos argumentos que são reescritos de modo que ele pegue o primeiro argumento e retorne uma função que, por sua vez, usa os argumentos restantes e retorna o valor.
Confuso?
Vamos ver um exemplo,
function add(a,b)
{
return a+b;
}
add(5,6);
Isso é semelhante à seguinte função de curry,
function add(a)
{
return function(b){
return a+b;
}
}
var curryAdd = add(5);
curryAdd(6);
Então, o que esse código significa?
Agora leia a definição novamente,
Currying é uma função de muitos argumentos que são reescritos de modo que ele pegue o primeiro argumento e retorne uma função que, por sua vez, usa os argumentos restantes e retorna o valor.
Ainda confuso? Deixe-me explicar profundamente!
Quando você chama essa função,
var curryAdd = add(5);
Ele retornará uma função como esta,
curryAdd=function(y){return 5+y;}
Portanto, isso é chamado de funções de ordem superior. Significado, invocar uma função em turnos retorna outra função é uma definição exata para a função de ordem superior. Essa é a maior vantagem da legenda, Java Script. Então volte para o curry,
Essa linha passará o segundo argumento para a função curryAdd.
curryAdd(6);
que por sua vez resulta,
curryAdd=function(6){return 5+6;}
// Which results in 11
Espero que você entenda o uso do curry aqui. Então, aproveitando as vantagens,
Por que currying?
Faz uso de reutilização de código. Menos código, menos erro. Você pode perguntar como é menos código?
Eu posso provar isso com o script ECMA 6, novas funções de seta.
Sim! ECMA 6, forneça o maravilhoso recurso chamado funções de seta,
function add(a)
{
return function(b){
return a+b;
}
}
Com a ajuda da função de seta, podemos escrever a função acima da seguinte maneira:
x=>y=>x+y
Legal certo?
Então, menos código e menos bugs !!
Com a ajuda dessas funções de ordem superior, é possível desenvolver facilmente um código sem erros.
Eu desafio-te!
Espero que você entenda o que está currying. Por favor, sinta-se livre para comentar aqui se precisar de algum esclarecimento.
Obrigado, tenha um bom dia!
Há um exemplo de "Currying in ReasonML".
let run = () => {
Js.log("Curryed function: ");
let sum = (x, y) => x + y;
Printf.printf("sum(2, 3) : %d\n", sum(2, 3));
let per2 = sum(2);
Printf.printf("per2(3) : %d\n", per2(3));
};
curry
euncurry
de Haskell. O importante aqui é que esses isomorfismos são corrigidos previamente e, portanto, "incorporados" à linguagem.