Wdrożenie funkcjonalnych paradygmatów programowania


21

Twoja firma dopiero zaczyna projekt i po raz pierwszy zdecydowałaś się na funkcjonalny styl programowania. Jednak twój szef jest naprawdę nieufny i nie chce korzystać z wbudowanych funkcji, i wymaga od ciebie wdrożenia głównych funkcji. W szczególności trzeba napisać funkcje: Map, Nest, Apply, Range, Foldi Tablew języku Twojego wyboru. Szef jest bardzo zajętym człowiekiem i chce, aby programy były jak najkrótsze, aby nie tracił czasu na czytanie. On również nie chce, abyś używał pętli, dlatego będziesz miał 10% zmniejszenie liczby bajtów za nieużywanie pętli.

Szczegółowe wymagania dotyczące funkcji są poniżej:

Mapa

MapFunkcja przyjmuje dwa parametry: fa listgdzie fjest funkcją i listznajduje się lista wartości. Powinien zwrócić fzastosowany do każdego elementu list. Dlatego będzie działał jako taki:

Map(f,{a,b,c})

zwraca

{ f(a), f(b), f(c) }

i

Map(f, {{a,b},{b,c}})

zwraca

{ f({a,b}), f({b,c})}

Gniazdo

NestFunkcja przyjmuje trzy parametry, a także: f, arg, timesgdzie fjest funkcją, argjest jej argument wyjściowy, i timesto ile razy funkcja jest stosowana. Powinien zwrócić wyrażenie z fzastosowanymi timesczasami do arg. Dlatego będzie działał jako taki:

Nest(f, x, 3)

zwraca

f(f(f(x)))

i

Nest(f, {a,b}, 3)

zwraca

f(f(f({a,b})))

Zastosować

ApplyFunkcja przyjmuje dwa parametry: fa argsgdzie fjest funkcją i argslista. Powinien mieć zastosowanie fdo args. W związku z tym:

Apply(f, {a,b,c})

zwraca

f(a,b,c)

Zasięg

RangeFunkcja ma jedną liczbą całkowitą ri wysyła całkowitymi do tej liczby. W związku z tym:

Range(5)

zwraca

{ 1, 2, 3, 4, 5}

Zagięcie

FoldFunkcja przyjmuje trzy parametry f, arg, othersgdzie fjest funkcją, argjest prosty parametr, a otherslista. Będzie działał jako taki:

Fold(f, x, {a, b, c, d})

zwraca

f(f(f(f(x,a),b),c),d)

Stół

Funkcje tabel powinny przyjmować funkcję fi parametr wywoływany iteratorw postaci: {iMin, iMax}gdzie iMini iMaxsą liczbami całkowitymi. Należy złożyć wniosek fw określonym zakresie. W związku z tym:

Table(f, {0, 5})

zwraca

{f(0), f(1), f(2), f(3), f(4), f(5)}

Użyłem definicji tych funkcji ze strony programowania funkcjonalnego Mathematica , więc jeśli potrzebujesz więcej wskazówek, przejdź tam. Pamiętaj, że nie będziesz musiał implementować wszystkich wersji funkcji pokazanych na tej stronie, ale tylko te napisane w tym poście.

Standardowe luki są niedozwolone jak zwykle.

Jeśli twój język nie pozwala na przekazywanie funkcji jako argumentów, musisz zaimplementować tę możliwość i dodać ją do swojej odpowiedzi. Jednak liczba bajtów tej operacji nie zostanie dodana do sumy.

To jest kod golfowy, więc wygrywa najkrótszy kod. Powodzenia!!!


To jest niesamowite! +1 Jednak tak naprawdę nie rozumiem, jak Tabletu działa. Czy twoim przykładem powinien być Table(f, {x, 0, 5})? Nie rozumiem też w ogóle celu x, ponieważ po prostu stosuje funkcję do zakresu.
kirbyfan64sos

@ kirbyfan64sos Dziękujemy! Tak, to była literówka. Zostawiłem x jako odniesienie do matematyki, która używa go jako symbolicznej sztuczki, jednak myślę, że mogę to wyciągnąć
WizardOfMenlo

Jeszcze jedno pytanie: jak nazywamy funkcje? Czy musimy nadać im dokładnie takie same nazwy? Pojedyncza litera?
kirbyfan64sos

@ kirbyfan64sos Ponieważ jest to golf golfowy, pozwolę na nazwy jednoliterowe, jednak w odpowiedzi umieść nagłówek nad każdą funkcją, abyśmy wiedzieli, która to jest. Nie używaj także kolidujących liter.
WizardOfMenlo

Czy możesz bardziej szczegółowo określić, co liczy się jako pętla?
xnor 24.09.15

Odpowiedzi:


9

Haskell, wiele poprzednich bajtów liczy 127 * 0,9 = 114,3 bajtów

f#(a:b)=f a:f#b;f#x=x
(f&x)0=x;(f&x)i=f$f&x$i-1
i=id
r x=i%(1,x)
(g?x)(a:b)=g(g?x$b)a;(g?x)y=x
f%(a,b)|a>b=[]|1<2=f a:f%(a+1,b)

Żadnych pętli, tylko rekurencja.

#to mapa: (*2) # [1,2,3]->[2,4,6]

&jest gniazdem: ((*2) & 3) 4->48

istosuje się: i (*2) 7->14

rto zakres: r 4->[1,2,3,4]

?jest krotnie: ((+) ? 0) [1,2,3,4]->10

%to tabela: (*2) % (2,4)->[4,6,8]

Zgodnie z prośbą wersja bez golfa z komentarzami. Uwaga, &i ?są to trójskładnikowe operatory infix, które wymagają dodatkowych nawiasów, gdy są wywoływane lub pasują do wzorca.

f # (a:b) = f a : f#b        -- map on a list (a->head, b->tail) is f a in front of mapping f to b
f # x     = x                -- map on the empty list is the empty list
                             -- (non empty lists are caught in the line before) 

(f & x) 0 = x                -- nesting zero times is x
(f & x) i = f $ f&x $ i-1    -- nesting i times is f (nesting one time less)

i=id                         -- apply in Haskell is just the identity function 

r x = i % (1,x)              -- defined via the "table" of the identity function from 1 to x

(g ? x) (a:b) = g (g?x$b) a  -- folding g into a list (a->head, b->tail) is g applied to (folding g into b) and a
(g ? x) y     = x             -- folding the empty list is x
                             --  again, y must be the empty list, else it would have been handled by the previous line

f % (a,b)                    
  |a>b       = []                -- if iMin is greater than iMax, the table is empty
  |otherwise = f a : f%(a+1,b)   --  otherwise f a in front of the table with iMin increased by one

Podziękowania dla @dfeuer i @Zgarb za przydatne wskazówki


Jestem nowym użytkownikiem Haskell, wygląda całkiem nieźle, jednak czy mógłbyś dodać wyjaśnienie tego, co robisz?
WizardOfMenlo

1
@WizardOfMenlo: dodał kilka komentarzy
nimi

Właśnie zdałem sobie sprawę, jak elegancki jest Haskell, naprawdę dobrze!
WizardOfMenlo

1
Ignorując nieskończone list i efektywności, m#q=reverse$f((:).m)[]q. Jest to ta sama długość co twoja, ale znacznie trudniejsza do odczytania.
dfeuer

Można skrócić !przez co zamiast nazwy operatora: i f=f.
dfeuer

5

Python 2, 305,1 bajtów (-10% 376 369 366 349 339 bajtów)

exec'e=eval;q=len;m=@,l:e("["+"f(l.pop()),"*q(l)+"][::-1]");n=@,x,l:e("f("*l+"*x"+")"*l);r=@:e("r(f-1)+"*(f>1)+"[f]");a=@,a:e("f(a["+`r(q(a))`[1:-1]$",","-1],a[")+"-1])");f=@,x,l:e("f("*q(l)+"x,["+`r(q(l))`[1:-1]$",","-1]),l[")+"-1])");t=@,n,x:e("[f("+`r(x)[n-1:]`$",","),f(")[1:-1]+")]")'.replace("@","lambda f").replace("$",".replace(")

Po rozwinięciu odpowiada:

e=eval;q=len
m=lambda f,l:e("["+"f(l.pop()),"*q(l)+"][::-1]")
n=lambda f,x,l:e("f("*l+"*x"+")"*l)
r=lambda i:e("r(i-1)+"*(i>1)+"[i]")
a=lambda f,a:e("f(a["+`r(q(a))`[1:-1].replace(",","-1],a[")+"-1])")
f=lambda f,x,l:e("f("*q(l)+"x,["+`r(q(l))`[1:-1].replace(",","-1]),l[")+"-1])")
t=lambda f,n,x:e("[f("+`r(x)[n-1:]`.replace(",","),f(")[1:-1]+")]")

Bez pętli!

Cóż, robi to dużo evalingerencji, a jeśli twój szef nie wytrzyma pętli, NIENAWIDZI ewaluacji. Ale będą musieli to znieść

rangeDoceniany jest sposób działania w lambda, więc nie muszę wykonywać żadnych funkcji (wzdrygnąć się).

Objaśnienia:

  • m=lambda f,l:eval("["+"f(l.pop()),"*len(l)+"][::-1]")
    • Utwórz ciąg, który wyskakuje z listy, zawiń go w listę, odwróć i na końcu ewaluuj!
  • n=lambda f,x,l:eval("f("*l+"*x"+")"*l)
    • Ręcznie utwórz łańcuch za pomocą zagnieżdżenia i sprawdź go!
  • r=lambda i:e("r(i-1)+"*(i>1)+"[i]")
    • Utwórz ciąg, który po evaledycji zwraca [0]lub używa rekurencji, aby uzyskać poprzednie wyniki i dodaje bieżący indeks do listy. Ewaluuje to.
  • a=lambda f,a:eval("f(a["+r (len (a))[1:-1].replace(",","-1],a[")+"-1])")
    • Używa funkcji zasięgu, aby uzyskać indeksy 1-len (lista). Zastępuje przecinki na liście uszeregowane sposobem uzyskania poprawnego indeksu listy a. Ewaluuje to!
  • f=lambda f,x,l:eval("f("*len(l)+"x,["+r (len (l))[1:-1].replace(",","-1]),l[")+"-1])")
    • To samo, co zastosowanie, z tym że zastępuje przecinki zamykającymi nawiasami klamrowymi, przecinkami i uruchamianiem indeksu listy.
  • t=lambda f,n,x:eval("[f("+r (x) [n-1:].replace(",","),f(")[1:-1]+")]")
    • To samo, co zastosuj i spasuj, z wyjątkiem zastępowania zakończeniem funkcji i wywołaniem nowej. Ewaluuje to!

Mapa, gniazdo, zasięg, zastosowanie, złożenie, tabela.

Dzięki @Zgarb za lambda za zasięg!


Mój szef będzie miał głowę na biurku :) Czy mógłbyś również dodać krótkie wyjaśnienie?
WizardOfMenlo

Jak o r=lambda i:[]if i<1 else r(i-1)+[i]? Żadnych pętli, tylko rekurencja.
Zgarb

1
Jasne, wezmę to na razie, ale szef potrzebuje więcej, evalaby pokazać im, że pętle nie są takie złe :)
Blue

Ha! Inna wersja używająca e=eval:r=lambda i:e("r(i-1)+"*(i>1)+"[i]")
Zgarb,

czy możesz to zmienić z 60% premii na 10%, proszę? Poprawiłem specyfikację pytania, aby było bardziej sprawiedliwe
WizardOfMenlo

5

JavaScript ES6, 197 * 0,9 = 177,3 bajtów

M=(f,l)=>F((a,b)=>[...a,f(b)],[],l)
N=(f,x,n)=>f(--n?N(f,x,n):x)
A=(f,l)=>f(...l)
R=n=>n--?[...R(n),n+1]:[]
F=(f,x,l,n=l.length)=>n--?f(F(f,x,l,n),l[n]):x
T=(f,i)=>([n,x]=i,M(q=>f(q+n-1),R(x-n+1)))

Mapa ( M=(f,l)=>F((a,b)=>[...a,f(b)],[],l)):

Używa opcji Fold, aby połączyć wyniki fzastosowane do każdego członka z lpustej listy. Korzystanie z wbudowanych funkcji ogranicza to do M=(f,l)=>l.map(f)(nie korzystałeś z niego, ponieważ wydaje się tani ...?).

Nest ( N=(f,x,n)=>f(--n?N(f,x,n):x)):

Zastosuj frekurencyjnie, aż nzmniejszy się do 0.

Apply ( A=(f,l)=>f(...l)):

Używa spread ( ...) operator może zastosować lna f.

Zakres ( R=n=>n--?[...R(n),n+1]:[]):

Łączenie nz rekurencyjnym wywołaniem zakresu, dopóki nie nzostanie zmniejszone do 0.

Fold ( F=(f,x,l,n=l.length)=>n--?f(F(f,x,l,n),l[n]):x):

Stosuje rekurencyjne wywołanie Fold i n„th elementu ldo faż” njest zmniejszane do 0. Użycie wbudowanych funkcji zmniejsza to F=(f,x,l)=>l.reduce(f,x)(ponownie, wydawało się tanie…).

Tabela ( T=(f,i)=>([n,x]=i,M(q=>f(q+n-1),R(x-n+1)))):

Pierwsze inicjuje ni xdo Imin i IMAX pomocą rozpad ( [n,x]=i), a następnie wykorzystuje zakres skonstruować tabelę wartości z Imin Imax. fjest następnie nakładany na tabelę za pomocą mapy, a wynik jest zwracany.


Chcesz poznać moją filozofię? „Jeśli jest tani, kup go”. W specyfikacji nie powiedziano, że nie możesz używać wbudowanych (jeszcze), więc używaj ich!
Mama Fun Roll

4

Python 3, 218 bajtów

Nieczytelna wersja:

exec("P!:[f(_)for _ in x];Y!,z:Y(f,f(x),z-1)if z else x;T!:f(*x);H!=0:(H(f-1)if~-f else[])+[f];O!,z:O(f,f(x,z[0]),z[1:])if z else x;N!:(N(f,[x[0],x[1]-1])if x[1]-x[0]else[])+[f(x[1])]".replace("!","=lambda f,x"))

(Więcej) czytelna wersja:

P=lambda f,x:[f(_)for _ in x]
Y=lambda f,x,z:Y(f,f(x),z-1)if z else x
T=lambda f,x:f(*x)
H=lambda f,x=0:(H(f-1)if~-f else[])+[f]
O=lambda f,x,z:O(f,f(x,z[0]),z[1:])if z else x
N=lambda f,x:(N(f,[x[0],x[1]-1])if x[1]-x[0]else[])+[f(x[1])]

Będzie to jedna lambda na raz:

Funkcja mapy P

P=lambda f,x:[f(_)for _ in x]
Po prostu prosty iterator. Nie ma tu wiele do powiedzenia.

Funkcja gniazda Y

Y=lambda f,x,z:Y(f,f(x),z-1)if z else x
Powtarzanie aż do zzera, zastosowanie za fkażdym razem. Na końcu klauzula if wydaje się niezgrabna; być może istnieje lepszy sposób na zakończenie rekurencji.

Zastosuj funkcję T

T=lambda f,x:f(*x)
Python ma fajnego operatora ekspansji, który wykonałby dla mnie wszystkie ciężkie zadania.

Funkcja zakresu H

H=lambda f,x=0:(H(f-1)if~-f else[])+[f]
Ten był trudniejszy niż się spodziewałem. Skończyło się na rekurencyjnym podejściu. Ponownie, konstrukcja if-else zajmuje dużo bajtów i uważam, że można ją poprawić. Dlaczego ma manekina x=0, pytasz? Jest tak, że kiedy go skompresuję exec, mogę go wymienić =lambda f,xzamiast po prostu =lambda f.

Funkcja składania O

O=lambda f,x,z:O(f,f(x,z[0]),z[1:])if z else x
Całkiem zadowolony z tego. Po prostu wycina pierwszy element tablicy za każdym razem, gdy się powtórzy, dopóki nic nie pozostanie.

Funkcja tabeli N

N=lambda f,x:(N(f,[x[0],x[1]-1])if x[1]-x[0]else[])+[f(x[1])]
Ten jest okropny i jestem pewien, że jest miejsce na ulepszenia. Próbowałem użyć funkcji zakresu i mapy zdefiniowanych wcześniej dla pewnego map(f,range(x,y))rodzaju konstrukcji, ale bez większego powodzenia. Skończyło się na strasznym rekurencyjnym podejściu, które dzieli pewne podobieństwo do funkcji zakresu.

Wszystkie lambdas są opakowane w sposób execz replaceskrócić liczbę bajtów znacząco.


Już miałem skomentować, [f(_)for _ in x]co można skrócić map(f,x), ale potem przypomniałem sobie, jakie to wyzwanie
Cyoce,

4

Julia, 181 bajtów

Brak premii dla mnie; Użyłem pętli swobodnie. Przepraszam szefie, ale pętle w Julii są skuteczne!

M(f,x)=[f(i...)for i=x]
N(f,x,n)=(for i=1:n x=f(x...)end;x)
A(f,x)=f(x...)
R(n)=(i=0;x={};while i<n push!(x,i+=1)end;x)
F(f,x,a)=(for b=a x=f(x,b)end;x)
T(f,i)=[f(j)for j=i[1]:i[2]]

Dodanie elips po argumencie do funkcji powoduje rozbicie tablicy, krotki lub innych elementów na zwykłe argumenty funkcji. W przeciwnym razie funkcja pomyśli, że próbujesz przekazać tablicę (lub krotkę itp.). Nie ma to wpływu na pojedyncze argumenty.

Nazwy funkcji:

  • Mapa: M
  • Gniazdo: N
  • Zastosować: A
  • Zasięg: R
  • Zagięcie: F
  • Stół: T

4

tinylisp , 325 * 0,9 = 292,5

Język jest nowszy od pytania, ale i tak nie wygra.

(d @(q(a a)))(d Q(q((l)(i l(c(@(q q)(h l))(Q(t l)))l))))(d A(q((f a)(v(c(q f)(Q a))))))(d M(q((f l)(i l(c(A f(@(h l)))(M f(t l)))l))))(d N(q((f a x)(i x(A f(@(N f a(s x 1))))a))))(d ,(q((m a b)(i(l b a)m(,(c b m)a(s b 1))))))(d R(q((a)(,()1 a))))(d T(q((f r)(M f(A ,(c()r))))))(d F(q((f a l)(i l(F f(A f(@ a(h l)))(t l))a))))

Definiuje funkcje A(zastosuj), M(mapa), N(gniazdo), R(zakres), T(tabela) i F(złóż) wraz z kilkoma funkcjami pomocniczymi. Toczekuje listy dwóch liczb całkowitych jako drugiego argumentu.

Tinylisp nie ma nawet żadnych konstrukcji pętli; wszystko odbywa się za pomocą rekurencji. Niektóre z tych funkcji nie są rekurencyjne , więc jeśli wywołasz je na dużych listach, prawdopodobnie zepsują stos wywołań. Można je wszystkie zaimplementować za pomocą rekurencji ogona ... ale zajmie to więcej bajtów, a to jest golf golfowy.

Oto rozszerzona wersja z białymi spacjami i prawdziwymi słowami nazw, które powinny być dość czytelne, jeśli znasz Lisp. (Aliasowałem większość wbudowanych funkcji Tinylisp z wyjątkiem q(quote) i i(if).)

(d define d)
(define cons c)
(define car h)
(define cdr t)
(define subtract s)
(define less l)
(define eval v)

(define lambda
  (q (()
      (arglist expr)
      (list arglist expr))))

(define list (lambda args args))

(define quote-all
  (lambda (lyst)
    (i lyst
       (cons
         (list (q q) (car lyst))
         (quote-all (cdr lyst)))
       lyst)))

(define Apply
  (lambda (func arglist)
    (eval (cons (q func) (quote-all arglist)))))

(define Map
  (lambda (func lyst)
    (i lyst
       (cons
         (Apply func (list (car lyst)))
         (Map func (cdr lyst)))
       lyst)))

(define Nest
  (lambda (func arg times)
    (i times
       (Apply func
              (list (Nest func arg (subtract times 1))))
       arg)))

(define range*
  (lambda (accumulator a b)
    (i (less b a)
       accumulator
       (range* (cons b accumulator) a (subtract b 1)))))

(define Range
  (lambda (x)
    (range* 1 x)))

(define Table
  (lambda (func iterator)
    (Map func
         (Apply range* (cons () iterator)))))

(define Fold
  (lambda (func arg lyst)
    (i lyst
       (Fold func
             (Apply func (list arg (car lyst)))
             (cdr lyst))
       arg)))

Dalsze wyjaśnienia dostępne na żądanie.

Próbka wyjściowa

Korzysta ze środowiska REPL z mojej referencyjnej implementacji. Użyłem q(quote) dla funkcji unary i s(odejmij) jako funkcji binarnej dla tych przykładów, a także funkcji @(zdefiniowanej w tym kodzie), która zwraca listę jej argumentów.

tl> [line of definitions goes here]
@
Q
A
M
N
,
R
T
F
tl> (A s (@ 10 7))
3
tl> (M q (@ 1 2 3 4))
((q 1) (q 2) (q 3) (q 4))
tl> (N q 123 4)
(q (q (q (q 123))))
tl> (R 5)
(1 2 3 4 5)
tl> (T q (@ 3 7))
((q 3) (q 4) (q 5) (q 6) (q 7))
tl> (F s 10 (@ 4 3 2))
1

2

Python 2.x: 450,6 bajtów (493 bajtów przed 10% rabatem)

Odpowiedź w golfa:

y=len
z=lambda a,b:a.append(b)
_=lambda a:a if a is not None else[]
def M(a,b,c=None):
 c=_(c);d=y(b)
 if d:z(c,A(a,b[0]))
 return M(a,b[1:],c)if d else c
def N(a,b,c):d=A(a,b);return N(a,d,c-1)if c>1 else d
A=lambda a,b:a(*b)if type(b)is list else a(b)
def R(a,b=None):b=_(b);b.insert(0,a);return b if a<=1 else R(a-1,b)
def F(a,b,c):d=a(b,c[0]);return F(a,d,c[1:])if y(c)>1 else d
def T(a,b,c=None,d=None):
 if c is None:c=b[0];d=[]
 z(d,a(c));return T(a,b,c+1,d)if c<b[1]else d

To pytanie było zabawne. Postanowiłem napisać moje funkcje bez użycia ekwiwalentów Pythona (choć może to być poprawna luka) i napisać funkcje tak, jakby Python obsługiwał rekurencję ogona. Aby to zadziałało, użyłem wielu opcjonalnych parametrów, które pozwalają, aby wymagane połączenia nadal działały.

Poniżej mam niepoznane listy dla każdej funkcji.

Apply:

A = lambda function, arguments: function(*arguments) if type(arguments) is list else function(arguments)

Map:

def M(function, arguments, result=None):
    result = result if result is not None else []
    length = len(arguments)
    if length != 0:
        result.append(A(function, arguments[0]))
    return M(function, arguments[1:], result) if length != 0 else result

Nest:

def N(function, arguments, times):
    result = A(function, arguments)
    return N(function, result, times - 1) if times > 1 else result

Zauważ, że ta funkcja wymaga, aby przekazany functionmógł reprezentować wiele argumentów zmiennie. Innym podejściem byłoby wymuszenie, aby funkcja zawsze otrzymywała jedną listę, ale wymagałoby to, aby przekazywane funkcje mogły interpretować listy argumentów. W każdym razie były pewne założenia, więc wybrałem ten, który lepiej pasuje do reszty systemu.

Range:

def R(ceiling, result=None):
    result = result if result is not None else []
    result.insert(0, ceiling)
    return result if ceiling <= 1 else R(ceiling - 1, result)

Fold:

def F(function, initial, rest):
    result = function(initial, rest[0])
    return F(function, result, rest[1:] if len(rest) > 1 else result

Table:

def T(function, iterator, current=None, result=None):
    if current is None:
        current = iterator[0]
        result = []
    result.append(function(current))
    return T(function, iterator, current + 1, result) if current < iterator[1] else result

Oto przykładowe dane wyjściowe przy użyciu następujących funkcji pomocniczych:

square = lambda x: x * x
def add(*args):
    addTwo = lambda a, b: a + b
    if len(args) == 1 and type(args[0]) is list:
        return F(addTwo, 0, args[0])
    else:
        return F(addTwo, 0, args)

>>> M(square, R(10))
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> M(add, [R(i) for i in R(10)])
[1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
>>> T(square, [0, 10])
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> N(square, 2, 4)
65536
>>> N(lambda *args: F(lambda a, b: a * b, 1, args) if len(args) > 1 else str(args[0]) + 'a', R(5), 10)
'120aaaaaaaaa'

Wow, wygląda naprawdę dobrze!
WizardOfMenlo

To wydaje się wypaczonym poczuciem estetyki; ) Zawsze zabawnie jest oglądać golfa Pythona od pierwszej przeczytanej książki, która mówi o tym, jak Python wymusza czytelność.
sadakatsu

Naprawdę mam zniekształcone poczucie estetyki :)
WizardOfMenlo

Jestem zdezorientowany wynikami innych osób. Wziąłem 10% wyniku każdej z wymaganych funkcji, które nie korzystały z pętli (która była wszystkim), ale inne osoby wzięły 10% wyniku dla każdej funkcji, która nie korzystała z pętli (która może być do 60% zniżki). Jakie jest właściwe podejście?
sadakatsu

Twoja jest właściwa droga, miałem nierealne oczekiwania, więc początkowo miałem na myśli podejście 60%, ale teraz myślę, że 10% będzie bardziej stymulujące i bardziej wyrównane między nimi
WizardOfMenlo

2

Cejlon, 370 * 0,9 = 333 364 * 0,9 = 327,4

Większość tych funkcji jest już dostępna w pakiecie językowym Cejlonu (choć czasem z nieco inną sygnaturą), ale definiujemy je tutaj, zgodnie z żądaniem w pytaniu.

alias I=>Integer;R[]m<A,R>(R(A)g,A[]l)=>f((R[]x,A y)=>x.append([g(y)]),[],l);A n<A>(A(A)g,A a,I t)=>f((A x,I y)=>g(x),a,r(t));R y<A,R>(Callable<R,A>g,A v)given A satisfies Anything[]=>g(*v);I[]r(I i)=>t((j)=>j,[1,i]);A f<A,O>(A(A,O)g,A a,O[]o)=>if(nonempty o)then f(g,g(a,o[0]),o.rest)else a;R[]t<R>(R(I)g,[I,I]i)=>i[1]<i[0]then[]else[g(i[0]),*t(g,[i[0]+1,i[1]])];

Właściwie tylko dwie funkcje ( tif ) faktycznie korzystają z rekurencji (odpowiednio nad listami i liczbami całkowitymi), inne oparte są na nich. (Apply jest nieco odstające, tak naprawdę nie odnosi się do innych.)

Interpretuję „Listę” jako sekwencyjny typ Cejlona, ​​który jest niezmienną uporządkowaną (być może pustą) sekwencją elementów. [R*]oznacza Sequential<R>- z jakiegoś powodu możemy to również napisaćR[] , który jest o jeden bajt krótszy.

Typem funkcji jest Callable<R, A>, gdzie Ajest krotką dla argumentów, na przykład [X, Y, Z](np. Jakiś podtyp Anything[]). Jako skrót możemy napisać R(X,Y,Z)zamiast Callable<R,[X,Y,Z]>.

I alias Integerjak Izaoszczędzić trochę bajtów.

Oto sformatowana (i nieco skomentowana) wersja:

// implement functional paradigms
//
// Question: http://codegolf.stackexchange.com/q/58588/2338
// My Answer: http://codegolf.stackexchange.com/a/64515/2338

alias I => Integer;

// map – based on fold.
R[] m<A, R>(R(A) g, A[] l) =>
        f((R[]x,A y) => x.append([g(y)]), [], l);

// nest – based on fold + range, throwing away the second
//        argument in a proxy function.
A n<A>(A(A) g, A a, I t) =>
        f((A x, I y) => g(x), a, r(t));

// apply – this looks quite heavy due to type safety.
//         This uses the "spread operator" *.
R y<A, R>(Callable<R,A> g, A v)
        given A satisfies Anything[] =>
        g(*v);

// range – based on table (using the identity function)
I[] r(I i) =>
        t((j) => j, [1, i]);

// fold – a plain list recursion.
A f<A, O>(A(A, O) g, A a, O[] o) =>
        if (nonempty o) then f(g, g(a, o[0]), o.rest) else a;

// table – an integer recursion.
//        (Not sure why the min/max parameters need
//         to be passed in one argument.)
R[] t<R>(R(I) g, [I, I] i) =>
        i[1] < i[0] then [] else [g(i[0]), *t(g, [i[0] + 1, i[1]])];

Korzystanie z „pętli”

Tabela i mapa mogą być zaimplementowane krócej za pomocą pętli (w rzeczywistości, rozumienie sekwencji):

// map – using a sequence comprehension:
R[] m<A, R>(R(A) g, A[] l) =>
        [for(a in l) g(a)];

// table – map with an integer range.
//        (Not sure why the min/max parameters need
//         to be passed in one argument.)
R[] t<R>(R(I) g, [I, I] i) =>
        m(g, i[0]..i[1]);

Chociaż nie jestem pewien, czy użycie ..operatora dla zakresu liczb całkowitych liczy się jako użycie wbudowanej funkcji. Jeśli jest to dozwolone, wynikowy kod jest tutaj, długość 312:

alias I=>Integer;R[]m<A,R>(R(A)g,A[]l)=>[for(a in l)g(a)];A n<A>(A(A)g,A a,I t)=>f((A x,I y)=>g(x),a,r(t));R y<A,R>(Callable<R,A>g,A v)given A satisfies Anything[]=>g(*v);I[]r(I i)=>t((j)=>j,[1,i]);A f<A,O>(A(A,O)g,A a,O[]o)=>if(nonempty o)then f(g,g(a,o[0]),o.rest)else a;R[]t<R>(R(I)g,[I,I]i)=>m(g,i[0]..i[1]);

(Można go jeszcze skrócić, definiując r(I i) => 1..i, co daje wynik 301. Chociaż wygląda to jeszcze bardziej na oszustwo).

Jeśli ..nie jest to dozwolone, będziemy musieli wprowadzić go ponownie. Możemy używać tych implementacji do ( ri powyżej):tm

// range – based two-limit range 
I[] r(I i) =>
        q(1, i);

// two-limit range implemented recursively
I[] q(I i, I j) =>
        j < i then [] else [i, *q(i + 1, j)];


// table – map with an integer range.
//        (Not sure why the min/max parameters need
//         to be passed in one argument.)
R[] t<R>(R(I) g, [I, I] i) =>
        m(g, q(i[0], i[1]));

Daje to 348 bajtów, lepiej niż wersja całkowicie rekurencyjna, ale nie po zastosowaniu premii.


0

Groovy (146 bajtów) (146 * 90% = 131,4)

PS Nie wiem, co w tym kontekście traktujesz jako „pętlę”, zastosowałem premię dopiero po tym, jak powiedziano mi to w komentarzach OP, i usunę ją, jeśli 2-3 dodatkowych użytkowników powie te funkcje i iteratory są pętle i że nie zasługuję na bonus. Ponadto, jeśli chcesz zadzwonić do mnie z powodu korzystania z 1..it, zrób to, a ja go przerobię / zaktualizuję moje bajtowe.

m={f,l->l.collect{f(it)}}            // Map
n={f,x,n->n.times{x=f(x)};x}         // Nest
a={f,l->f(l)}                        // Apply
r={1..it}                            // Range (Is this cheating?)
f={f,x,l->l.each{x=f(x,it)};x}       // Fold
t={f,l->(l[0]..l[1]).collect{f(it)}} // Table

Przykład wejścia / wyjścia

f1={2*it}
f2={a,b,c,d,e->a*b*c*d*e}
f3={a,b->a*b}
l=[1,2,3,4,5]
l2=[1,9]
y=5
x=1
println m(f1,l)
println n(f1,x,y)
println a(f2,l)
println r(y)
println f(f3,x,l)
println t(f1,l2)

Wydajność

MAP:   [2, 4, 6, 8, 10]
NEST:  32
APPLY: 120
RANGE: [1, 2, 3, 4, 5]
FOLD:  120
TABLE: [2, 4, 6, 8, 10, 12, 14, 16, 18]

Wypróbuj sam: https://groovyconsole.appspot.com/edit/5203951758606336


To technicznie nie wykorzystuje pętli, więc pamiętaj o bonusie! W przeciwnym razie świetna odpowiedź!
WizardOfMenlo

Technicznie nie ma pętli ?! Naprawdę?! .each {} .times {} .collect {} to iteratory.
Magic Octopus Urn
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.