Ich analysiere Ihren Code im Abschnitt Analysieren Ihres Codes . Vorher präsentiere ich ein paar lustige Abschnitte mit Bonusmaterial.
Ein Liner Ein Buchstabe 1
say e; # 2.718281828459045
Klicken Sie auf den obigen Link, um Damian Conways außergewöhnlichen Artikel über Computer e
in Raku zu lesen.
Der Artikel macht viel Spaß (schließlich ist es Damian). Es ist eine sehr verständliche Diskussion über Computer e
. Und es ist eine Hommage an Rakus Bikarbonat-Reinkarnation der von Larry Wall vertretenen TIMTOWTDI-Philosophie. 3
Als Vorspeise hier ein Zitat aus der Mitte des Artikels:
Angesichts der Tatsache, dass diese effizienten Methoden alle auf die gleiche Weise funktionieren - durch Summieren (einer anfänglichen Teilmenge) einer unendlichen Reihe von Begriffen - wäre es vielleicht besser, wenn wir eine Funktion hätten, die dies für uns erledigt. Und es wäre sicherlich besser, wenn die Funktion selbst genau herausfinden könnte, wie viel von dieser anfänglichen Teilmenge der Serie tatsächlich enthalten sein muss, um eine genaue Antwort zu erhalten ... anstatt dass wir die Ergebnisse von manuell durchkämmen müssen mehrere Versuche, um das zu entdecken.
Und wie so oft in Raku ist es überraschend einfach, genau das zu bauen, was wir brauchen:
sub Σ (Unary $block --> Numeric) {
(0..∞).map($block).produce(&[+]).&converge
}
Analysieren Sie Ihren Code
Hier ist die erste Zeile, in der die Serie generiert wird:
my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
Der Abschluss ( { code goes here }
) berechnet einen Begriff. Ein Abschluss hat eine implizite oder explizite Signatur, die bestimmt, wie viele Argumente er akzeptiert. In diesem Fall gibt es keine explizite Signatur. Die Verwendung von $_
( der Variablen "topic" ) führt zu einer impliziten Signatur, für die ein Argument erforderlich ist, an das gebunden ist $_
.
Der Sequenzoperator ( ...
) ruft wiederholt den Abschluss auf der linken Seite auf und übergibt den vorherigen Begriff als Argument des Abschlusses, um träge eine Reihe von Begriffen bis zum Endpunkt auf der rechten Seite zu erstellen, in diesem Fall *
Abkürzung für Inf
aka Infinity.
Das Thema im ersten Aufruf zur Schließung ist 1
. Der Abschluss berechnet und gibt also 1 / (1 * 1)
die ersten beiden Terme der Reihe als zurück 1, 1/1
.
Das Thema im zweiten Aufruf ist der Wert des vorherigen 1/1
, dh 1
erneut. Der Abschluss berechnet und kehrt zurück 1 / (1 * 2)
und erweitert die Serie auf 1, 1/1, 1/2
. Es sieht alles gut aus.
Der nächste Abschluss berechnet, 1 / (1/2 * 3)
welches ist 0.666667
. Dieser Begriff sollte sein 1 / (1 * 2 * 3)
. Hoppla.
Passen Sie Ihren Code an die Formel an
Ihr Code soll mit der Formel übereinstimmen:
In dieser Formel wird jeder Term basierend auf seiner Position in der Reihe berechnet . Der k- te Term in der Reihe (wobei k = 0 für den ersten ist 1
) ist nur der Kehrwert von Fakultät k .
(Es hat also nichts mit dem Wert des vorherigen Terms zu tun . Daher $_
sollte der Wert , der den Wert des vorherigen Terms erhält , nicht für den Abschluss verwendet werden.)
Erstellen wir einen faktoriellen Postfix-Operator:
sub postfix:<!> (\k) { [×] 1 .. k }
( ×
ist ein Infix-Multiplikationsoperator, ein besser aussehender Unicode-Alias des üblichen ASCII-Infix *
.)
Das ist eine Abkürzung für:
sub postfix:<!> (\k) { 1 × 2 × 3 × .... × k }
(Ich habe die pseudometasyntaktische Notation in geschweiften Klammern verwendet, um die Idee zu kennzeichnen, so viele Begriffe wie erforderlich zu addieren oder zu subtrahieren.
Im Allgemeinen bildet das Setzen eines Infix-Operators op
in eckige Klammern am Anfang eines Ausdrucks einen zusammengesetzten Präfix-Operator, der dem entspricht reduce with => &[op],
. Weitere Informationen finden Sie unter Reduktions-Metaoperator .
Jetzt können wir den Abschluss neu schreiben, um den neuen faktoriellen Postfix-Operator zu verwenden:
my @e = 1, { state $a=1; 1 / $a++! } ... *;
Bingo. Dies ergibt die richtige Serie.
... bis es aus einem anderen Grund nicht mehr geht. Das nächste Problem ist die numerische Genauigkeit. Aber lassen Sie uns das im nächsten Abschnitt behandeln.
Ein Einzeiler, der von Ihrem Code abgeleitet ist
Vielleicht komprimieren Sie die drei Zeilen auf eine:
say [+] .[^10] given 1, { 1 / [×] 1 .. ++$ } ... Inf
.[^10]
gilt für das Thema, das von der festgelegt wird given
. ( ^10
ist eine Abkürzung für 0..9
, daher berechnet der obige Code die Summe der ersten zehn Terme in der Reihe.)
Ich habe $a
das nächste Semester aus dem Closure Computing gestrichen . Ein einsamer $
ist der gleiche wie (state $)
ein anonymer Zustandsskalar. Ich habe es zu einem Pre-Inkrement anstatt zu einem Post-Inkrement gemacht, um den gleichen Effekt wie bei der Initialisierung $a
von zu erzielen 1
.
Wir haben jetzt das letzte (große!) Problem, auf das Sie in einem Kommentar unten hingewiesen haben.
Vorausgesetzt, keiner seiner Operanden ist ein Num
(ein Float und somit ungefähr), gibt der /
Operator normalerweise eine 100% ige Genauigkeit zurück Rat
(ein rationales Rational mit begrenzter Genauigkeit). Wenn der Nenner des Ergebnisses jedoch 64 Bit überschreitet, wird dieses Ergebnis in a umgewandelt, Num
das die Leistung gegen Genauigkeit eintauscht, ein Kompromiss, den wir nicht eingehen möchten. Das müssen wir berücksichtigen.
Um eine unbegrenzte Genauigkeit sowie eine 100% ige Genauigkeit festzulegen , zwingen Sie die Operation einfach zur Verwendung von FatRat
s. Um dies richtig zu machen, machen Sie einfach (mindestens) einen der Operanden zu einem FatRat
(und keinen anderen zu einem Num
):
say [+] .[^500] given 1, { 1.FatRat / [×] 1 .. ++$ } ... Inf
Ich habe dies auf 500 Dezimalstellen überprüft. Ich erwarte, dass es genau bleibt, bis das Programm abstürzt, weil ein Limit der Raku-Sprache oder des Rakudo-Compilers überschritten wird. (Siehe meine Antwort auf Kann 65536 Bit breites Bigint nicht in native Ganzzahl entpacken, um eine Diskussion darüber zu erhalten.)
Fußnoten
1 Raku hat einige wichtige mathematische Konstanten eingebaut, einschließlich e
, i
und pi
(und dessen Alias π
). So kann man Eulers Identität in Raku so schreiben, wie es in Mathematikbüchern aussieht. Mit Anerkennung für RosettaCodes Raku-Eintrag für Eulers Identität :
# There's an invisible character between <> and iπ character pairs!
sub infix:<> (\left, \right) is tighter(&infix:<**>) { left * right };
# Raku doesn't have built in symbolic math so use approximate equal
say e**iπ + 1 ≅ 0; # True
2 Damians Artikel ist ein Muss. Aber es ist nur eine von mehreren bewundernswerten Behandlungen, die zu den über 100 Übereinstimmungen für ein Google für "Raku" Eulernummer "gehören .
3 Siehe TIMTOWTDI vs TSBO-Apoo-OWTDI für eine der ausgeglicheneren Blick auf TIMTOWTDI von einem Fan von Python geschrieben. Aber es gibt Nachteile, TIMTOWTDI zu weit zu bringen. Um diese letztere "Gefahr" widerzuspiegeln, prägte die Perl-Community die humorvoll lange, unlesbare und untertriebene TIMTOWTDIBSCINABTE - Es gibt mehr als einen Weg, dies zu tun, aber manchmal ist Konsistenz auch keine schlechte Sache, ausgesprochen "Tim Toady Bicarbonate". Seltsamerweise verwendete Larry Bicarbonat auf Rakus Design und Damian wendete es auf das Rechnen e
in Raku an.