p
\Ai
\&
>(&]&|0
<*&d
&~bN
10
( )/+
/*
Provalo online!
Spiegazione
Questo è di gran lunga il programma più elaborato (e anche il più lungo) che ho scritto finora in Jellyfish. Non ho idea se sarò in grado di scomporre in modo comprensibile, ma immagino che dovrò provare.
Jellyfish fornisce un operatore di iterazione abbastanza generale \
, che aiuta molto a "trovare l'ennesimo qualcosa ". Una delle sue semantiche è "iterare una funzione su un valore finché una funzione di test separata non dà qualcosa di vero" (in effetti, la funzione di test riceve sia l'elemento corrente che l'ultimo, ma faremo solo guardare l'elemento corrente) . Possiamo usarlo per implementare una funzione "prossimo numero valido". Un altro sovraccarico di \
è "iterare una funzione su un valore iniziale N volte". Possiamo usare la nostra funzione precedente e iterarla su 0
N volte, dove N è l'input. Tutto ciò è impostato in modo abbastanza conciso con questa parte del codice:
p
\Ai
\&
> 0
(I motivi per cui 0
, l'effettivo input per la funzione risultante, è finita lì sono un po 'complicati e non entrerò in essi qui.)
Il problema con tutto ciò è che non passeremo manualmente il valore corrente alla funzione di test. L' \
operatore lo farà per noi. Quindi ora abbiamo costruire una singola funzione unaria (tramite composizioni, ganci, forchette e curry) che accetta un numero e ci dice se è un numero valido (cioè uno che è diviso per la somma delle cifre e il prodotto delle cifre). Questo è abbastanza banale quando non puoi fare riferimento all'argomento. Mai. È questa bellezza:
(&]&|
<*&d
&~bN
10
( )/+
/*
La (
è una unario gancio , il che significa che chiama la funzione qui sotto ( f
) sul suo ingresso (valore attuale x
), e poi passa entrambi per la funzione di test a destra ( g
), che è esso calcola g(f(x), x)
.
Nel nostro caso, f(x)
è un'altra funzione composita che ottiene una coppia con il prodotto cifra e la somma cifre di x
. Ciò significa g
che sarà una funzione che ha tutti e tre i valori per verificare se x
è valida.
Inizieremo esaminando come f
calcola la somma delle cifre e il prodotto della cifra. Questo è f
:
&~b
10
( )/*
/+
&
è anche composizione (ma viceversa). ~
sta eseguendo il curry, quindi 10~b
dà una funzione che calcola le cifre decimali di un numero, e poiché lo stiamo passando &
da destra, questa è la prima cosa che accadrà all'input x
. Il resto utilizza questo elenco di cifre per calcolare la somma e il prodotto.
Per calcolare una somma, possiamo piegare l' aggiunta su di essa, che è /+
. Allo stesso modo, per calcolare il prodotto con cui pieghiamo la moltiplicazione su di esso /*
. Per combinare entrambi questi risultati in una coppia, usiamo una coppia di ganci (
e )
. La struttura di questo è:
()g
f
(Dove f
e g
sono rispettivamente prodotto e somma.) Proviamo a capire perché questo ci dà un paio di f(x)
e g(x)
. Nota che l'hook destro )
ha solo un argomento. In questo caso, si suppone che sia l'altro argomento ;
che avvolge i suoi argomenti in una coppia. Inoltre, gli hook possono anche essere usati come funzioni binarie (come sarà il caso qui) nel qual caso applicano semplicemente la funzione interna solo a un argomento. Quindi davvero )
su una singola funzione g
dà una funzione che calcola [x, g(y)]
. Usando questo in un gancio sinistro, insieme a f
, otteniamo [f(x), g(y)]
. Questo, a sua volta, viene utilizzato in un contesto unario, il che significa che in realtà viene chiamato con x == y
e quindi finiamo con [f(x), g(x)]
come richiesto. Uff.
Ciò lascia solo una cosa, che era la nostra precedente funzione di test g
. Ricordiamo che verrà chiamato come g([p, s], x)
dove x
è ancora il valore di input corrente, p
è il suo prodotto cifra ed s
è la sua somma cifra. Questo è g
:
&]&|
<*&d
N
Per testare la divisibilità, useremo ovviamente il modulo, che si trova |
in Jellyfish. Un po 'insolitamente, prende il suo operando di destra modulo il suo operando di sinistra, il che significa che gli argomenti g
sono già nell'ordine giusto (funzioni aritmetiche come questa passano automaticamente sugli elenchi, quindi questo calcolerà i due moduli separati gratuitamente) . Il nostro numero è divisibile per prodotto e somma, se il risultato è una coppia di zeri. Per verificare se è così, trattiamo la coppia come un elenco di cifre di base 2 ( d
). Il risultato è zero, solo quando entrambi gli elementi della coppia sono zero, quindi possiamo negare il risultato di questo ( N
) per ottenere un valore veritiero se entrambi i valori dividono l'input. Si noti che |
, d
eN
sono semplicemente tutti composti insieme con una coppia di &
s.
Sfortunatamente, questa non è la storia completa. Cosa succede se il prodotto con cifre è zero? Divisione e modulo per zero restituiscono entrambi zero in Meduse. Mentre questa può sembrare una convenzione un po 'strana, in realtà si rivela in qualche modo utile (perché non è necessario controllare lo zero prima di eseguire il modulo). Tuttavia, significa anche che possiamo ottenere un falso positivo, se la somma delle cifre divide l'input, ma il prodotto della cifra è zero (ad es. Input 10
).
Possiamo risolvere questo problema moltiplicando il nostro risultato di divisibilità per il prodotto cifra (quindi se il prodotto cifra è zero trasformerà anche il nostro valore di verità in zero). Risulta più semplice moltiplicare il risultato di divisibilità con la coppia di prodotto e la somma, ed estrarre successivamente il risultato dal prodotto.
Per moltiplicare il risultato con la coppia, è necessario tornare a un valore precedente (la coppia). Questo viene fatto con un fork ( ]
). Le forcelle sono un po 'come i ganci degli steroidi. Se si danno loro due funzioni f
e g
, rappresentano una funzione binaria che calcola f(a, g(a, b))
. Nel nostro caso, a
è la coppia prodotto / somma, b
è il valore di input corrente, g
è il nostro test di divisibilità ed f
è la moltiplicazione. Quindi tutto questo calcola [p, s] * ([p, s] % x == [0, 0])
.
Tutto ciò che rimane ora è estrarre il primo valore di questo, che è il valore finale della funzione di test utilizzata nell'iteratore. Questo è semplice come comporre ( &
) il fork con la funzione head<
, che restituisce il primo valore di un elenco.