Implementare paradigmi di programmazione funzionale


21

La tua azienda ha appena iniziato un progetto e per la prima volta hai deciso di utilizzare uno stile di programmazione funzionale in stile codice. Tuttavia il tuo capo è molto diffidente e non vuole usare le funzioni integrate e richiede di implementare te stesso le funzioni principali. In particolare è necessario scrivere le funzioni: Map, Nest, Apply, Range, Folde Tablein una lingua a vostra scelta. Il capo è un uomo molto impegnato e vuole avere i programmi il più breve possibile, quindi non perde tempo a leggere. Inoltre, non vorrebbe che tu utilizzassi i loop, quindi avrai una riduzione del 10% sul conteggio dei byte per non usare i loop.

I requisiti dettagliati delle funzioni sono di seguito:

Carta geografica

La Mapfunzione accetta due parametri: fe listdove fè una funzione ed listè un elenco di valori. Dovrebbe restituire l' fapplicazione applicata a ciascun elemento di list. Pertanto funzionerà come tale:

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

ritorna

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

e

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

ritorna

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

Nido

La Nestfunzione accetta tre parametri vedere: f, arg, timesdove fè una funzione, argè la sua tesi di partenza, ed timesè quante volte viene applicata la funzione. Dovrebbe restituire un'espressione con tempi fapplicati timesa arg. Pertanto funzionerà come tale:

Nest(f, x, 3)

ritorna

f(f(f(x)))

e

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

ritorna

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

Applicare

La Applyfunzione accetta due parametri: fe argsdov'è funa funzione e argsun elenco. Dovrebbe applicarsi fal args. Perciò:

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

ritorna

f(a,b,c)

Gamma

La Rangefunzione accetta un numero intero re genera numeri interi fino a quel numero. Perciò:

Range(5)

ritorna

{ 1, 2, 3, 4, 5}

piega

La Foldfunzione prende tre parametri f, arg, othersdove fè una funzione, argè semplice parametro, e othersun elenco. Funzionerà come tale:

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

ritorna

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

tavolo

Le funzioni della tabella dovrebbero assumere una funzione fe un parametro chiamato iteratornel formato: {iMin, iMax}dove iMine iMaxsono numeri interi. È necessario applicare foltre l'intervallo specificato. Perciò:

Table(f, {0, 5})

ritorna

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

Ho usato la definizione di queste funzioni dalla pagina di programmazione funzionale di Mathematica , quindi vai lì se hai bisogno di ulteriori indicazioni. Nota che non dovrai implementare tutte le versioni delle funzioni mostrate in quella pagina, ma solo quelle scritte in questo post.

Le scappatoie standard non sono consentite come al solito.

Nel caso in cui la tua lingua non consenta il passaggio di funzioni come argomenti, devi implementare questa funzionalità e aggiungerla alla tua risposta. Tuttavia, il conteggio dei byte di questa operazione non verrà aggiunto al totale.

Questo è il codice golf, quindi vince il codice più corto. In bocca al lupo!!!


Questo e spettacolare! +1 Tuttavia, non capisco come Tablefunzioni qui. Il tuo esempio dovrebbe essere Table(f, {x, 0, 5})? Inoltre non capisco affatto lo scopo x, poiché applica solo la funzione all'intervallo.
kirbyfan64sos,

@ kirbyfan64sos Grazie! Sì, era un errore di battitura, ho lasciato x come riferimento alla matematica, che lo usa come bellezza simbolica, tuttavia penso di poterlo ottenere
WizardOfMenlo

Un'altra domanda: come chiamiamo le funzioni? Dobbiamo dare loro gli stessi identici nomi? Single-lettera?
kirbyfan64sos,

@ kirbyfan64sos Dato che si tratta di code-golf, permetterò nomi a lettera singola, tuttavia nella tua risposta metti un'intestazione su ogni funzione in modo da sapere quale sia. Inoltre, non utilizzare lettere in collisione.
WizardOfMenlo,

Potresti essere più specifico su ciò che conta come un ciclo?
xnor

Risposte:


9

Haskell, molti byte precedenti contano 127 * 0.9 = 114.3 byte

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)

Nessun loop, solo ricorsione.

#è la mappa: (*2) # [1,2,3]->[2,4,6]

&è nido: ((*2) & 3) 4->48

isi applica: i (*2) 7->14

rè intervallo: r 4->[1,2,3,4]

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

%è tabella: (*2) % (2,4)->[4,6,8]

Come richiesto una versione non modificata con commenti. Nota, &e ?sono operatori di infusione ternaria, che richiedono parentesi aggiuntive quando vengono chiamate o con pattern corrispondenti.

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

Grazie a @dfeuer e @Zgarb per alcuni suggerimenti utili


Sono nuovo di Haskell, sembra abbastanza buono, tuttavia potresti per favore aggiungere una spiegazione a quello che stai facendo?
WizardOfMenlo,

1
@WizardOfMenlo: aggiunti alcuni commenti
nimi

Ho appena realizzato quanto sia elegante Haskell, davvero buono!
WizardOfMenlo,

1
Ignorando infinite liste e l'efficienza, m#q=reverse$f((:).m)[]q. Questa è la stessa lunghezza della tua, ma molto più difficile da leggere.
dfeuer,

È possibile abbreviare !facendo un nome al posto di un operatore: i f=f.
dfeuer,

5

Python 2, 305,1 byte (-10% 376 369 366 349 339 byte)

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

Se espanso, equivalente a:

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]+")]")

Nessun loop!

Bene, fa molto evaling e se il tuo capo non sopporta i loop, allora ODIERANNO EVAL. Ma dovranno sopportarlo

Un modo di fare rangein una lambda è apprezzato quindi non devo svolgere alcuna funzione (Shudder.).

spiegazioni:

  • m=lambda f,l:eval("["+"f(l.pop()),"*len(l)+"][::-1]")
    • Crea una stringa che fa apparire elementi dall'elenco, avvolgilo in un elenco, invertilo e infine valutalo!
  • n=lambda f,x,l:eval("f("*l+"*x"+")"*l)
    • Crea manualmente la stringa con l'annidamento e valutala!
  • r=lambda i:e("r(i-1)+"*(i>1)+"[i]")
    • Crea una stringa che, se modificata eval, restituisce [0]o utilizza la ricorsione per ottenere i risultati precedenti e aggiunge l'indice corrente all'elenco. Lo valuta.
  • a=lambda f,a:eval("f(a["+r (len (a))[1:-1].replace(",","-1],a[")+"-1])")
    • Utilizza la funzione di intervallo per ottenere gli indici 1-len (elenco). Sostituisce le virgole nell'elenco elencate con un modo per ottenere l'indice corretto dell'elenco a. Lo valuta!
  • f=lambda f,x,l:eval("f("*len(l)+"x,["+r (len (l))[1:-1].replace(",","-1]),l[")+"-1])")
    • Lo stesso vale per tranne che sostituisce le virgole con parentesi quadre, virgole e avvio dell'indice dell'elenco.
  • t=lambda f,n,x:eval("[f("+r (x) [n-1:].replace(",","),f(")[1:-1]+")]")
    • Come applicare e piegare tranne per la sostituzione con la fine della funzione e la chiamata a quella nuova. Lo valuta!

Mappa, annidamento, intervallo, applica, piega, tabella.

Grazie @Zgarb per un lambda per la gamma!


Il mio capo avrà la testa sulla sua scrivania :) Potresti aggiungere anche una breve spiegazione per favore?
WizardOfMenlo,

Che ne dici r=lambda i:[]if i<1 else r(i-1)+[i]? Nessun loop, solo ricorsione.
Zgarb,

1
Certo, lo prenderò per ora, ma il capo ha bisogno di più evalper mostrare loro come i loop non sono così male :)
Blue

Ha! Un'altra versione che utilizza e=eval:r=lambda i:e("r(i-1)+"*(i>1)+"[i]")
Zgarb,

puoi cambiarlo dal bonus del 60% ad un 10% per favore? Ho rivisto la specifica della domanda, in modo da renderla più
corretta

5

Javascript ES6, 197 * 0,9 = 177,3 byte

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

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

Utilizza Piega per concatenare i risultati di fapplicati a ciascun membro di lsu un elenco vuoto. L'uso delle funzioni integrate riduce questo a M=(f,l)=>l.map(f)(non lo hai usato perché sembra economico ...?).

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

Applicare in fmodo ricorsivo fino a quando non nviene ridotto a 0.

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

Utilizza lo spread ( ...operatore) da applicare lsu f.

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

Concatena nalla chiamata ricorsiva di Range fino a quando non nviene decrementato a 0.

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

Applica la chiamata ricorsiva di Fold e l' nelemento th di fino la fquando nviene decrementato a 0. L'uso di funzioni integrate lo riduce a F=(f,x,l)=>l.reduce(f,x)(di nuovo, sembrava economico ...).

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

Inizializza dapprima ne xsu iMin e iMax usando destructuring ( [n,x]=i), quindi utilizza Range per costruire la tabella di valori da iMin a iMax. fviene quindi applicato sulla tabella utilizzando Mappa e il risultato viene restituito.


Vuoi conoscere la mia filosofia? "Se è economico, compralo." Nelle specifiche non dice che non puoi usare i builtin (ancora), quindi usali!
Mama Fun Roll

4

Python 3, 218 byte

La versione illeggibile:

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

La (più) versione leggibile:

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

Andando però un lambda alla volta:

Funzione mappa P

P=lambda f,x:[f(_)for _ in x]
Solo un semplice iteratore. Non c'è molto da dire qui.

Funzione Nest Y

Y=lambda f,x,z:Y(f,f(x),z-1)if z else x
Ricorrendo fino a zraggiungere zero, applicando fogni volta. La clausola if alla fine sembra goffa; forse c'è un modo migliore per terminare la ricorsione.

Applica la funzione T

T=lambda f,x:f(*x)
Python ha un simpatico operatore di espansione per fare tutto il lavoro pesante per me.

Funzione di portata H

H=lambda f,x=0:(H(f-1)if~-f else[])+[f]
Questo è stato più difficile di quanto mi aspettassi. Ho finito con un approccio ricorsivo. Ancora una volta, la costruzione if-else occupa molti byte e ritengo che possa essere migliorata. Perché ha un manichino x=0, chiedi? È così che quando lo comprimo con il exec, posso sostituire =lambda f,xinvece di solo =lambda f.

Funzione di piegatura O

O=lambda f,x,z:O(f,f(x,z[0]),z[1:])if z else x
Abbastanza felice con questo. Elimina semplicemente il primo elemento dell'array ogni volta che ricorre, fino a quando non rimane più nulla.

Funzione tabella N

N=lambda f,x:(N(f,[x[0],x[1]-1])if x[1]-x[0]else[])+[f(x[1])]
Questo è orribile e sono sicuro che ci sia spazio per miglioramenti. Ho cercato di utilizzare le funzioni di intervallo e mappa precedentemente definite per una map(f,range(x,y))sorta di costruzione, ma senza molto successo. Finì per fare un terribile approccio ricorsivo che condivideva una certa somiglianza con la funzione range.

Tutti i lambda sono racchiusi in un carattere execcon a replaceper ridurre significativamente il numero di byte.


Stavo per commentare che [f(_)for _ in x]poteva essere abbreviato map(f,x), ma poi mi sono ricordato quale fosse la sfida
Cyoce

4

Julia, 181 byte

Nessun bonus per me; Ho usato i loop liberamente. Mi dispiace capo, ma i loop in Julia sono efficienti!

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

L'aggiunta dei puntini di sospensione dopo un argomento a una funzione interrompe un array, una tupla o ciò che è presente in argomenti di funzioni regolari. Altrimenti la funzione penserà che stai cercando di passare un array (o tupla, ecc.). Non ha alcun effetto per singoli argomenti.

Nomi delle funzioni:

  • Carta geografica: M
  • Nido: N
  • Applicare: A
  • Gamma: R
  • Fold: F
  • Tavolo: T

4

tinylisp , 325 * 0.9 = 292.5

La lingua è più recente della domanda, ma non vincerà comunque.

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

Definisce le funzioni A(applica), M(mappa), N(annidamento), R(intervallo), T(tabella) e F(piega), insieme a una coppia di funzioni di supporto.Tsi aspetta un elenco di due numeri interi per il suo secondo argomento.

Tinylisp non ha nemmeno alcun costrutto ad anello; tutto viene realizzato usando la ricorsione. Molte di queste funzioni non sono ricorsive , quindi se le chiamate su grandi elenchi probabilmente faranno esplodere lo stack di chiamate. Potrebbero essere tutti implementati con la ricorsione della coda ... ma richiederebbero più byte, e questo è il golf del codice.

Ecco una versione estesa con spazi bianchi e parole reali per i nomi, che dovrebbe essere abbastanza leggibile se hai familiarità con Lisp. (Ho modificato la maggior parte dei builtin tinylisp ad eccezione di q(quote) e 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)))

Ulteriori spiegazioni disponibili su richiesta.

Uscita campione

Utilizza l'ambiente REPL dalla mia implementazione di riferimento. Ho usato q(quote) per la funzione unaria e s(sottrarre) come funzione binaria per questi esempi, così come la funzione @(definita in questo codice) che restituisce un elenco dei suoi argomenti.

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 byte (493 byte prima del 10% di sconto)

Risposta golfata:

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

Questa domanda è stata divertente. Ho deciso di scrivere le mie funzioni senza usare gli equivalenti di Python (anche se potrebbe essere stata una valida scappatoia) e di scrivere le funzioni come se Python supportasse la ricorsione della coda. Per farlo funzionare, ho usato molti parametri opzionali che consentono alle chiamate richieste di funzionare ancora.

Di seguito ho elenchi non memorizzati per ogni funzione.

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

Nota che questa funzione richiede che i passati functionpossano rappresentare più argomenti in modo variabile. Un altro approccio sarebbe stato quello di imporre che la funzione ricevesse sempre un unico elenco, ma ciò avrebbe richiesto che le funzioni passate fossero in grado di interpretare gli elenchi di argomenti. In entrambi i casi c'erano delle ipotesi, quindi ho scelto quella che si adattava meglio al resto del sistema.

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

Ecco un esempio di output che utilizza le seguenti funzioni di supporto:

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, sembra davvero bello!
WizardOfMenlo,

Sembra un senso distorto dell'estetica; ) Trovo sempre divertente vedere Python golf dal primo libro Python che ho letto che parlava di come Python applica la leggibilità.
Sadakatsu,

Ho davvero un senso distorto dell'estetica :)
WizardOfMenlo

Sono confuso dai punteggi degli altri. Ho preso il 10% del punteggio di ciascuna delle funzioni richieste che non utilizzavano un loop (che era tutto loro), ma altre persone prendevano il 10% dell'intero punteggio per ciascuna funzione che non utilizzava un loop (che può essere fino al 60% di sconto). Qual è l'approccio corretto?
Sadakatsu,

La tua è la strada giusta da percorrere, avevo un'aspettativa irrealistica e quindi inizialmente avevo in mente l'approccio del 60%, ma ora penso che il 10% sarà più stimolante e più giusto tra i due
WizardOfMenlo

2

Ceylon, 370 * 0.9 = 333 364 * 0.9 = 327.4

Molte di queste funzioni sono già disponibili nel pacchetto linguistico di Ceylon (anche se a volte con una firma leggermente diversa), ma le stiamo definendo qui come richiesto nella domanda.

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

In realtà solo due delle funzioni ( te f) stanno effettivamente utilizzando la ricorsione (rispettivamente su liste e numeri interi), le altre si basano su queste. (Applicare è un po 'anomalo, non si riferisce in realtà agli altri.)

Interpreto "Elenco" come tipo sequenziale di Ceylon, che è una sequenza di elementi ordinata immutabile (possibilmente vuota). [R*]sta per Sequential<R>- per qualche motivo possiamo anche scriverloR[] , che è più corto di un byte.

Un tipo di funzione è Callable<R, A>, dove Aè un tipo di tupla per gli argomenti, come [X, Y, Z](cioè un sottotipo di Anything[]). Come scorciatoia possiamo scrivere R(X,Y,Z)invece di Callable<R,[X,Y,Z]>.

Ho Integerdato le mie indicazioni su come Isalvare alcuni byte.

Ecco una versione formattata (e leggermente commentata):

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

Utilizzando "loop"

Tabella e mappa possono essere implementate in modo più breve usando i loop (in realtà, una comprensione della sequenza):

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

Anche se non sono sicuro che l'uso ..dell'operatore per l'intervallo intero contenga come l'utilizzo di una funzione integrata. Se questo è consentito, il codice risultante è questo qui, lunghezza 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]);

(Potrebbe essere reso ancora più breve definendo r(I i) => 1..i , ottenendo il punteggio 301. Anche se sembra ancora più simile a un imbroglio.)

Se ..non è consentito, dovremo implementarlo di nuovo. Possiamo usare queste implementazioni per re t(con quanto msopra):

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

Ciò si traduce in 348 byte, meglio della versione completamente ricorsiva, ma non dopo aver applicato il bonus.


0

Groovy (146 byte) (146 * 90% = 131,4)

PS Non so cosa stai considerando come un 'ciclo' in questo contesto, ho applicato il bonus solo dopo che mi è stato detto nei commenti da OP e rimuoverò se 2-3 utenti aggiuntivi dicono queste funzioni di raccolta e iteratori sono loop e che non merito il bonus. Inoltre, se vuoi chiamarmi sul mio uso di 1..it, ti preghiamo di farlo e lo rielaborerò / aggiornerò il mio bytecount.

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

Esempio di input / output

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)

Produzione

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]

Provalo tu stesso: https://groovyconsole.appspot.com/edit/5203951758606336


Questo tecnicamente non utilizza i loop, quindi ricorda il bonus! Altrimenti, ottima risposta!
WizardOfMenlo,

Tecnicamente nessun loop ?! Veramente?! .each {} .times {} .collect {} sono iteratori.
Magic Octopus Urn
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.