Hazme un poco de curry

20

Tener una función f que toma argumentos x 1 , x 2 , ..., x n

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

- curry redefine f como una función tomando un solo argumento a 1 que se asigna a otra función más. Esta técnica es útil para aplicaciones parciales, por ejemplo, con una powfunción currificada que podríamos escribir exp = pow(e).

Ejemplo

Suponiendo que tenemos la siguiente función f tomando tres argumentos ( f: X 1 × X 2 × X 3 → Y ):

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

Curry esta función nos deja con f_curry: X 1 → (X 2 → (X 3 → Y)) , si ahora llamamos a esa función dos veces con f_curry(1)(2), obtendríamos una función ( h) equivalente a la siguiente devuelta:

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

La función curry fpodría escribirse así (Python 3):

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

Pruébalo en línea!

Desafío

Su desafío será curry una función como se describe anteriormente, aquí están las reglas:

  • La entrada será una función de caja negra que toma al menos 2 argumentos
  • La función de entrada siempre tendrá un número fijo de argumentos (a diferencia printfo similar, nota: debe admitir funciones con cualquier número de argumentos ≥2)
  • Si su lenguaje usa funciones curriculares por defecto (por ejemplo, Haskell), puede esperar que la función de entrada se defina sobre N -tuplas, en lugar de una "función de orden superior"
  • Puede tomar el número de argumentos como entrada
  • La salida será el equivalente al curry de la entrada *
  • Puede suponer que la función de salida solo será:
    • llamado con menos o igual al número de argumentos que toma la función de entrada
    • llamado con argumentos del tipo correcto

* Esto significaría para una entrada fcon Nargumentos y una salida hque para todos los argumentos válidos a1,…,aNcontiene eso f(a1,a2,…,aN) == h(a1)(a2)…(aN).

ბიმო
fuente
Relacionados .
ბიმო
entonces la entrada es def f(a,b,c): return a + b * cy la salida es def f_curry(a): def g_curry(b): def h(c): return a + b * c return h return g_curry?
DanielIndie
@DanielIndie: Si tomas ese ejemplo, la entrada sería f(que se define en alguna parte) y la salida debería ser algo equivalente a f_curry. O la entrada sería lambda a,b,c: a+b*cy la salida una función equivalente a f_curry.
ბიმო
Esto es difícil de hacer en la mayoría de los idiomas escritos estáticamente ... Supongo que necesita funciones de tipo para esto.
Paŭlo Ebermann
@ PaŭloEbermann: Cierto, algunos lenguajes no podrán resolver esta tarea (tenga en cuenta la etiqueta de programación funcional ). Sin embargo, algunos lenguajes tipados estáticamente podrían usar punteros de función que serían una E / S válida, esa es principalmente la razón por la que permití tomar el número de argumentos como entrada adicional.
ბიმო

Respuestas:

11

JavaScript (ES6), 35 bytes

f=g=>g.length<2?g:a=>f(g.bind(f,a))
Neil
fuente
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))

Pruébalo en línea!

¡Suena como un trabajo para personas dependientes! Bien quizás.


C es una función de tipo curry. Dado un vector de tipos a = [t 1 , t 2 ,… t n ] y una función de tipo T: HVect a → Type , devuelve un nuevo tipo:

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

Aquí, HVect es el tipo de vector heterogéneo del Preludio Idris, el tipo de n -tuplas cuyos elementos son de n tipos diferentes.

c es una función que toma una y T como argumentos implícitos, y luego convierte una uncurried función fde tipo ((b: HVect a) → T b) en un curry uno de tipo C a T .

( C simplemente describe lo que deseamos hacer; c en realidad lo hace. Pero no podemos escapar sin definir C , ya que Idris exige que cada definición de nivel superior tenga una firma de tipo).


El enlace TIO ofrece un ejemplo de uso. Si definimos una función en 3-tuplas (Nat, Nat, String) de la siguiente manera:

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

entonces uncurried [3, 4, "th"]produce el mismo resultado que c uncurried 3 4 "th". Idris infiere los argumentos a=[Nat, Nat, String]y T=const Stringpara nosotros, creo.

Basé este código en esta esencia de timjb.

Lynn
fuente
En mi opinión, las tuplas en Haskell e Idris deberían ser HVectpor defecto, HVectes esencialmente una tupla que puedes deshacer.
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))

Pruébalo en línea!


Versión anterior (97 bytes)

-1 byte gracias a @JayCE

digEmAll
fuente
No veo cómo acortarlo fundamentalmente . Puede eliminar tres bytes de golf eliminando las llaves y el espacio al final de la primera línea. Y dos más debido a la convención de no incluir el nombre de la función en el recuento de bytes. TIO
ngm
@ngm El nombre de la función debe incluirse cuando es recursivo.
Ørjan Johansen
@ngm: puse la declaración if dentro de la subfunción ahorrando una décima 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])

Pruébalo en línea!

El pie de página es un probador que usa STDIN de la siguiente manera por línea:

  1. La función en sí
  2. El número de argumentos de la función, ≥2
  3. Una lista de los argumentos ( [a,b,...])

Tenga en cuenta que, mientras que una lista de los argumentos se proporciona como entrada en el probador, en realidad, el equivalente curricular se antepone a la lista y la lista se reduce por la llamada a la función.

Ovs ha proporcionado amablemente una versión similar de 55 bytes :

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

Pruébalo en línea!

Erik el Outgolfer
fuente
2

Coliflor , 84 bytes

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

Pruébalo en línea!

Solo ASCII
fuente
1
Mmm, coliflor al curry. Delicioso. ^ _ ^
DLosc
@DLosc no hay suficientes respuestas a este desafío en idiomas con nombres relacionados con alimentos: P (aunque supongo que la mayoría de ellos en realidad no tienen funciones)
solo ASCII
1

Adjunto , 5 bytes

Curry

Pruébalo en línea!

Simple incorporado, en gran parte poco interesante. Pero, aquí hay una versión desde cero:

Adjunto, 35 bytes

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

Explicación:

{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
Conor O'Brien
fuente
1

Java 8, 46 + 318 = 364 bytes

Esta es una lambda al curry (hah) que toma una función y un recuento de argumentos y devuelve la función al 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;}}}

Pruébalo en línea

Tipo de envío

Función de entrada

La entrada de función es un objeto con un único método (excluyendo métodos heredados) que representa la función. Tenga en cuenta que una interfaz funcional estándar no se puede utilizar como tipo de entrada porque se deben admitir funciones de (por ejemplo) 3 parámetros. También tenga en cuenta que java.util.function.Functionse puede pasar una expresión lambda convertida a un tipo de tipo estándar (el método único es apply).

Se pueden declarar excepciones marcadas en la función de entrada, pero no se pueden lanzar (es decir, no se propagarán al llamador de la función de salida). Se presume que esto es aceptable porque las interfaces funcionales de Java no permiten excepciones comprobadas (y su propagación evitaría que el envío devuelva a Function). Excepciones de tiempo de ejecución (asignables RuntimeExceptionoError ) se propagan.

Función de salida

El resultado de la presentación es a java.util.function.Function<Object, Object>. Pensé en devolver un plano Objectcon un applymétodo (como en la entrada), pero luego se requeriría una reflexión para invocar el resultado, lo que parecía lo suficientemente inconveniente como para que no se pueda permitir, en particular, llamar todo el tiempo ya no sería posible en un solo expresión.

Uso

Debido a que el envío devuelve una función de Objecta Object, la salida se puede invocar directamente (con apply), pero los valores de retorno intermedios posteriores se deben convertir a un tipo apropiado (por ejemplojava.util.function.Function<Object, Object> ) antes de invocarlo. Consulte el TIO para algunos ejemplos de uso.

Tenga en cuenta que en Java las funciones (es decir, los métodos) no son objetos de primera clase. Por lo tanto, la sintaxis utilizada en la viñeta de salida de la descripción del desafío no tiene sentido en Java. En lugar de f(a1, a2, a3)lo que tenemos f.apply(a1, a2, a3), y más de f(a1)(a2)(a3)lo que tenemos f.apply(a1).apply(a2).apply(a3).

Limitaciones

Cuando se aplica un resultado intermedio (se agrega un argumento), el resultado es en realidad una copia mutada del resultado original. Por ejemplo, en este fragmento:

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));

la línea 4 se imprimirá 4, pero la línea 5 fallará, porque para ese momento c2ya tiene argumentos 2y 2(tenga en cuenta también eso c2 == c). Esto viola el espíritu de curry, pero cumple con el requisito específico establecido en el desafío.

Sin golf

Vea el TIO para obtener una copia sin golf.

Jakob
fuente
1

Julia 0.6 , 48 bytes

c=(f,n,a=[])->n>1?x->c(f,n-1,[a;x]):x->f(a...,x)

Pruébalo en línea!

Puerto de la respuesta de Python de @ EricTheOutgolfer.

sundar - Restablecer a Monica
fuente
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

Pruébalo en línea!

Llamando a la sintaxis (con función de curry g, los argumentos x1a través de x3, y el número de argumentos n):

((n x1 f g) x2) x3

Requiere ⎕IO←1

Zacharý
fuente