Faça-me um pouco de curry


20

Ter uma função f que recebe argumentos x 1 , x 2 ,…, x n

                                               - ie.  f: X 1 × X 2 ×… × X n → Y

- currying redefine f como uma função usando um único argumento a 1 que mapeia para outra função. Essa técnica é útil para aplicação parcial, por exemplo, com uma powfunção ao curry que poderíamos escrever exp = pow(e).

Exemplo

Assumindo que temos a seguinte função f usando três argumentos ( f: X 1 × X 2 × X 3 → Y ):

def f(a,b,c):
  return a + b * c

A seleção dessa função nos deixa com f_curry: X 1 → (X 2 → (X 3 → Y)) , se agora chamarmos essa função duas vezes, obteríamosf_curry(1)(2) uma função ( h) equivalente ao seguinte retornado:

def h(c):
   return 1 + 2 * c

A função ao curry fpode ser escrita assim (Python 3):

def f_curry(a):
  def g_curry(b):
    def h(c):
      return a + b * c
    return h
  return g_curry

Experimente online!

Desafio

Seu desafio será curry uma função como descrito acima, aqui estão as regras:

  • Entrada será uma função de caixa preta que requer pelo menos 2 argumentos
  • A função de entrada sempre terá um número fixo de argumentos (diferente printfou semelhante, nota: você precisa suportar funções com qualquer número de argumentos ≥2)
  • Se o seu idioma usa funções com caril por padrão (por exemplo, Haskell), você pode esperar que a função de entrada seja definida sobre N- pares, em vez de uma "função de ordem superior"
  • Você pode usar o número de argumentos como entrada
  • A saída será o equivalente ao curry da entrada *
  • Você pode assumir que a função de saída será sempre:
    • chamado com menor ou igual ao número de argumentos que a função de entrada leva
    • chamado com argumentos do tipo certo

* Isso significaria para uma entrada fcom Nargumentos e uma saída hque, para todos os argumentos válidos, a1,…,aNela contenha isso f(a1,a2,…,aN) == h(a1)(a2)…(aN).



então a entrada é def f(a,b,c): return a + b * ce a saída é def f_curry(a): def g_curry(b): def h(c): return a + b * c return h return g_curry?
precisa saber é o seguinte

@ DanielIndie: Se você está tomando esse exemplo, a entrada seria f(que é definida em algum lugar) e a saída deve ser algo equivalente f_curry. Ou a entrada seria lambda a,b,c: a+b*ce a saída uma função equivalente a f_curry.
ბიმო

Isso é difícil de fazer na maioria das linguagens estaticamente tipadas ... Acho que você precisa de funções de tipo para isso.
Paŭlo Ebermann

@ PaŭloEbermann: É verdade que algumas linguagens não serão capazes de resolver esta tarefa (observe a tag programação-funcional ). No entanto, algumas linguagens de tipo estaticamente podem usar ponteiros de função que seriam uma E / S válida, esse é principalmente o motivo pelo qual permiti usar o número de argumentos como entrada adicional.
ბიმო

Respostas:


11

JavaScript (ES6), 35 bytes

f=g=>g.length<2?g:a=>f(g.bind(f,a))

9

Idris , 204 bytes

import Data.HVect
C:(a:Vect n Type)->(HVect a->Type)->Type
C[]T=T[]
C(h::t)T=(x:h)->C t(T .(x::))
c:{a:Vect n Type}->{T:HVect a->Type}->((b:HVect a)->T b)->C a T
c{a=[]}f=f[]
c{a=h::t}f=\v=>c(\u=>f(v::u))

Experimente online!

Parece um trabalho para tipos dependentes! Bem, talvez.


C é uma função do tipo curry. Dado um vetor dos tipos a = [t 1 , t 2 ,… t n ] e uma função de tipo T: HVect a → Type , ele retorna um novo tipo:

           (x 1  : t 1 ) → (x 2  : t 2 ) →… → (T [x 1 , x 2 ,… x n ])

Aqui, HVect é o tipo de vetor heterogêneo do Idris Prelude - o tipo de n- pares cujos elementos são de n tipos diferentes.

c é uma função que recebe um e T como argumentos implícitas, e, em seguida, converte um uncurried função fdo tipo ((b: HVect um) → T b) em uma curried um de tipo C de um T .

( C simplesmente descreve o que queremos fazer; c realmente faz isso. Mas não podemos deixar de definir C , pois Idris exige que todas as definições de nível superior tenham uma assinatura de tipo.)


O link TIO fornece um exemplo de uso. Se definirmos uma função em três tuplas (Nat, Nat, String) da seguinte maneira:

uncurried : HVect [Nat, Nat, String] -> String
uncurried [a, b, c] = show (a*a + b*b) ++ c

então uncurried [3, 4, "th"]gera o mesmo resultado que c uncurried 3 4 "th". Idris deduz os argumentos a=[Nat, Nat, String]e, T=const Stringpara nós, acredito.

Baseei este código nesta essência por timjb.


Na minha opinião, as tuplas em Haskell e Idris devem, na verdade, ser HVectpor padrão - HVecté essencialmente uma tupla que você pode desconectar.
Esolanging Fruit


5

R , 96 bytes

y=function(f,n=length(formals(f)),p=list())function(x,u=c(p,x))`if`(n<2,do.call(f,u),y(f,n-1,u))

Experimente online!


Versão anterior (97 bytes)

-1 byte graças a @JayCE


Não vejo como encurtá-lo fundamentalmente . Você pode jogar fora três bytes de golfe se livrando das chaves e do espaço no final da primeira linha. E mais dois devido à convenção aqui de não incluir o nome da função na contagem de bytes. TIO
ngm 27/05

@ngm O nome da função deve ser incluído quando for recursivo.
Ørjan Johansen

@ngm: Eu coloquei a instrução if dentro da sub-função de poupança de um décimo de bytes :)
digEmAll


3

Python 2 , 60 bytes

c=lambda f,n,l=[]:lambda a:n-1and c(f,n-1,l+[a])or f(*l+[a])

Experimente online!

O rodapé é um testador que usa STDIN da seguinte maneira por linha:

  1. A própria função
  2. O número de argumentos da função, ≥2
  3. Uma lista dos argumentos ( [a,b,...])

Observe que, enquanto uma lista dos argumentos é fornecida como entrada no testador, na realidade, o equivalente ao curry é anexado à lista e a lista é reduzida por chamada de função.

Uma versão semelhante de 55 bytes foi gentilmente fornecida por ovs :

c=lambda f,n,*l:lambda a:n-1and c(f,n-1,*l,a)or f(*l,a)

Experimente online!


2

Couve-flor , 84 bytes

(:= c(\($f$n(@a))(if$n(\($a)(call c(cat(list$f(-$n 1))@a(list$a))))else(call$f@a))))

Experimente online!


1
Caril de couve-flor. Delicioso. ^ _ ^
DLosc

@DLosc não há respostas suficientes para esse desafio em idiomas com nomes relacionados a alimentos: P (embora eu ache que a maioria deles não tenha funções) #
ASCII-only



1

Anexo , 5 bytes

Curry

Experimente online!

Simples construído, em grande parte desinteressante. Mas, aqui está uma versão do zero:

Anexo, 35 bytes

{If[#__2<#_,Fold[`&:,$'__],_@@__2]}

Explicação:

{If[#__2<#_,Fold[`&:,$'__],_@@__2]}
{                                 }    lambda
 If[       ,              ,      ]     if
    #__2                                 the number of parameters after the first
        <#_                              is less than the arity of the first
            Fold[   ,    ]             then fold:
                 `&:                     right-function bonding
                     $                     over this function
                      '__                  paired with the rest of the parameters
                          ,            otherwise:
                           _@@           call the first parameter
                              __2        with the rest of them

1

Java 8, 46 + 318 = 364 bytes

Este é um lambda com curry (hah) que recebe uma função e uma contagem de argumentos e retorna a função com curry.

import java.lang.reflect.*;import java.util.*;

f->p->new java.util.function.Function(){Object F=f;Method m=F.getClass().getDeclaredMethods()[0];int P=p;List a=new Stack();public Object apply(Object r){a.add(r);try{return a.size()<P?this:m.invoke(F,a.toArray());}catch(Throwable t){t=t.getCause();if(t instanceof Error)throw(Error)t;else throw(RuntimeException)t;}}}

Experimente Online

Tipo de envio

Função de entrada

A entrada da função é um objeto com um único método (excluindo métodos herdados) representando a função. Observe que uma interface funcional padrão não pode ser usada como o tipo de entrada, porque funções de (por exemplo) 3 parâmetros devem ser suportadas. Observe também que uma expressão lambda convertida em um java.util.function.Functiontipo padrão pode ser passada (o método único é apply).

As exceções marcadas podem ser declaradas na função de entrada, mas não podem ser lançadas (ou seja, não serão propagadas para o chamador da função de saída). Presume-se que isso seja aceitável porque as interfaces funcionais do Java não permitem exceções verificadas (e sua propagação impediria o envio de retornar a Function). As exceções de tempo de execução (atribuíveis a RuntimeExceptionou Error) são propagadas.

Função de saída

A saída da submissão é a java.util.function.Function<Object, Object>. Eu considerei retornar uma planície Objectcom um applymétodo (como na entrada), mas a reflexão seria necessária para invocar o resultado, o que parecia inconveniente o suficiente para ser desaprovável - em particular, chamar o tempo todo não seria mais possível em um único expressão.

Uso

Como o envio retorna uma função de Objectpara Object, a saída pode ser chamada diretamente (com apply), mas os valores de retorno intermediários subsequentes devem ser convertidos em um tipo apropriado (por exemplo java.util.function.Function<Object, Object>) antes de serem chamados. Consulte o TIO para obter alguns exemplos de uso.

Observe que nas funções Java (ou seja, métodos) não são objetos de primeira classe. Portanto, a sintaxe usada no marcador de saída da descrição do desafio não tem sentido em Java. Mais do f(a1, a2, a3)que temos f.apply(a1, a2, a3), e mais do f(a1)(a2)(a3)que temos f.apply(a1).apply(a2).apply(a3).

Limitações

Quando um resultado intermediário é aplicado (um argumento adicionado), o resultado é na verdade uma cópia modificada do resultado original. Por exemplo, neste trecho:

Function<Object, Function<Integer, Function>> submission = ...;
Function c = submission.apply((IntBinaryOperator) (a, b) -> a + b).apply(2);
Function c2 = (Function) c.apply(2);
System.out.println(c2.apply(2));
System.out.println(c2.apply(3));

a linha 4 seria impressa 4, mas a linha 5 falharia, porque nessa época c2já contém argumentos 2e 2(observe também isso c2 == c). Isso viola o espírito de curry, mas atende aos requisitos específicos declarados no desafio.

Ungolfed

Veja o TIO para obter uma cópia não destruída.



0

APL (Dyalog Classic) , 58 57 bytes

r←(a f g)x
:If a[1]=≢a
rg 1a,x
:Else
r←(a,x)f g
:End

Experimente online!

Sintaxe de chamada (com função ao curry g, argumentos x1até x3e número de argumentosn ):

((n x1 f g) x2) x3

Requer ⎕IO←1

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.