Zrób mi curry


20

Posiadanie funkcji f, która przyjmuje argumenty x 1 , x 2 ,…, x n

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

- curry redefiniuje f jako funkcję przyjmującą pojedynczy argument a 1, który odwzorowuje na jeszcze jedną funkcję. Ta technika jest przydatna do częściowego zastosowania, na przykład z powfunkcją curry, którą moglibyśmy napisać exp = pow(e).

Przykład

Zakładając, że mamy następującą funkcję f przyjmującą trzy argumenty ( f: X 1 × X 2 × X 3 → Y ):

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

Curry tej funkcji pozostawia nam f_curry: X 1 → (X 2 → (X 3 → Y)) , jeśli teraz wywołalibyśmy tę funkcję dwa razy f_curry(1)(2), otrzymalibyśmy funkcję ( h) równoważną z następującym:

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

Funkcję curry fmożna zapisać w następujący sposób (Python 3):

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

Wypróbuj online!

Wyzwanie

Twoim wyzwaniem będzie wywołanie funkcji w sposób opisany powyżej, oto zasady:

  • Wejście będzie funkcją blackboksa, która przyjmuje co najmniej 2 argumenty
  • Funkcja wejściowa zawsze będzie miała stałą liczbę argumentów (w przeciwieństwie do printfpodobnych, uwaga: musisz obsługiwać funkcje z dowolną liczbą argumentów ≥2)
  • Jeśli twój język domyślnie korzysta z funkcji curry (np. Haskell), możesz oczekiwać, że funkcja wejściowa zostanie zdefiniowana w liczbie N -zamiast zamiast „funkcji wyższego rzędu”
  • Jako argument można przyjąć liczbę argumentów
  • Dane wyjściowe będą curry równoważnikiem *
  • Możesz założyć, że funkcją wyjściową będzie zawsze:
    • wywoływany z mniejszą lub równą liczbą argumentów pobieranych przez funkcję wejściową
    • wywoływany z argumentami odpowiedniego typu

* Oznaczałoby to dla wejścia fz Nargumentami i mocy h, że dla wszystkich ważnych argumentów a1,…,aNjest warunek f(a1,a2,…,aN) == h(a1)(a2)…(aN).



więc wejście jest def f(a,b,c): return a + b * ci wyjście jest def f_curry(a): def g_curry(b): def h(c): return a + b * c return h return g_curry?
DanielIndie

@DanielIndie: Jeśli weźmiesz ten przykład, dane wejściowe byłyby f(które gdzieś zdefiniowane), a dane wyjściowe powinny być czymś równoważnym f_curry. Albo wejście byłoby, lambda a,b,c: a+b*ca wyjście funkcją równoważną f_curry.
ბიმო

Jest to trudne w większości statycznie typowanych języków ... Chyba potrzebujesz do tego funkcji typu.
Paŭlo Ebermann

@ PaŭloEbermann: To prawda, że ​​niektóre języki nie będą w stanie rozwiązać tego zadania (zwróć uwagę na znacznik programowanie funkcjonalne ). Jednak niektóre języki o typie statycznym mogą być w stanie korzystać ze wskaźników funkcji, które byłyby prawidłowymi operacjami wejścia / wyjścia, dlatego głównie zezwoliłem na przyjmowanie liczby argumentów jako dodatkowych danych wejściowych.
ბიმო

Odpowiedzi:


11

JavaScript (ES6), 35 bajtów

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

9

Idris , 204 bajty

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

Wypróbuj online!

Brzmi jak praca dla typów zależnych! Być może.


C jest funkcją typu curry. Biorąc pod uwagę wektor typów a = [t 1 , t 2 ,… t n ] i funkcję typu T: HVect a → Type , zwraca nowy typ:

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

Tutaj HVect jest heterogenicznym typem wektora z Idris Prelude - typ n pary, której elementy są n różnych typów.

C jest funkcją, która przyjmuje się i T kalkulacyjne argumentów, a następnie przekształca uncurried funkcję ftypu ((B: HVect a) → Tb) w curry jednego typu C T .

( C po prostu opisuje to, co chcemy zrobić; c faktycznie to robi. Ale nie możemy uciec od braku definicji C , ponieważ Idris wymaga, aby każda definicja najwyższego poziomu miała podpis typu).


Łącze TIO podaje przykład użycia. Jeśli zdefiniujemy funkcję dla 3-krotek (Nat, Nat, String) w następujący sposób:

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

następnie uncurried [3, 4, "th"]daje taki sam wynik jak c uncurried 3 4 "th". Idris wysuwa argumenty a=[Nat, Nat, String]i T=const String, jak sądzę, dla nas.

Oparłem ten kod na tej treści autorstwa timjb.


Moim zdaniem krotki w Haskell i Idris powinny faktycznie być HVectdomyślnie - HVectjest to w zasadzie krotka, którą można rozszyfrować.
Esolanging Fruit


5

R , 96 bajtów

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

Wypróbuj online!


Poprzednia wersja (97 bajtów)

-1 bajt dzięki @JayCE


Nie rozumiem, jak to zasadniczo skrócić. Możesz zagrać w golfa o trzy bajty, pozbywając się nawiasów klamrowych i miejsca na końcu pierwszej linii. I jeszcze dwa ze względu na konwencję o nie włączaniu nazwy funkcji do liczby bajtów. TIO
ngm

@ngm Nazwa funkcji musi być uwzględniona, gdy jest rekurencyjna.
Ørjan Johansen

@ngm: Wstawiam instrukcję if do podfunkcji, oszczędzając jedną dziesiątą bajtów :)
digEmAll


3

Python 2 , 60 bajtów

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

Wypróbuj online!

Stopka to tester, który używa STDIN w następujący sposób w wierszu:

  1. Sama funkcja
  2. Liczba argumentów funkcji, ≥2
  3. Lista argumentów ( [a,b,...])

Zauważ, że chociaż lista argumentów jest podawana jako dane wejściowe w testerze, w rzeczywistości curry ekwiwalent jest dodawany do listy, a lista jest zmniejszana przez wywołanie funkcji.

Podobna 55-bajtowa wersja została uprzejmie dostarczona przez ovs :

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

Wypróbuj online!


2

Kalafior , 84 bajtów

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

Wypróbuj online!


1
Mmm, curry z kalafiora. Pyszne. ^ _ ^
DLosc

@DLosc nie ma wystarczającej liczby odpowiedzi na to wyzwanie w językach z nazwami związanymi z jedzeniem: P (chociaż chyba większość z nich nie ma funkcji)
tylko ASCII



1

Attache , 5 bajtów

Curry

Wypróbuj online!

Prosty wbudowany, w dużej mierze nieciekawy. Ale oto wersja od podstaw:

Attache, 35 bajtów

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

Wyjaśnienie:

{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 bajtów

Jest to lambda curry (hah) przyjmująca funkcję i argument liczący i zwracająca funkcję curled.

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

Wypróbuj online

Rodzaj zgłoszenia

Funkcja wprowadzania

Dane wejściowe funkcji to obiekt z jedną metodą (wyłączając metody dziedziczone) reprezentującą funkcję. Należy pamiętać, że standardowy typ funkcjonalny interfejs nie może być używany jako typ danych wejściowych, ponieważ muszą być obsługiwane funkcje (np.) 3 parametrów. Należy również zauważyć, że wyrażenie lambda rzutowane na java.util.function.Functiontyp podobny do standardowego może zostać przekazane (metoda pojedyncza to apply).

Sprawdzone wyjątki mogą zostać zadeklarowane w funkcji wejściowej, ale nie mogą zostać wyrzucone (tzn. Nie zostaną propagowane do osoby wywołującej funkcję wyjściową). Przyjmuje się, że jest to do przyjęcia, ponieważ interfejsy funkcjonalne Javy nie zezwalają na sprawdzone wyjątki (a ich rozpowszechnienie zapobiegnie zwrotowi a Function). Propagowane są wyjątki czasu wykonywania (przypisywane do RuntimeExceptionlub Error).

Funkcja wyjściowa

Wynik przedłożenia jest java.util.function.Function<Object, Object>. Zastanawiałem się nad zwróceniem zwykłego Objectza pomocą applymetody (jak na wejściu), ale wtedy konieczne będzie odbicie, aby wywołać wynik, który wydawał się wystarczająco niewygodny, aby był niedopuszczalny - w szczególności wywołanie do końca nie byłoby możliwe w jednym wyrażenie.

Stosowanie

Ponieważ przesyłanie zwraca funkcję od Objectdo Object, dane wyjściowe mogą być wywoływane bezpośrednio (za pomocą apply), ale kolejne pośrednie wartości zwracane muszą być rzutowane na odpowiedni typ (np. java.util.function.Function<Object, Object>) Przed wywołaniem. Zobacz przykłady użycia w TIO.

Zauważ, że funkcje Java (tj. Metody) nie są obiektami pierwszej klasy. W związku z tym składnia użyta w wypunkcie wyjściowym opisu wyzwania nie ma znaczenia w Javie. Zamiast f(a1, a2, a3)tego mamy f.apply(a1, a2, a3), a nie f(a1)(a2)(a3)mamy f.apply(a1).apply(a2).apply(a3).

Ograniczenia

Gdy zastosowany zostanie wynik pośredni (dodany argument), wynik jest faktycznie zmutowaną kopią oryginalnego wyniku. Na przykład w tym fragmencie:

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

linia 4 zostanie wydrukowana 4, ale linia 5 nie powiedzie się, ponieważ do tego czasu c2już zawiera argumenty 2i 2(zauważ też, że c2 == c). Narusza to ducha curry, ale spełnia określone wymagania określone w wyzwaniu.

Nie golfił

Zobacz TIO, aby uzyskać nie golfową kopię.



Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.