Mach mir was Curry

20

Eine Funktion f haben , die die Argumente x 1 , x 2 ,…, x n annimmt

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

- currying definiert f als eine Funktion neu, wobei ein einzelnes Argument a 1 verwendet wird, das einer weiteren Funktion zugeordnet ist. Diese Technik ist nützlich für eine teilweise Anwendung, zum Beispiel mit einer Curry- powFunktion, die wir schreiben könnten exp = pow(e).

Beispiel

Angenommen, wir haben die folgende Funktion f mit drei Argumenten ( f: X 1 × X 2 × X 3 → Y ):

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

Wenn wir diese Funktion aufrufen, erhalten wir f_curry: X 1 → (X 2 → (X 3 → Y)) . Wenn wir diese Funktion jetzt zweimal mit aufrufen f_curry(1)(2)würden, würden wir eine function ( h) erhalten, die dem folgenden Ergebnis entspricht:

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

Die Curry-Funktion fkönnte so geschrieben werden (Python 3):

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

Probieren Sie es online!

Herausforderung

Deine Herausforderung wird darin bestehen, eine Funktion wie oben beschrieben zu curry. Hier sind die Regeln:

  • Die Eingabe ist eine Blackbox-Funktion, die mindestens 2 Argumente akzeptiert
  • Die Eingabefunktion hat immer eine feste Anzahl von Argumenten (anders als printfoder ähnlich, Anmerkung: Sie müssen Funktionen mit einer beliebigen Anzahl von Argumenten ≥2 unterstützen)
  • Wenn Ihre Sprache standardmäßig Curry-Funktionen verwendet (z. B. Haskell), können Sie erwarten, dass die Eingabefunktion über N- Tupel definiert wird, anstatt über eine Funktion "höherer Ordnung".
  • Sie können die Anzahl der Argumente als Eingabe verwenden
  • Der Output ist das aktuelle Äquivalent des Inputs *
  • Sie können davon ausgehen, dass die Ausgabefunktion immer nur:
    • wird mit weniger oder gleich der Anzahl der Argumente aufgerufen, die die Eingabefunktion annimmt
    • mit Argumenten des richtigen Typs aufgerufen

* Dies würde für eine Eingabe fmit NArgumenten und für eine Ausgabe bedeuten, hdass für alle gültigen Argumente a1,…,aNdies gilt f(a1,a2,…,aN) == h(a1)(a2)…(aN).

ბიმო
quelle
Verwandte .
14.
also ist der eingang def f(a,b,c): return a + b * cund der ausgang ist def f_curry(a): def g_curry(b): def h(c): return a + b * c return h return g_curry?
DanielIndie
@DanielIndie: Wenn Sie dieses Beispiel nehmen, wäre die Eingabe f(die irgendwo definiert ist) und die Ausgabe sollte etwas Äquivalentes sein f_curry. Oder die Eingabe wäre lambda a,b,c: a+b*cund die Ausgabe eine äquivalente Funktion f_curry.
14.
Dies ist in den meisten statisch getippten Sprachen schwer zu bewerkstelligen ... Ich vermute, Sie benötigen dafür Typfunktionen.
Paŭlo Ebermann
@ PaŭloEbermann: Richtig, einige Sprachen können diese Aufgabe nicht lösen (beachten Sie das Tag Funktionsprogrammierung ). Einige statisch typisierte Sprachen können jedoch möglicherweise Funktionszeiger verwenden, die eine gültige E / A darstellen. Dies ist hauptsächlich der Grund, warum ich die Anzahl der Argumente als zusätzliche Eingabe zugelassen habe.
14.

Antworten:

11

JavaScript (ES6), 35 Byte

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

Probieren Sie es online!

Klingt nach einem Job für abhängige Typen! Vielleicht.


C ist eine currying Art Funktion. Bei einem Vektor der Typen a = [t 1 , t 2 ,… t n ] und einer Typfunktion T: HVect a → Type wird ein neuer Typ zurückgegeben:

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

Hier ist HVect der heterogene Vektortyp aus dem Idris-Präludium - der Typ von n- Tupeln, deren Elemente von n sind verschiedenen Typen sind.

c ist eine Funktion, nimmt ein und T als implizite Argumente, und dann eine wandelt uncurried Funktion fvom Typ ((b: HVect a) → T b) in eine curried eines vom Typ C einem T .

( C beschreibt einfach, was wir tun möchten; c tut es tatsächlich. Aber wir kommen nicht damit davon, C nicht zu definieren , da Idris verlangt, dass jede Definition der obersten Ebene eine Typensignatur hat.)


Der TIO-Link enthält ein Verwendungsbeispiel. Wenn wir eine Funktion für 3-Tupel (Nat, Nat, String) wie folgt definieren:

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

uncurried [3, 4, "th"]ergibt dann das gleiche Ergebnis wie c uncurried 3 4 "th". Idris schließt sich den Argumenten an a=[Nat, Nat, String]und T=const Stringfür uns glaube ich.

Ich habe diesen Code auf diesem Kern von timjb aufgebaut.

Lynn
quelle
Meiner Meinung nach sollten Tupel in Haskell und Idris eigentlich HVectstandardmäßig verwendet werden. Dies HVectist im Wesentlichen ein Tupel, das Sie dekomprimieren können.
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))

Probieren Sie es online!


Vorherige Version (97 Bytes)

-1 Byte dank @JayCE

digEmAll
quelle
Ich verstehe nicht, wie ich es grundlegend verkürzen soll. Sie können drei Bytes weggolfen, indem Sie die Klammern und das Leerzeichen am Ende der ersten Zeile entfernen. Und zwei weitere aufgrund der Konvention, den Namen der Funktion nicht in die Byteanzahl aufzunehmen. TIO
ngm
@ngm Der Funktionsname muss angegeben werden, wenn er rekursiv ist.
Ørjan Johansen
@ngm: Ich habe die if-Anweisung in die Unterfunktion
eingefügt
3

Python 2 , 60 Bytes

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

Probieren Sie es online!

Die Fußzeile ist ein Tester, der STDIN pro Zeile folgendermaßen verwendet:

  1. Die Funktion selbst
  2. Die Anzahl der Argumente der Funktion, ≥2
  3. Eine Liste der Argumente ( [a,b,...])

Beachten Sie, dass, während eine Liste der Argumente als Eingabe im Tester angegeben wird, in der Realität das Curry-Äquivalent der Liste vorangestellt wird und die Liste durch Funktionsaufruf reduziert wird.

Eine ähnliche 55-Byte-Version wurde freundlicherweise von ovs bereitgestellt :

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

Probieren Sie es online!

Erik der Outgolfer
quelle
2

Blumenkohl , 84 Bytes

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

Probieren Sie es online!

Nur ASCII
quelle
1
Mmm, Blumenkohl-Curry. Köstlich. ^ _ ^
DLosc
@ DLosc Es gibt nicht genug Antworten auf diese Herausforderung in Sprachen mit lebensmittelbezogenen Namen: P (obwohl ich denke, die meisten von ihnen haben keine Funktionen)
Nur ASCII
2

Perl 6 , 42 40 Bytes

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

Probieren Sie es online!

-2 Bytes dank Brad Gilbert b2gills .

nwellnhof
quelle
Sie müssen kein Trailing verwenden *, es ist nur notwendig, wenn etwas danach steht .assuming(*,1).
Brad Gilbert b2gills
1

Attache , 5 Bytes

Curry

Probieren Sie es online!

Einfach eingebaut, weitgehend uninteressant. Aber hier ist eine Version von Grund auf neu:

Attache, 35 Bytes

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

Erläuterung:

{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
quelle
1

Java 8, 46 + 318 = 364 Bytes

Dies ist ein Curry-Lambda (hah), das eine Funktion und eine Argumentanzahl annimmt und die Curry-Funktion zurückgibt.

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

Probieren Sie es online

Einreichungsart

Eingabefunktion

Die Funktionseingabe ist ein Objekt mit einer einzelnen Methode (ausgenommen geerbte Methoden), die die Funktion darstellt. Beachten Sie, dass eine Standardfunktionsschnittstelle nicht als Eingabetyp verwendet werden kann, da Funktionen von (z. B.) 3 Parametern unterstützt werden müssen. Beachten Sie auch, dass ein Lambda-Ausdruck, der in einen standardähnlichen java.util.function.FunctionTyp umgewandelt wurde, übergeben werden kann (die einzige Methode ist apply).

Überprüfte Ausnahmen können in der Eingabefunktion deklariert, aber nicht ausgelöst werden (dh sie werden nicht an den Aufrufer der Ausgabefunktion weitergegeben). Dies wird als akzeptabel angesehen, da die Funktionsschnittstellen von Java keine geprüften Ausnahmen zulassen (und deren Weitergabe die Übermittlung von a verhindern würde Function). Laufzeitausnahmen (zuweisbar auf RuntimeExceptionoderError ) werden weitergegeben.

Ausgangsfunktion

Die Ausgabe der Einreichung ist a java.util.function.Function<Object, Object>. Ich überlegte, eine Ebene Objectmit einemapply Methode (wie in der Eingabe) zurückzugeben, aber dann wäre eine Reflektion erforderlich, um das Ergebnis aufzurufen, was unpraktisch genug schien, um nicht zulässig zu sein - insbesondere, wenn in einer einzigen Methode kein vollständiger Abwärtsaufruf mehr möglich wäre Ausdruck.

Verwendung

Da die Übermittlung eine Funktion von Objectbis zurückgibt Object, kann die Ausgabe direkt (mit apply) aufgerufen werden , nachfolgende Zwischenrückgabewerte müssen jedoch in einen geeigneten Typ umgewandelt werden (z. B.java.util.function.Function<Object, Object> ) umgewandelt werden. Konsultieren Sie den TIO für einige Anwendungsbeispiele.

Beachten Sie, dass in Java Funktionen (dh Methoden) keine erstklassigen Objekte sind. Daher ist die im Ausgabe-Aufzählungszeichen der Challenge-Beschreibung verwendete Syntax in Java bedeutungslos. Eher als f(a1, a2, a3)wir f.apply(a1, a2, a3)und eher als f(a1)(a2)(a3)wir f.apply(a1).apply(a2).apply(a3).

Einschränkungen

Wenn ein Zwischenergebnis angewendet wird (ein Argument hinzugefügt), ist das Ergebnis tatsächlich eine mutierte Kopie des ursprünglichen Ergebnisses. Zum Beispiel in diesem Snippet:

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

Zeile 4 würde gedruckt 4, aber Zeile 5 würde fehlschlagen, da zu diesem Zeitpunkt c2bereits Argumente 2und 2(beachte auch das) enthalten sindc2 == c). This violates the spirit of currying, but meets the specific requirement stated in the challenge.

Ungolfed

Eine unbenutzte Kopie finden Sie beim TIO.

Jakob
quelle
1

Julia 0,6 , 48 Bytes

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

Probieren Sie es online!

Port von @ EricTheOutgolfers Python-Antwort.

Sundar - Setzen Sie Monica wieder ein
quelle
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

Try it online!

Calling syntax (with curried function g, arguments x1 through x3, and number of arguments n):

((n x1 f g) x2) x3

Requires ⎕IO←1

Zacharý
quelle