p
\Ai
\&
>(&]&|0
<*&d
&~bN
10
( )/+
/*
Probieren Sie es online!
Erläuterung
Dies ist mit Abstand das aufwändigste (und auch längste) Programm, das ich bisher in Jellyfish geschrieben habe. Ich habe keine Ahnung, ob ich das verständlich aufschlüsseln kann, aber ich denke, ich muss es versuchen.
Jellyfish bietet einen ziemlich allgemeinen Iterationsoperator \
, der beim "Finden des N-ten Etwas " sehr hilfreich ist . Eine seiner Semantiken ist "eine Funktion auf einem Wert iterieren, bis eine separate Testfunktion etwas Wahres ergibt" (in der Tat empfängt die Testfunktion sowohl das aktuelle als auch das letzte Element, aber wir lassen es nur das aktuelle Element betrachten). . Wir können dies verwenden, um eine Funktion "Nächste gültige Nummer" zu implementieren. Eine weitere Überladung von \
ist "N-mal eine Funktion auf einem Startwert iterieren". Wir können unsere vorherige Funktion verwenden und sie 0
N-mal durchlaufen , wobei N die Eingabe ist. All das ist ziemlich genau mit diesem Teil des Codes zusammengestellt:
p
\Ai
\&
> 0
(Die Gründe, warum 0
die eigentliche Eingabe für die resultierende Funktion dort drüben ist, sind etwas kompliziert und ich werde hier nicht darauf eingehen.)
Das Problem dabei ist, dass wir den aktuellen Wert nicht manuell an die Testfunktion übergeben. Der \
Betreiber wird dies für uns tun. Also haben wir jetzt eine einzige unäre Funktion (über Kompositionen, Haken, Gabeln und Curry), die eine Zahl aufnimmt und uns sagt, ob es eine gültige Zahl ist (dh eine, die durch ihre Ziffernsumme und ihr Ziffernprodukt geteilt wird). Dies ist ziemlich nicht trivial, wenn Sie sich nicht auf das Argument beziehen können. Je. Es ist diese Schönheit:
(&]&|
<*&d
&~bN
10
( )/+
/*
Das (
ist ein unärer Hook , was bedeutet, dass es die Funktion below ( f
) an seinem Eingang aufruft (den aktuellen Wert x
) und dann beide an die Testfunktion rechts ( g
) übergibt , das heißt, es berechnet g(f(x), x)
.
In unserem Fall f(x)
handelt es sich um eine andere zusammengesetzte Funktion, die ein Paar mit dem Ziffernprodukt und der Ziffernsumme von ergibt x
. Das heißt, es g
wird eine Funktion sein, die alle drei Werte hat, um zu prüfen, ob sie x
gültig ist.
Wir beginnen damit, wie f
die Ziffernsumme und das Ziffernprodukt berechnet werden. Das ist f
:
&~b
10
( )/*
/+
&
ist auch Komposition (aber umgekehrt). ~
Wenn Sie mit currying arbeiten, erhalten Sie 10~b
eine Funktion, die die Dezimalstellen einer Zahl berechnet, und da wir diese &
von rechts übergeben, passiert das als Erstes mit der Eingabe x
. Der Rest verwendet diese Ziffernliste, um die Summe und das Produkt zu berechnen.
Um eine Summe zu berechnen, können wir falten zusätzlich darüber, was ist /+
. Um das Produkt zu berechnen, falten wir die Multiplikation mit /*
. Um diese beiden Ergebnisse zu einem Paar zu kombinieren, verwenden wir ein Paar Hooks (
und )
. Die Struktur hierfür ist:
()g
f
(Wo f
und g
sind Produkt bzw. Summe.) Versuchen wir herauszufinden, warum dies uns ein Paar von f(x)
und gibt g(x)
. Beachten Sie, dass der rechte Haken )
nur ein Argument hat. In diesem Fall bedeutet das andere Argument, dass es ;
die Argumente in ein Paar einschließt. Außerdem können Hooks auch als Binärfunktionen verwendet werden (was hier der Fall sein wird). In diesem Fall wenden sie einfach die innere Funktion nur auf ein Argument an. Also wirklich )
auf eine einzige Funktion g
gibt eine Funktion, die berechnet [x, g(y)]
. Wenn f
wir dies in einem linken Haken zusammen mit verwenden , erhalten wir [f(x), g(y)]
. Dies wird wiederum in einem unären Kontext verwendet, was bedeutet, dass es tatsächlich mit aufgerufen wird x == y
und wir am Ende mit [f(x), g(x)]
wie erforderlich enden . Puh.
Damit bleibt nur eines übrig, nämlich unsere frühere Testfunktion g
. Erinnern Sie sich, dass es aufgerufen wird, als g([p, s], x)
wo x
noch der aktuelle Eingabewert ist, p
ist sein stelliges Produkt und s
ist seine stellige Summe. Das ist g
:
&]&|
<*&d
N
Um die Teilbarkeit zu testen, verwenden wir natürlich Modulo, das |
in Jellyfish enthalten ist. Etwas ungewöhnlich ist, dass es seinen rechten Operanden modulo als linken Operanden verwendet, was bedeutet, dass die Argumente g
bereits in der richtigen Reihenfolge vorliegen (arithmetische Funktionen wie diese ziehen sich automatisch über Listen, sodass die beiden separaten Module kostenlos berechnet werden). . Unsere Zahl ist sowohl durch das Produkt als auch durch die Summe teilbar, wenn das Ergebnis ein Nullenpaar ist. Um zu überprüfen, ob dies der Fall ist, behandeln wir das Paar als eine Liste von Basis-2-Ziffern ( d
). Das Ergebnis davon ist Null, nur wenn beide Elemente des Paares Null sind. Daher können wir das Ergebnis von this ( N
) negieren , um einen Wahrheitswert dafür zu erhalten, ob beide Werte die Eingabe teilen. Beachten Sie, dass |
, d
undN
sind einfach alle zusammen mit einem Paar von &
s zusammengesetzt.
Leider ist das nicht die ganze Geschichte. Was ist, wenn das Ziffernprodukt Null ist? Division und Modulo durch Null geben beide Null in Jellyfish zurück. Während dies wie eine etwas seltsame Konvention erscheint, erweist es sich tatsächlich als etwas nützlich (da wir nicht auf Null prüfen müssen, bevor wir das Modulo ausführen). Es bedeutet jedoch auch, dass wir ein falsches Positiv erhalten können, wenn die Ziffernsumme die Eingabe teilt, das Ziffernprodukt jedoch Null ist (z 10
. B. Eingabe ).
Wir können dies beheben, indem wir unser Divisionsergebnis mit dem Zahlenprodukt multiplizieren (wenn also das Zahlenprodukt Null ist, wird unser Wahrheitswert ebenfalls zu Null). Es erweist sich als einfacher, das Ergebnis der Teilbarkeit mit dem Paar aus Produkt und Summe zu multiplizieren und anschließend das Ergebnis aus dem Produkt zu extrahieren.
Um das Ergebnis mit dem Paar zu multiplizieren, müssen wir zu einem früheren Wert zurückkehren (dem Paar). Dies geschieht mit einer Gabel ( ]
). Gabeln sind ein bisschen wie Haken an Steroiden. Wenn Sie ihnen zwei Funktionen f
und geben g
, stellen sie eine Binärfunktion dar, die berechnet f(a, g(a, b))
. In unserem Fall a
ist das Produkt / Summen-Paar b
der aktuelle Eingabewert, g
unser Teilbarkeitstest und f
die Multiplikation. Das alles berechnet sich also [p, s] * ([p, s] % x == [0, 0])
.
Jetzt müssen Sie nur noch den ersten Wert daraus extrahieren, den Endwert der im Iterator verwendeten Testfunktion. Dies ist so einfach wie das Erstellen ( &
) der Verzweigung mit der head- Funktion <
, die den ersten Wert einer Liste zurückgibt.