Pergunta

Eu muitas vezes ver na Internet várias queixas que outros povos exemplos de currying não são currying, mas são de aplicação na verdade apenas parcial.

Eu não encontrei uma explicação decente do que a aplicação parcial é, ou como ele difere do currying. Parece haver uma confusão geral, com exemplos equivalentes ser descrito como currying em alguns lugares, e aplicação parcial nos outros.

Alguém poderia me fornecer uma definição de ambos os termos e detalhes de como eles diferem?

Foi útil?

Solução

Currying está convertendo uma única função de n argumentos em n funções com um único argumento cada. Dada a seguinte função:

function f(x,y,z) { z(x(y));}

Quando curry, torna-se:

function f(x) { lambda(y) { lambda(z) { z(x(y)); } } }

A fim de obter a plena aplicação do f (x, y, z), você precisa fazer isso:

f(x)(y)(z);

Muitas linguagens funcionais permitem escrever f x y z. Se você só chamar f x y ou f (x) (y) então você obtém uma função do valor de retorno parcialmente aplicada é um fechamento de lambda(z){z(x(y))} com passados-in os valores de X e Y para f(x,y).

Uma maneira de usar a aplicação parcial é definir as funções como aplicações parciais de funções generalizadas, como dobrar :

function fold(combineFunction, accumulator, list) {/* ... */}
function sum     = curry(fold)(lambda(accum,e){e+accum}))(0);
function length  = curry(fold)(lambda(accum,_){1+accum})(empty-list);
function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list);

/* ... */
@list = [1, 2, 3, 4]
sum(list) //returns 10
@f = fold(lambda(accum,e){e+accum}) //f = lambda(accumulator,list) {/*...*/}
f(0,list) //returns 10
@g = f(0) //same as sum
g(list)  //returns 10

Outras dicas

A maneira mais fácil de ver como eles diferem é considerar um verdadeiro exemplo . Vamos supor que temos um Add função que leva 2 números como entrada e retorna um número como de saída, por exemplo, Add(7, 5) retornos 12. Neste caso:

  • parcial aplicação o Add função com um 7 valor nos dará uma nova função como saída. Que a função em si leva um número como entrada e gera um número. Como tal:

    Partial(Add, 7); // returns a function f2 as output
    
                     // f2 takes 1 number as input and returns a number as output
    

    Assim, podemos fazer isso:

    f2 = Partial(Add, 7);
    f2(5); // returns 12;
           // f2(7)(5) is just a syntactic shortcut
    
  • Currying a função Add nos dará uma nova função como saída. Que a função em si leva um número como entrada e saídas ainda outra nova função. Essa terceira função, em seguida, leva um número como entrada e retorna um número como saída. Como tal:

    Curry(Add); // returns a function f2 as output
    
                // f2 takes 1 number as input and returns a function f3 as output
                // i.e. f2(number) = f3
    
                // f3 takes 1 number as input and returns a number as output
                // i.e. f3(number) = number
    

    Assim, podemos fazer isso:

    f2 = Curry(Add);
    f3 = f2(7);
    f3(5); // returns 12
    

Em outras palavras, "currying" e "aplicação parcial" são duas funções totalmente diferentes. Currying leva exactamente uma entrada, que a aplicação parcial leva 2 entradas (ou mais).

Mesmo que ambos retorno de uma função de saída como, as funções retornados são totalmente de diferentes formas, como demonstrado acima.

Nota: este foi tomado de F # Basics um excelente artigo introdutório para desenvolvedores .NET recebendo na programação funcional.

significa quebrar uma função com muitos argumentos em uma série Currying de funções que cada um leva um argumento e em última análise produzir o mesmo resultado que a função original. Currying é provavelmente o mais desafiando tópico para desenvolvedores de novo em programação funcional, sobretudo porque é muitas vezes confundida com a aplicação parcial. Você pode ver tanto no trabalho neste exemplo:

let multiply x y = x * y    
let double = multiply 2
let ten = double 5

Logo de cara, você deve ver o comportamento que é diferente da maioria linguagens imperativas. A segunda instrução cria uma nova função chamado double passando um argumento para uma função que leva dois. O resultado é uma função que aceita um argumento int e produz o mesmo resultado como se você tivesse chamado multiplicam com x igual a 2 e y igual ao argumento. Em termos de comportamento, é o mesmo que este código:

let double2 z = multiply 2 z

Muitas vezes, as pessoas erroneamente dizem que se multiplicam é curry para formar dupla. Mas este é apenas um pouco verdade. A função de multiplicar é curry, mas que acontece quando ele é definido porque as funções em F # são curry por padrão. Quando a dupla função é criada, é mais preciso dizer que a função Multiply é aplicada parcialmente.

A função de multiplicar é realmente uma série de duas funções. O primeiro função recebe um argumento int e retorna outra função, x efectivamente se ligar a um valor específico. Esta função também aceita um argumento int que você pode pensar em como o valor para vincular a y. Depois de chamando esta segunda função, x e y são ambos ligado, de modo que o resultado seja o produto de x e y, tal como definido no corpo da dupla.

Para criar dupla, a primeira função na cadeia de multiplicar funções é avaliada para aplicar parcialmente multiplicar. O resultado função é dado o nome de casal. Quando dupla é avaliado, ele usa seu argumento, juntamente com o valor aplicado parcialmente para criar o resultado.

É uma pergunta interessante. Depois de um pouco de pesquisa, "função parcial de aplicativo não é currying" deu a melhor explicação que encontrado. Eu não posso dizer que o prática diferença é particularmente óbvio para mim, mas então eu não sou um especialista FP ...

Outra página útil para o futuro (o que eu confesso que não li ainda totalmente) é "Currying e Aplicação parcial com Java Closures" .

O aspecto como este é par amplamente confusa de termos, você mente.

Eu respondi isso em outro segmento https://stackoverflow.com/a/12846865/1685865 . Em suma, a aplicação de função parcial é fixa sobre alguns argumentos de uma dada função multivariável para se obter uma outra função com menos argumentos, enquanto Currying consiste em transformar uma função de N argumentos para uma função unária que retorna uma função unária ... [Um exemplo de currying é mostrado no final deste post.]

Currying é principalmente de interesse teórico: pode-se expressar computações usando apenas funções unários (ou seja, todas função é unário). Na prática, e como um subproduto, é uma técnica que pode fazer muitas útil (mas não todas) aplicações funcionais parciais trivial, se a linguagem tem funções curry. Mais uma vez, não é o único meio para implementar aplicações parciais. Assim, você poderá encontrar situações em que a aplicação parcial é feito de outra forma, mas as pessoas estão confundindo-o como Currying.

(Exemplo de Currying)

Na prática, seria não apenas escrever

lambda x: lambda y: lambda z: x + y + z

ou o javascript equivalente

function (x) { return function (y){ return function (z){ return x + y + z }}}

em vez de

lambda x, y, z: x + y + z

por causa do Currying.

Currying é uma função do um argumento que leva uma função f e retorna uma nova função h. Note-se que h leva um argumento de X e retorna uma função que mapeia Y para Z:

curry(f) = h 
f: (X x Y) -> Z 
h: X -> (Y -> Z)

aplicação parcial é uma função de <> fortes dois (ou mais) fortes argumentos que leva um f função e um ou mais argumentos adicionais para f e retorna uma nova função g:

part(f, 2) = g
f: (X x Y) -> Z 
g: Y -> Z

A confusão surge porque com uma função com dois argumentos a seguinte igualdade é válida:

partial(f, a) = curry(f)(a)

Ambos os lados irá produzir a mesma função de um argumento.

A igualdade não é verdadeiro para as funções de aridade mais altos porque neste caso currying irá retornar uma função de um argumento, que a aplicação parcial irá retornar uma função de múltiplas argumento.

A diferença está também no comportamento, enquanto que currying transforma toda a função original de forma recursiva (uma vez para cada argumento), a aplicação parcial é apenas um substituto um passo.

Fonte:. Wikipedia Currying

A diferença entre o caril e aplicação parcial pode ser melhor ilustrada através deste exemplo seguinte JavaScript:

function f(x, y, z) {
    return x + y + z;
}

var partial = f.bind(null, 1);

6 === partial(2, 3);

aplicação parcial resulta numa função de aridade menor; no exemplo acima, f tem uma aridade de 3 enquanto partial só tem uma aridade de 2. Mais importante ainda, uma função parcialmente aplicada faria retornar o resultado imediatamente após serem invocação , e não outra função abaixo do currying corrente. Então, se você está vendo algo como partial(2)(3), não é aplicação parcial na realidade.

Além disso leitura:

Para mim aplicação parcial deve criar uma nova função em que os argumentos utilizados são perfeitamente integrados na função resultante.

A maioria das linguagens funcionais implementar currying, retornando um fecho: não avaliam sob lambda quando parcialmente aplicada. Assim, a aplicação parcial de ser interessante, é preciso fazer a diferença entre currying e aplicação parcial e considerar a aplicação parcial como currying além de avaliação sob lambda.

Eu poderia ser muito errado aqui, como eu não tenho uma sólida formação em matemática teórica ou de programação funcional, mas a partir de minha breve incursão na FP, parece que currying tende a transformar uma função de argumentos N em funções N de um argumento, que a aplicação parcial [na prática] funciona melhor com funções variádicos com um número indeterminado de argumentos. Sei que alguns dos exemplos em respostas anteriores desafiar essa explicação, mas ele me ajudou a mais para separar os conceitos. Considere este exemplo (escrito em CoffeeScript de concisão, as minhas desculpas se confunde ainda mais, mas por favor, pedir esclarecimentos, se necessário):

# partial application
partial_apply = (func) ->
  args = [].slice.call arguments, 1
  -> func.apply null, args.concat [].slice.call arguments

sum_variadic = -> [].reduce.call arguments, (acc, num) -> acc + num

add_to_7_and_5 = partial_apply sum_variadic, 7, 5

add_to_7_and_5 10 # returns 22
add_to_7_and_5 10, 11, 12 # returns 45

# currying
curry = (func) ->
  num_args = func.length
  helper = (prev) ->
    ->
      args = prev.concat [].slice.call arguments
      return if args.length < num_args then helper args else func.apply null, args
  helper []

sum_of_three = (x, y, z) -> x + y + z
curried_sum_of_three = curry sum_of_three
curried_sum_of_three 4 # returns a function expecting more arguments
curried_sum_of_three(4)(5) # still returns a function expecting more arguments
curried_sum_of_three(4)(5)(6) # returns 15
curried_sum_of_three 4, 5, 6 # returns 15

Este é obviamente um exemplo inventado, mas aviso que a aplicação parcialmente uma função que aceita qualquer número de argumentos que nos permite executar uma função, mas com alguns dados preliminares. Currying uma função é semelhante, mas nos permite executar uma função N-parâmetro em pedaços até, mas só até que, todos os parâmetros N são contabilizados.

Mais uma vez, esta é a minha opinião de coisas que eu li. Se discorda ninguém, eu gostaria de receber um comentário a respeito de porque em vez de um downvote imediata. Além disso, se o CoffeeScript é difícil de ler, por favor visita coffeescript.org, clique em "tentar CoffeeScript" e cole no meu código para ver a versão compilada, o que pode (espero) fazer mais sentido. Obrigado!

Eu tive esta pergunta muito enquanto aprende e já foram perguntou-lo muitas vezes. A maneira mais simples que eu posso descrever a diferença é que ambos são os mesmos :) Deixe-me explicar ... há obviamente diferenças.

Tanto a aplicação parcial e currying envolvem fornecendo argumentos para uma função, talvez não todos de uma vez. Um exemplo bastante canónico é a adição de dois números. Em pseudocódigo (na verdade JS sem palavras-chave), a função de base pode ser o seguinte:

add = (x, y) => x + y

Se eu quisesse uma função "addOne", eu poderia parcialmente aplicá-lo ou curry-lo:

addOneC = curry(add, 1)
addOneP = partial(add, 1)

Agora usá-los é claro:

addOneC(2) #=> 3
addOneP(2) #=> 3

Então, qual é a diferença? Bem, é sutil, mas a aplicação parcial envolve o fornecimento de alguns argumentos ea função retornou, então, executar a função principal em cima próxima invocação enquanto currying vai continuar esperando até que tenha todos os argumentos necessários:

curriedAdd = curry(add) # notice, no args are provided
addOne = curriedAdd(1) # returns a function that can be used to provide the last argument
addOne(2) #=> returns 3, as we want

partialAdd = partial(add) # no args provided, but this still returns a function
addOne = partialAdd(1) # oops! can only use a partially applied function once, so now we're trying to add one to an undefined value (no second argument), and we get an error

Em suma, usar a aplicação parcial por preencher alguns valores, sabendo que a próxima vez que você chamar o método, ele irá executar, deixando indefinido todos os argumentos desprovidos; usar currying quando você quer retornar continuamente uma função parcialmente aplicada tantas vezes quanto necessário para cumprir a assinatura da função. Um exemplo inventado final:

curriedAdd = curry(add)
curriedAdd()()()()()(1)(2) # ugly and dumb, but it works

partialAdd = partial(add)
partialAdd()()()()()(1)(2) # second invocation of those 7 calls fires it off with undefined parameters

Espero que isso ajude!

UPDATE: Algumas línguas ou implementações lib permitirá que você passe um arity (número total de argumentos na avaliação final) para a implementação aplicação parcial que pode fundem minhas duas descrições em uma confusão confuso ... mas nesse ponto, o duas técnicas são em grande parte intercambiáveis.

A resposta é simples

Curry:. permite que você chamar uma função, dividindo-o em várias chamadas, fornecendo um argumento per-call

parcial:. permite que você chamar uma função, dividindo-o em várias chamadas, fornecendo vários argumentos per-call


Dicas simples

Ambos permitem que você chamar uma função fornecendo menos argumentos (ou, melhor, proporcionando-lhes cumulativamente). Na verdade, ambos bind (em cada chamada) um valor específico para argumentos específicos da função.

A diferença real pode ser visto quando a função tem mais de 2 argumentos.


Simples e (c) (amostra)

(em JavaScript)

function process(context, success_callback, error_callback, subject) {...}

porque sempre passando os argumentos, como contexto e os retornos de chamada, se eles vão ser sempre o mesmo? Apenas ligam alguns valores para a função

processSubject = _.partial(process, my_context, my_success, my_error)

e chamá-lo em assunto1 e foobar com

processSubject('subject1');
processSubject('foobar');

Comfy, não é? ??

Com currying que você precisa para passar um argumento por hora

curriedProcess = _.curry(process);
processWithBoundedContext = curriedProcess(my_context);
processWithCallbacks = processWithBoundedContext(my_success)(my_error); // note: these are two sequential calls

result1 = processWithCallbacks('subject1');
// same as: process(my_context, my_success, my_error, 'subject1');
result2 = processWithCallbacks('foobar'); 
// same as: process(my_context, my_success, my_error, 'foobar');

Aviso

Eu pulei toda a explicação acadêmica / matemática. Porque eu não sei isso. Talvez tenha ajudado ??

Existem outros grandes respostas aqui, mas eu acredito que este exemplo (de acordo com o meu entendimento) em Java pode ser benéfica para algumas pessoas:

public static <A,B,X> Function< B, X > partiallyApply( BiFunction< A, B, X > aBiFunction, A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< X > partiallyApply( Function< A, X > aFunction, A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  A, Function< B, X >  > curry( BiFunction< A, B, X > bif ){
    return a -> partiallyApply( bif, a );
}

Então currying dá-lhe uma função de um argumento para criar funções, onde aplicação parcial cria uma função de invólucro que códigos rígidos um ou mais argumentos.

Se você quiser copiar e colar, o seguinte é mais ruidoso, mas mais amigável para trabalhar com uma vez que os tipos são mais tolerantes:

public static <A,B,X> Function< ? super B, ? extends X > partiallyApply( final BiFunction< ? super A, ? super B, X > aBiFunction, final A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< ? extends X > partiallyApply( final Function< ? super A, X > aFunction, final A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  ? super A,  Function< ? super B, ? extends X >  > curry( final BiFunction< ? super A, ? super B, ? extends X > bif ){
    return a -> partiallyApply( bif, a );
}

Ao escrever isto, eu confuso currying e uncurrying. Eles são transformações inversas em funções. Realmente não importa o que você chama o que, contanto que você recebe o que a transformação eo seu inverso representam.

Uncurrying não está definido de forma muito clara (ou melhor, não são "conflitantes" definições que todos captura o espírito da idéia). Basicamente, isso significa transformar uma função que leva vários argumentos para uma função que leva um único argumento. Por exemplo,

(+) :: Int -> Int -> Int

Agora, como você transformar isso em uma função que recebe um único argumento? Você enganar, é claro!

plus :: (Int, Int) -> Int

Observe que mais agora recebe um único argumento (que é composto de duas coisas). Super!

Qual é o ponto disso? Bem, se você tem uma função que recebe dois argumentos, e você tem um par de argumentos, é bom saber que você pode aplicar a função aos argumentos, e ainda obter o que você espera. E, de fato, o encanamento para fazer isso já existe, de modo que você não tem que fazer coisas como correspondência de padrão explícito. Tudo que você tem a fazer é:

(uncurry (+)) (1,2)

Então, qual é a aplicação de função parcial? É uma maneira diferente de transformar uma função em dois argumentos em uma função com um argumento. Ele funciona de forma diferente embora. Mais uma vez, vamos dar (+) como um exemplo. Como podemos transformá-lo em uma função que leva um único Int como um argumento? Nós enganamos!

((+) 0) :: Int -> Int

Essa é a função que adiciona zero a qualquer Int.

((+) 1) :: Int -> Int

adiciona 1 a qualquer Int. Etc. Em cada um destes casos, (+) é "parcialmente aplicado".

Eu estou indo supor que a maioria das pessoas que fazem essa pergunta já estão familiarizados com os conceitos básicos para que a sua não é preciso falar sobre isso. É a sobreposição que é a parte confusa.

Você pode ser capaz de utilizar plenamente os conceitos, mas você entende-los juntos como este borrão conceitual amorfo pseudo-atômica. O que está faltando é saber onde o limite entre eles é.

Em vez de definir o que cada um é, é mais fácil para destacar apenas as diferenças-limite.

Currying é quando você definir a função.

aplicação parcial é quando você chamada a função.

Aplicação é matemática-falar para chamar uma função.

Parcial aplicação requer chamar uma função curry e recebendo uma função como o tipo de retorno.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top