Golfscript - 12 Zeichen
{,1\{)*}/}:f
Erste Schritte mit Golfscript - Factorial Schritt für Schritt
Ist hier etwas für die Leute, die versuchen, golfscript zu erlernen. Voraussetzung ist ein grundlegendes Verständnis von Golfscript und die Fähigkeit, die Golfscript-Dokumentation zu lesen.
Also wollen wir unser neues Tool Golfscript ausprobieren . Es ist immer gut, mit etwas Einfachem zu beginnen, also fangen wir mit Fakultät an. Hier ist ein erster Versuch, der auf einem einfachen imperativen Pseudocode basiert:
# pseudocode: f(n){c=1;while(n>1){c*=n;n--};return c}
{:n;1:c;{n 1>}{n c*:c;n 1-:n;}while c}:f
Leerzeichen werden im Golfscript sehr selten verwendet. Der einfachste Trick, um Whitespace zu entfernen, ist die Verwendung verschiedener Variablennamen. Jedes Token kann als Variable verwendet werden (siehe Syntaxseite ). Nützliche Token zu verwenden , als Variablen sind Sonderzeichen wie |
, &
, ?
- in der Regel alles , was nicht an anderer Stelle im Code verwendet. Diese werden immer als einzelne Zeichen-Token analysiert. Im Gegensatz n
dazu benötigen Variablen wie ein Leerzeichen, um eine Zahl auf den Stapel zu verschieben. Zahlen sind im Wesentlichen vorinitialisierte Variablen.
Wie immer wird es Aussagen geben, die wir ändern können, ohne das Endergebnis zu beeinflussen. In golfscript wertet alles wahr , außer 0
, []
, ""
und {}
(siehe diese ). Hier können wir die Schleifenausgangsbedingung in einfach ändern {n}
(wir schleifen eine zusätzliche Zeit und beenden, wenn n = 0 ist).
Wie beim Golfspielen jeder Sprache hilft es, die verfügbaren Funktionen zu kennen. Zum Glück ist die Liste für golfscript sehr kurz. Wir können ändern 1-
, (
um einen anderen Charakter zu speichern. Gegenwärtig sieht der Code so aus: (Wir könnten 1
anstelle von |
hier verwenden, wenn wir wollten, was die Initialisierung fallen lassen würde.)
{:n;1:|;{n}{n|*:|;n(:n;}while|}:f
Es ist wichtig, den Stapel gut zu verwenden, um die kürzesten Lösungen zu erhalten (üben üben üben). Wenn Werte nur in einem kleinen Codesegment verwendet werden, ist es im Allgemeinen möglicherweise nicht erforderlich, sie in Variablen zu speichern. Durch Entfernen der aktiven Produktvariablen und einfaches Verwenden des Stapels können wir eine ganze Reihe von Zeichen speichern.
{:n;1{n}{n*n(:n;}while}:f
Hier ist noch etwas zum Nachdenken. Wir entfernen die Variable n
aus dem Stapel am Ende des Schleifenkörpers, verschieben sie jedoch sofort danach. In der Tat, bevor die Schleife beginnt, entfernen wir sie auch vom Stapel. Wir sollten es stattdessen auf dem Stapel belassen, und wir können die Schleifenbedingung leer lassen.
{1\:n{}{n*n(:n}while}:f
Vielleicht können wir die Variable sogar komplett entfernen. Dazu müssen wir die Variable immer auf dem Stapel halten. Dies bedeutet, dass wir am Ende der Bedingungsprüfung zwei Kopien der Variablen auf dem Stapel benötigen, damit wir sie nach der Prüfung nicht verlieren. Das bedeutet, dass wir 0
nach dem Ende der Schleife eine Redundanz auf dem Stack haben, die aber leicht zu beheben ist.
Dies führt uns zu unserer optimalen while
Schleifenlösung!
{1\{.}{.@*\(}while;}:f
Jetzt wollen wir das noch kürzer machen. Das offensichtliche Ziel sollte das Wort sein while
. In der Dokumentation gibt es zwei praktikable Alternativen - entfalten und tun . Wenn Sie die Wahl zwischen verschiedenen Routen haben, versuchen Sie, die Vorteile von beiden abzuwägen. Unfold ist so gut wie eine while-Schleife. Als Schätzung werden wir also die 5 Zeichen while
um 4 verringern /
. Was do
wir schneiden while
von 3 Zeichen, und erhalten die beiden Blöcke zu fusionieren, die einen anderen Charakter oder zwei retten könnte.
Die Verwendung einer do
Schleife hat tatsächlich einen großen Nachteil . Da die Bedingungsprüfung nach einmaliger Ausführung des Körpers durchgeführt wird, ist der Wert von 0
falsch, sodass möglicherweise eine if-Anweisung erforderlich ist. Ich sage Ihnen jetzt, dass die Entfaltung kürzer ist (einige Lösungen do
werden am Ende bereitgestellt). Probieren Sie es aus, der Code, den wir bereits haben, erfordert nur minimale Änderungen.
{1\{}{.@*\(}/;}:f
Toll! Unsere Lösung ist jetzt superkurz und wir sind hier fertig, oder? Nee. Dies ist 17 Zeichen und J hat 12 Zeichen. Gib niemals eine Niederlage zu!
Jetzt denkst du mit ... Rekursion
Rekursion bedeutet , dass wir müssen eine Verzweigungsstruktur verwenden. Leider, aber da Fakultät so prägnant rekursiv ausgedrückt werden kann, scheint dies eine gangbare Alternative zur Iteration zu sein.
# pseudocode: f(n){return n==0?n*f(n-1):1}
{:n{n.(f*}1if}:f # taking advantage of the tokeniser
Nun, das war einfach - hätten wir früher eine Rekursion versucht, hätten wir uns vielleicht nicht einmal die Verwendung einer while
Schleife angeschaut ! Trotzdem sind wir nur bei 16 Zeichen.
Arrays
Arrays werden in der Regel auf zwei Arten erstellt - die Verwendung [
und ]
Zeichen oder mit der ,
Funktion. Bei Ausführung mit einer Ganzzahl oben im Stapel wird ,
ein Array dieser Länge mit arr [i] = i zurückgegeben.
Für die Iteration über Arrays haben wir drei Möglichkeiten:
{block}/
: schieben, blockieren, schieben, blockieren, ...
{block}%
: [push, block, push, block, ...] (dies hat einige Nuancen, zB Zwischenwerte werden vor jedem Push vom Stack entfernt)
{block}*
: drücken, drücken, blockieren, drücken, blockieren, ...
In der Golfscript-Dokumentation wird beispielhaft {+}*
der Inhalt eines Arrays summiert. Dies legt nahe, dass wir {*}*
das Produkt eines Arrays erhalten können.
{,{*}*}:f
Ganz so einfach ist es leider nicht. Alle Elemente sind um eins ( [0 1 2]
anstelle von [1 2 3]
) deaktiviert. Wir können {)}%
dieses Problem beheben.
{,{)}%{*}*}:f
Nicht ganz. Dies behandelt Null nicht richtig. Wir können (n + 1)! / (N + 1) berechnen, um dies zu korrigieren, obwohl dies viel zu viel kostet.
{).,{)}%{*}*\/}:f
Wir können auch versuchen, n = 0 im selben Bucket wie n = 1 zu behandeln. Dies ist tatsächlich extrem kurz zu tun, versuchen Sie, so schnell wie möglich zu trainieren.
Nicht so gut ist die Sortierung, bei 7 Zeichen: [1\]$1=
. Beachten Sie, dass diese Sortiertechnik nützliche Funktionen hat, z. B. das Auferlegen von Grenzen für eine Zahl (z. B. `[0 \ 100] $ 1 =).
Hier ist der Gewinner mit nur 3 Zeichen:.! +
Wenn wir das Inkrement und die Multiplikation im selben Block haben wollen, sollten wir jedes Element im Array durchlaufen. Da wir kein Array erstellen {)*}/
, sollten wir es verwenden , was uns zur kürzesten Implementierung von Fakultät bringt! Bei einer Länge von 12 Zeichen ist dies mit J verknüpft!
{,1\{)*}/}:f
Bonuslösungen
Beginnen Sie mit einer einfachen if
Lösung für eine do
Schleife:
{.{1\{.@*\(.}do;}{)}if}:f
Wir können ein paar mehr herausholen. Ein bisschen kompliziert, also müssen Sie sich selbst davon überzeugen, dass diese Arbeiten funktionieren. Stellen Sie sicher, dass Sie alle diese verstehen.
{1\.!!{{.@*\(.}do}*+}:f
{.!{1\{.@*\(.}do}or+}:f
{.{1\{.@*\(.}do}1if+}:f
Eine bessere Alternative ist die Berechnung von (n + 1)! / (N + 1), wodurch die Notwendigkeit einer if
Struktur entfällt .
{).1\{.@*\(.}do;\/}:f
Die kürzeste do
Lösung benötigt hier jedoch ein paar Zeichen, um 0 zu 1 und alles andere für sich selbst zuzuordnen - wir brauchen also keine Verzweigung. Diese Art der Optimierung ist sehr leicht zu übersehen.
{.!+1\{.@*\(.}do;}:f
Für alle Interessierten gibt es hier einige alternative rekursive Lösungen mit der gleichen Länge wie oben:
{.!{.)f*0}or+}:f
{.{.)f*0}1if+}:f
{.{.(f*}{)}if}:f
* Hinweis: Ich habe noch nicht viele der Code-Teile in diesem Beitrag getestet. Sie können sich also jederzeit informieren, wenn Fehler auftreten.