Pergunta

Já vi referências a funções curry em vários artigos e blogs, mas não consigo encontrar uma boa explicação (ou pelo menos uma que faça sentido!)

Foi útil?

Solução

Currying é quando você divide uma função que recebe vários argumentos em uma série de funções que fazem parte dos argumentos.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, a, e 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.Isso é o que algumas pessoas podem chamar de encerramento.A terceira instrução usa a operação add3 para somar 3 a 4, produzindo novamente 7 como resultado.

Outras dicas

Em uma álgebra de funções, lidar com funções que aceitam múltiplos argumentos (ou equivalente a um argumento que é uma N-tupla) é um tanto deselegante - mas, como Moses Schönfinkel (e, independentemente, Haskell Curry) provaram, não é necessário:tudo que você precisa são funções que recebam um argumento.

Então, como você lida com algo que você expressaria naturalmente como, digamos, f(x,y)?Bem, você considera isso equivalente a f(x)(y) -- f(x), chame-o g, é uma função, e você aplica essa função a y.Em outras palavras, você só tem funções que aceitam um argumento - mas algumas dessas funções retornam outras funções (que TAMBÉM aceitam um argumento ;-).

Como sempre, Wikipédia tem um bom resumo sobre isso, com muitas dicas úteis (provavelmente incluindo aquelas relacionadas aos seus idiomas favoritos ;-) bem como um tratamento matemático um pouco mais rigoroso.

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, você pode encontrá-la aqui.Esta função recebe os três parâmetros necessários como argumentos.

Agora, estando na Terra, você deseja apenas calcular as forças dos objetos neste planeta.Em uma linguagem funcional, você poderia passar a massa da Terra para a função e então avaliá-la parcialmente.O que você receberá é outra função que recebe apenas dois argumentos e calcula a força gravitacional dos objetos na Terra.Isso é chamado de curry.

Currying é uma transformação que pode ser aplicada a funções para permitir que elas recebam um argumento a menos do que antes.

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, y e z e os soma, então: -

f 1 2 3

Retorna 6.

A partir da nossa definição podemos, portanto, definir a função curry para f: -

let curry f = fun x -> f x

Onde 'fun x -> f x' é 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 leva um único argumento e retorna a função especificada com o primeiro argumento definido como 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 y z = 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 de função', que pode ser definida assim: -

let papply f x = f x

Embora possamos estendê-lo para 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.

Uma aplicação parcial pegará a função e o(s) parâmetro(s) e retornará uma função que requer um ou mais parâmetros a menos e, como mostram os dois exemplos anteriores, é implementada diretamente na definição de função F# padrão para que possamos obter o resultado anterior assim: -

let f1 = f 1
f1 2 3

O que retornará um resultado de 6.

Para concluir:-

A diferença entre currying e aplicação de função parcial é que: -

Currying pega uma função e fornece uma nova função que aceita um único argumento e retorna a função especificada com seu primeiro argumento definido para esse argumento. Isso nos permite representar funções com múltiplos 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

A aplicação de função parcial é mais direta - ela 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 forma 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 executa o 10 é passado como x;

let add = function(10){
  return function(y){
    return 10 + y 
  };
};

o que significa que recebemos esta função:

function(y) { return 10 + y };

Então, quando você ligar

 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

Então nosso addTen() sempre acrescenta dez a tudo o que passamos.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...

Uma função curried é uma função de vários argumentos reescritos de forma que aceite o primeiro argumento e retorne uma função que aceite o segundo argumento e assim por diante.Isto permite que funções de vários argumentos tenham alguns de seus argumentos iniciais parcialmente aplicados.

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..."

(Apenas usando concatenação via + para evitar distração para programadores que não são Python.)

Editando para adicionar:

Ver http://docs.python.org/library/functools.html?highlight=partial#functools.partial, que também mostra o objeto parcial vs.distinção de função na maneira como o Python implementa isso.

Se você entender partial você está na metade do caminho.A ideia de partial é pré-aplicar argumentos a uma função e devolver uma nova função que deseja apenas os argumentos restantes.Quando esta nova função é chamada, ela inclui os argumentos pré-carregados junto com quaisquer argumentos que tenham sido fornecidos a ela.

Em Clojure + é uma função, mas para deixar as coisas bem claras:

(defn add [a b] (+ a b))

Você pode estar ciente de que o inc função simplesmente adiciona 1 a qualquer número passado.

(inc 7) # => 8

Vamos construí-lo nós mesmos usando partial:

(def inc (partial add 1))

Aqui retornamos outra função que possui 1 carregado no primeiro argumento de add.Como add leva dois argumentos o novo inc função quer apenas o b argumento - não 2 argumentos como antes, pois 1 já foi parcialmente aplicado.Por isso partial é uma ferramenta para criar novas funções com valores padrão pré-fornecidos.É por isso que em uma linguagem funcional as funções geralmente ordenam os argumentos do geral para o específico.Isso torna mais fácil reutilizar 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 queria dois argumentos.Quando passamos um argumento para ela, em vez de hesitar, e se a função aplicasse parcialmente o argumento que passamos em nosso nome, entendendo que provavelmente pretendíamos fornecer o outro argumento mais tarde?Poderíamos então definir inc sem usar explicitamente partial.

(def inc (add 1)) #partial is implied

É assim que algumas linguagens se comportam.É excepcionalmente útil quando se deseja compor funções em transformações maiores.Isso levaria a transdutores.

Achei este artigo, e o artigo a que ele faz referência, úteis para entender melhor o curry:http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx

Como os outros mencionaram, é apenas uma forma de ter uma função de um parâmetro.

Isso é útil porque você não precisa assumir quantos parâmetros serão passados, portanto não precisa de funções de 2 parâmetros, 3 parâmetros e 4 parâmetros.

Curry pode simplificar seu código.Esta é uma das principais razões 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 completar a operação.

A vantagem disso é que pode simplificar o processamento de parâmetros, lidando com um parâmetro por vez, o que também pode melhorar a flexibilidade e a legibilidade do programa.Isso também torna o programa mais gerenciável.Além disso, dividir o código em partes menores o tornaria fácil de reutilizar.

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 códigos complexos organizados e lidar com métodos não sincronizados, etc.

Currying está traduzindo uma função de chamável como f(a, b, c) em chamável como f(a)(b)(c).

Caso contrário, curry é quando você divide uma função que recebe vários argumentos em uma série de funções que fazem parte dos argumentos.

Literalmente, curry é 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.

Currying não chama uma função.Apenas o transforma.

Vamos fazer uma função curry que executa curry para funções de dois argumentos.Em outras palavras, curry(f) para dois argumentos f(a, b) traduz isso em f(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.

  • O resultado de curry(func) é um invólucro function(a).
  • Quando é chamado assim sum(1), o argumento é salvo no Ambiente Lexical e um novo wrapper é retornado function(b).
  • Então sum(1)(2) finalmente liga function(b) fornecendo 2 e passa a chamada para a soma original de vários argumentos.

Uma função ao curry é aplicada a várias listas de argumentos, em vez de apenas uma.

Aqui está uma função regular e sem corrente, que adiciona dois parâmetros int, x e y:

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 ao 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ê obtém duas invocações de função tradicionais consecutivas.A primeira invocação de funções leva um único parâmetro int nomeado x e retorna um valor de função para a segunda função.Esta segunda função leva o parâmetro Inty.

Aqui está uma função chamada first Isso faz em espírito o que a primeira invocação de função tradicional de curriedSum faria:

scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int

Aplicando 1 à primeira função - em outras palavras, invocando a primeira função e passando em 1 - sim a segunda função:

scala> val second = first(1)
second: (Int) => Int = <function1>

Aplicar 2 à segunda função produz o resultado:

scala> second(2)
res6: Int = 3

Um exemplo de curry seria quando, tendo funções, 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 do retorno de chamada ao enviá-lo para performAsyncRequest(_:) você teria que criar outro lambda/fechamento para enviá-lo para a função.

Como todas as outras respostas, o curry ajuda a criar funções parcialmente aplicadas.Javascript não fornece suporte nativo para curry automático.Portanto, os exemplos fornecidos acima podem não ajudar na codificação prática.Há alguns exemplos excelentes em livescript (que essencialmente compila para 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 curried para você (duplo)

Aqui você pode encontrar uma explicação simples da implementação do curry 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);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top