Fais moi du curry

20

Avoir une fonction f qui prend les arguments x 1 , x 2 ,…, x n

                                               - c'est à dire.  f: X 1 × X 2 ×… × X n → Y

- le curry redéfinit f comme une fonction prenant un seul argument a 1 qui correspond à une autre fonction. Cette technique est utile pour une application partielle, par exemple avec une powfonction curry que nous pourrions écrire exp = pow(e).

Exemple

En supposant que nous ayons la fonction f suivante en prenant trois arguments ( f: X 1 × X 2 × X 3 → Y ):

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

Le curry de cette fonction nous laisse avec f_curry: X 1 → (X 2 → (X 3 → Y)) , si nous appelions maintenant cette fonction deux fois avec f_curry(1)(2)nous obtiendrions une fonction ( h) équivalente à la suivante:

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

La fonction curry fpourrait être écrite comme ceci (Python 3):

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

Essayez-le en ligne!

Défi

Votre défi sera de curry une fonction comme décrit ci-dessus, voici les règles:

  • L'entrée sera une fonction blackbox qui prend au moins 2 arguments
  • La fonction d'entrée aura toujours un nombre fixe d'arguments (contrairement printfou similaire, notez: vous devez prendre en charge les fonctions avec un nombre d'arguments ≥2)
  • Si votre langue utilise des fonctions curry par défaut (par exemple Haskell), vous pouvez vous attendre à ce que la fonction d'entrée soit définie sur N -tuples, au lieu d'une "fonction d'ordre supérieur"
  • Vous pouvez prendre le nombre d'arguments en entrée
  • La sortie sera l'équivalent au curry de l'entrée *
  • Vous pouvez supposer que la fonction de sortie ne sera que:
    • appelé avec moins ou égal au nombre d'arguments que la fonction d'entrée prend
    • appelé avec des arguments du bon type

* Cela signifierait pour une entrée favec des Narguments et une sortie hque pour tous les arguments valides, a1,…,aNelle contient cela f(a1,a2,…,aN) == h(a1)(a2)…(aN).

ბიმო
la source
Connexes .
ბიმო
donc l'entrée est def f(a,b,c): return a + b * cet la sortie est def f_curry(a): def g_curry(b): def h(c): return a + b * c return h return g_curry?
DanielIndie
@DanielIndie: Si vous prenez cet exemple, l'entrée serait f(qui est définie quelque part) et la sortie devrait être quelque chose d'équivalent f_curry. Ou l'entrée serait lambda a,b,c: a+b*cet la sortie une fonction équivalente à f_curry.
ბიმო
C'est difficile à faire dans la plupart des langages typés statiquement ... Je suppose que vous avez besoin de fonctions de type pour cela.
Paŭlo Ebermann
@ PaŭloEbermann: Certes, certains langages ne pourront pas résoudre cette tâche (notez la balise programmation-fonctionnelle ). Cependant, certains langages typés statiquement peuvent être capables d'utiliser des pointeurs de fonction qui seraient des E / S valides, c'est principalement la raison pour laquelle j'ai autorisé la prise du nombre d'arguments comme entrée supplémentaire.
ბიმო

Réponses:

11

JavaScript (ES6), 35 octets

f=g=>g.length<2?g:a=>f(g.bind(f,a))
Neil
la source
9

Idris , 204 octets

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

Essayez-le en ligne!

Cela ressemble à un travail pour les types dépendants! Eh bien, peut-être.


C est une fonction de type curry. Étant donné un vecteur de types a = [t 1 , t 2 ,… t n ] et une fonction de type T: HVect a → Type , il renvoie un nouveau type:

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

Ici, HVect est le type de vecteur hétérogène du Idris Prelude - le type de n -tuples dont les éléments sont de n types différents.

c est une fonction qui prend une et T comme arguments implicites, et convertit ensuite un uncurried fonction fdu type ((b: HVect a) → T b) dans un curry un de type C T .

( C décrit simplement ce que nous souhaitons faire; c le fait réellement. Mais nous ne pouvons pas nous passer de ne pas définir C , car Idris exige que chaque définition de niveau supérieur ait une signature de type.)


Le lien TIO donne un exemple d'utilisation. Si nous définissons une fonction sur 3 tuples (Nat, Nat, String) comme suit:

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

uncurried [3, 4, "th"]donne alors le même résultat que c uncurried 3 4 "th". Idris déduit les arguments a=[Nat, Nat, String]et T=const Stringpour nous, je crois.

J'ai basé ce code sur cet essentiel de timjb.

Lynn
la source
À mon avis, les tuples dans Haskell et Idris devraient en fait être HVectpar défaut - HVectest essentiellement un tuple que vous pouvez annuler.
Esolanging Fruit
5

R , 96 octets

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

Essayez-le en ligne!


Version précédente (97 octets)

-1 octet grâce à @JayCE

digEmAll
la source
Je ne vois pas comment le raccourcir fondamentalement . Vous pouvez jouer au golf sur trois octets en vous débarrassant des accolades et de l'espace à la fin de la première ligne. Et deux de plus en raison de la convention ici de ne pas inclure le nom de la fonction dans le nombre d'octets. TIO
ngm
@ngm Le nom de la fonction doit être inclus lorsqu'il est récursif.
Ørjan Johansen
@ngm: J'ai mis l'instruction if à l'intérieur de la sous-fonction en économisant un dixième d'octets :)
digEmAll
3

Python 2 , 60 octets

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

Essayez-le en ligne!

Le pied de page est un testeur qui utilise STDIN de la manière suivante par ligne:

  1. La fonction elle-même
  2. Le nombre d'arguments de la fonction, ≥2
  3. Une liste des arguments ( [a,b,...])

Notez que, alors qu'une liste des arguments est donnée en entrée dans le testeur, en réalité, l'équivalent au curry est ajouté à la liste et la liste est réduite par appel de fonction.

Une version similaire de 55 octets a été aimablement fournie par les ovs :

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

Essayez-le en ligne!

Erik le Outgolfer
la source
2

Chou - fleur , 84 octets

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

Essayez-le en ligne!

ASCII uniquement
la source
1
Mmm, curry de chou-fleur. Délicieux. ^ _ ^
DLosc
@DLosc il n'y a pas assez de réponses à ce défi dans les langages avec des noms liés à la nourriture: P (bien que je suppose que la plupart d'entre eux n'ont pas de fonctions)
ASCII uniquement
2

Perl 6 , 42 40 octets

my&c={.count>1??{c(.assuming($^a))}!!$_}

Essayez-le en ligne!

-2 octets grâce à Brad Gilbert b2gills .

nwellnhof
la source
Vous n'avez pas besoin d'utiliser un suivi *, il n'est nécessaire que s'il y a quelque chose après .assuming(*,1).
Brad Gilbert b2gills
1

Attaché , 5 octets

Curry

Essayez-le en ligne!

Simple intégré, largement inintéressant. Mais, voici une version à partir de zéro:

Attaché, 35 octets

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

Explication:

{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
la source
1

Java 8, 46 + 318 = 364 octets

Il s'agit d'un lambda au curry (hah) prenant une fonction et un nombre d'arguments et retournant la fonction au 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;}}}

Essayez-le en ligne

Type de soumission

Fonction d'entrée

L'entrée de fonction est un objet avec une seule méthode (à l'exclusion des méthodes héritées) représentant la fonction. Notez qu'une interface fonctionnelle standard ne peut pas être utilisée comme type d'entrée car les fonctions de (par exemple) 3 paramètres doivent être prises en charge. Notez également qu'une expression lambda convertie en un type de java.util.function.Functiontype standard peut être transmise (la méthode unique est apply).

Les exceptions vérifiées peuvent être déclarées sur la fonction d'entrée, mais elles ne peuvent pas être levées (c'est-à-dire qu'elles ne seront pas propagées à l'appelant de la fonction de sortie). Cela est présumé acceptable car les interfaces fonctionnelles de Java ne permettent pas les exceptions vérifiées (et leur propagation empêcherait la soumission de renvoyer a Function). Les exceptions d'exécution (attribuables à RuntimeExceptionou Error) sont propagées.

Fonction de sortie

Le résultat de la soumission est a java.util.function.Function<Object, Object>. J'ai envisagé de renvoyer une plaine Objectavec une applyméthode (comme dans l'entrée), mais alors une réflexion serait nécessaire pour invoquer le résultat, ce qui semblait assez gênant pour être interdit - en particulier, appeler à fond ne serait plus possible en un seul expression.

Usage

Étant donné que la soumission renvoie une fonction de Objectà Object, la sortie peut être invoquée directement (avec apply), mais les valeurs de retour intermédiaires suivantes doivent être converties en un type approprié (par exemple java.util.function.Function<Object, Object>) avant d'être invoquées. Consultez le TIO pour quelques exemples d'utilisation.

Notez qu'en Java, les fonctions (c'est-à-dire les méthodes) ne sont pas des objets de première classe. Ainsi, la syntaxe utilisée dans la puce de sortie de la description du défi n'a pas de sens en Java. Plutôt que f(a1, a2, a3)ce que nous avons f.apply(a1, a2, a3)et plutôt que f(a1)(a2)(a3)ce que nous avons f.apply(a1).apply(a2).apply(a3).

Limites

Lorsqu'un résultat intermédiaire est appliqué (un argument ajouté), le résultat est en fait une copie mutée du résultat d'origine. Par exemple, dans cet extrait:

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 ligne 4 s'imprime 4, mais la ligne 5 échoue, car à ce moment c2contient déjà des arguments 2et 2(notez aussi cela c2 == c). Cela viole l'esprit du curry, mais répond aux exigences spécifiques énoncées dans le défi.

Non golfé

Voir le TIO pour une copie non golfée.

Jakob
la source
1

Julia 0,6 , 48 octets

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

Essayez-le en ligne!

Port de la réponse Python d'EricTheOutgolfer.

Sundar - Rétablir Monica
la source
0

APL (Dyalog Classic) , 58 57 octets

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

Essayez-le en ligne!

Syntaxe d'appel (avec fonction curry g, arguments à x1travers x3et nombre d'arguments n):

((n x1 f g) x2) x3

A besoin ⎕IO←1

Zacharý
la source