Fammi del curry


20

Avere una funzione f che accetta argomenti x 1 , x 2 ,…, x n

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

- curry ridefinisce f come funzione prendendo un singolo argomento a 1 che si associa a un'altra funzione. Questa tecnica è utile per un'applicazione parziale, ad esempio con una powfunzione al curry che potremmo scrivere exp = pow(e).

Esempio

Supponendo che abbiamo la seguente funzione f prendendo tre argomenti ( f: X 1 × X 2 × X 3 → Y ):

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

L'attivazione di questa funzione ci lascia con f_curry: X 1 → (X 2 → (X 3 → Y)) , se ora chiamassimo quella funzione due volte f_curry(1)(2)otterremmo una funzione ( h) equivalente alla seguente restituita:

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

La funzione al curry fpotrebbe essere scritta in questo modo (Python 3):

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

Provalo online!

Sfida

La tua sfida sarà quella di curry di una funzione come descritto sopra, ecco le regole:

  • L'input sarà una funzione blackbox che accetta almeno 2 argomenti
  • La funzione di input avrà sempre un numero fisso di argomenti (diverso printfo simile, nota: è necessario supportare funzioni con qualsiasi numero di argomenti ≥2)
  • Se la tua lingua utilizza funzioni curry per impostazione predefinita (ad es. Haskell), potresti aspettarti che la funzione di input sia definita su N -tuple, invece di una "funzione di ordine superiore"
  • Puoi prendere il numero di argomenti come input
  • L'output sarà l'equivalente al curry dell'ingresso *
  • Si può presumere che la funzione di output sarà sempre e solo:
    • chiamato con minore o uguale al numero di argomenti che accetta la funzione di input
    • chiamato con argomenti del tipo giusto

* Ciò significherebbe per un input fcon Nargomenti e un output hche per tutti gli argomenti validi a1,…,aNlo sostiene f(a1,a2,…,aN) == h(a1)(a2)…(aN).



quindi l'ingresso è def f(a,b,c): return a + b * ce l'uscita è def f_curry(a): def g_curry(b): def h(c): return a + b * c return h return g_curry?
DanielIndie,

@DanielIndie: se stai prendendo quell'esempio, l'input sarebbe f(che è definito da qualche parte) e l'output dovrebbe essere qualcosa di equivalente f_curry. O l'input sarebbe lambda a,b,c: a+b*ce l'output sarebbe una funzione equivalente f_curry.
ბიმო

Questo è difficile da fare nella maggior parte dei linguaggi tipicamente statici ... Immagino che tu abbia bisogno di funzioni di tipo per questo.
Paŭlo Ebermann,

@ PaŭloEbermann: vero, alcuni linguaggi non saranno in grado di risolvere questo compito (notare il tag di programmazione funzionale ). Tuttavia, alcuni linguaggi tipizzati staticamente potrebbero essere in grado di utilizzare i puntatori a funzione che sarebbero un I / O valido, questo è principalmente il motivo per cui ho permesso di prendere il numero di argomenti come input aggiuntivo.
ბიმო

Risposte:


11

JavaScript (ES6), 35 byte

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

9

Idris , 204 byte

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

Provalo online!

Sembra un lavoro per tipi dipendenti! Beh forse.


C è una funzione di tipo curry. Dato un vettore di tipi a = [t 1 , t 2 ,… t n ] e una funzione di tipo T: HVect a → Tipo , restituisce un nuovo tipo:

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

Qui, HVect è il tipo di vettore eterogeneo dal Preludio di Idris - il tipo di n -tuple i cui elementi sono di n tipi diversi.

c è una funzione che prende una e T come argomenti impliciti, e quindi converte una uncurried funzione fdi tipo ((b: HVect a) → T b) in un curry uno di tipo C a T .

( C descrive semplicemente ciò che desideriamo fare; c lo fa effettivamente. Ma non possiamo cavarcela con la non definizione di C , poiché Idris richiede che ogni definizione di livello superiore abbia una firma di tipo.)


Il collegamento TIO fornisce un esempio di utilizzo. Se definiamo una funzione su 3 tuple (Nat, Nat, String) come segue:

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

quindi uncurried [3, 4, "th"]produce lo stesso risultato di c uncurried 3 4 "th". Idris deduce gli argomenti a=[Nat, Nat, String]e, T=const Stringper noi, credo.

Ho basato questo codice su questa sintesi di timjb.


Secondo me, le tuple in Haskell e Idris dovrebbero essere effettivamente HVectdi default - HVectè essenzialmente una tupla che puoi annullare.
Esolanging Fruit,


5

R , 96 byte

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

Provalo online!


Versione precedente (97 byte)

-1 byte grazie a @JayCE


Non vedo come accorciarlo sostanzialmente . Puoi giocare a golf di tre byte eliminando le parentesi graffe e lo spazio alla fine della prima riga. E altri due a causa della convenzione qui di non includere il nome della funzione nel conteggio dei byte. TIO
ngm,

@ngm Il nome della funzione deve essere incluso quando è ricorsivo.
Ørjan Johansen,

@ngm: ho inserito l'istruzione if all'interno della
sottofunzione


3

Python 2 , 60 byte

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

Provalo online!

Il piè di pagina è un tester che utilizza STDIN nel modo seguente per riga:

  1. La stessa funzione
  2. Il numero degli argomenti della funzione, ≥2
  3. Un elenco degli argomenti ( [a,b,...])

Si noti che, mentre un elenco degli argomenti viene fornito come input nel tester, in realtà l'equivalente al curry viene anteposto all'elenco e l'elenco viene ridotto dalla chiamata di funzione.

Una versione simile a 55 byte è stata gentilmente fornita da ovs :

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

Provalo online!


2

Cavolfiore , 84 byte

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

Provalo online!


1
Mmm, curry di cavolfiore. Delizioso. ^ _ ^
DLosc,

@DLosc non ci sono abbastanza risposte a questa sfida in lingue con nomi legati al cibo: P (anche se immagino che la maggior parte di loro non abbia effettivamente funzioni)
ASCII



1

Attache , 5 byte

Curry

Provalo online!

Semplice da installare, in gran parte poco interessante. Ma, ecco una versione da zero:

Attache, 35 byte

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

Spiegazione:

{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 byte

Questo è un lambda al curry (hah) che prende una funzione e un argomento conta e restituisce la funzione 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;}}}

Provalo online

Tipo di presentazione

Funzione di input

L'input della funzione è un oggetto con un singolo metodo (esclusi i metodi ereditati) che rappresenta la funzione. Si noti che non è possibile utilizzare un'interfaccia funzionale standard come tipo di ingresso poiché devono essere supportate le funzioni di (ad esempio) 3 parametri. Si noti inoltre che java.util.function.Functionpuò essere passata un'espressione lambda a un tipo simile allo standard (il metodo singolo è apply).

Le eccezioni verificate possono essere dichiarate sulla funzione di input, ma potrebbero non essere generate (ovvero non verranno propagate al chiamante della funzione di output). Si presume che ciò sia accettabile perché le interfacce funzionali di Java non consentono eccezioni verificate (e la loro propagazione impedirebbe che l'invio restituisca a Function). Le eccezioni di runtime (assegnabili a RuntimeExceptiono Error) vengono propagate.

Funzione di uscita

L'output dell'invio è a java.util.function.Function<Object, Object>. Ho preso in considerazione l'idea di restituire una pianura Objectcon un applymetodo (come nell'input), ma poi sarebbe necessario riflettere per invocare il risultato, che sembrava abbastanza scomodo da non consentire - in particolare, non sarebbe più possibile chiamare fino in fondo in un singolo espressione.

uso

Poiché l'invio restituisce una funzione da Objecta Object, l'output può essere richiamato direttamente (con apply), ma i valori di ritorno intermedi successivi devono essere trasmessi a un tipo appropriato (ad esempio java.util.function.Function<Object, Object>) prima di essere richiamati. Consultare il TIO per alcuni esempi di utilizzo.

Si noti che nelle funzioni Java (ovvero i metodi) non sono oggetti di prima classe. Pertanto la sintassi utilizzata nel proiettile di output della descrizione della sfida non ha senso in Java. Piuttosto che f(a1, a2, a3)abbiamo f.apply(a1, a2, a3), e piuttosto che f(a1)(a2)(a3)abbiamo f.apply(a1).apply(a2).apply(a3).

limitazioni

Quando viene applicato un risultato intermedio (aggiunto un argomento), il risultato è in realtà una copia mutata del risultato originale. Ad esempio, in questo frammento:

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 riga 4 verrebbe stampata 4, ma la riga 5 fallirebbe, perché a quel punto c2già contiene argomenti 2e 2(nota anche quello c2 == c). Ciò viola lo spirito di curry, ma soddisfa i requisiti specifici indicati nella sfida.

Ungolfed

Vedi il TIO per una copia non giocata.



0

APL (Dyalog Classic) , 58 57 byte

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

Provalo online!

Chiamando la sintassi (con funzione di curry g, gli argomenti x1attraverso x3, e il numero di argomenti n):

((n x1 f g) x2) x3

Richiede ⎕IO←1

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.