Brainf * ck hat 8 Arten von Anweisungen
>
Inkrementiere den Zeiger. Wenn der Zeiger ptr ist, entspricht er "ptr ++;
" in der Sprache C.<
Dekrementiert den Zeiger. Entspricht "ptr-;
" in C-Sprache.+
Erhöht den Wert, auf den der Zeiger zeigt. Entspricht "(* ptr) ++;
" in C-Sprache.-
Dekrementiert den Wert, auf den der Zeiger zeigt. Entspricht "(* ptr) -;
" in C-Sprache..
Schreiben Sie den Wert, auf den der Zeiger auf die Ausgabe zeigt. Entspricht "putchar (* ptr);" in C-Sprache.,
Liest 1 Byte von der Eingabe und weist es dem Ziel zu, auf das der Zeiger zeigt. Entspricht "* ptr = getchar ();` "in C-Sprache.[
Wenn der Wert, auf den der Zeiger zeigt, 0 ist, springen Sie unmittelbar nach dem entsprechenden ]
. Entspricht " while (* ptr) {
" in C-Sprache.]
Wenn der Wert, auf den der Zeiger zeigt, nicht Null ist, springen Sie unmittelbar nach dem entsprechenden [
. Entspricht "}
" in C-Sprache.Die Erklärung stammt aus dem Abschnitt Brainfuck (ja) von Wikipedia.
Brainf * ck verfügt über ein Array von mindestens 30000 Zellen und einen Eingabe- / Ausgabebyte-Stream, der bearbeitet werden kann, um eine Verarbeitung zu erreichen.
Laut der englischen Version von Wikipedia Brainfuck (en) gibt es vier Probleme mit der Portabilität von Brainf * ck:
Cell size
Die Größe der Zelle, auf die der Zeiger zeigt, beträgt im Allgemeinen 8 Bit, es gibt jedoch auch andere Größen.
Es scheint 16-Bit-, 32-Bit- oder andere Implementierungen zu geben.
Array size
In der klassischen Implementierung besteht das Array aus 30000 Zellen, wobei sich der erste Zeiger am linken Rand befindet (Position 0).
Es scheint jedoch eine Implementierung zu geben, die eine Zelle auf der linken Seite hat (der Index des Arrays ist negativ).
Es scheint auch Implementierungen zu geben, die einen Fehler verursachen, wenn sie über den Bereich des Arrays hinausgehen, Implementierungen, die das Array erweitern, Implementierungen, die sich in die entgegengesetzte Richtung des Arrays bewegen, und Implementierungen, die den Bereich nicht überprüfen (Moos).
End-of-line code
Nicht auf Brainf * ck beschränkt, aber der Zeilenvorschubcode ist oft unterschiedlich.
Die meisten behandeln LF (\ n
) als Zeilenvorschubcode, aber es gibt Implementierungen (oder Betriebssysteme), die nur CR + LF ( \ r \ n
) oder CR (\ r
) verwenden.
End-of-file behavior
Es scheint, dass es drei Verhaltensweisen gibt (hauptsächlich?), Wenn man das Ende der Datei liest, abhängig von der Implementierung.
EOF
)Um ein Programm mit einigen Funktionen mit Brainf * ck zu erstellen, dachte ich, dass es besser wäre, dem Compiler einige Funktionen hinzuzufügen, also habe ich den Compiler "bf2c" in C-Sprache erstellt. Es ist ein Compiler oder genauer gesagt ein Übersetzer, der die Quelle von Brainf * ck in C übersetzt. Die Codeoptimierung bleibt dem C-Compiler überlassen.
Es hat die folgenden Funktionen
Die folgenden Optionen können für "bf2c" angegeben werden.
-o
oder-output
: Geben Sie die Ausgabedatei (C-Quelle) an.-F
oder --force-flush
: Force Flush für jedes Zeichen-O
oder --output-default
: Legen Sie das Standardargument des Ausführungsbefehls als Ausgabepfad fest-I
oder --input-default
: Legen Sie das Standardargument des Ausführungsbefehls als Eingabepfad fest-M
oder --message-default
: Legen Sie das Standardargument des Ausführungsbefehls als Nachricht fest-s
oder --size
: Gibt die Anzahl der Speicherarrays an (Standard 30000).-1
oder --cell-chear
: Macht das Speicherarray zu einem Array von char (Standard)-2
oder --cell-short
: Machen Sie das Speicherarray zu einem kurzen Array-4
oder --cell-int
: Das Speicherarray sei ein int-Array-z
oder --eof-zero
: Setze EOF auf 0-m
oder --eof-minus
: EOF ist -1 (Standard)-n
oder --eof-no-effect
: Laden Sie EOF nicht-C
oder --copyright
: Geben Sie die Copyright- oder Lizenzanzeige an-V
oder --version-string
: Geben Sie die Versionsinformationen an-v
oder --version
: Zeigt die Version des Compilers an-h
oder --help
: Hilfemeldung anzeigenSelbst wenn Sie "-2" oder "-4" angeben, hängt es vom C-Compiler ab, ob die Zellengröße 16 Bit oder 32 Bit beträgt. Verwenden Sie "short" bzw. "int" als Spezifikationen.
Wenn Sie die Versionsinformationen nicht mit -V angeben, werden die Standardinformationen basierend auf dem Namen der Quelldatei, dem aktuellen Datum, dem Anmeldenamen usw. eingebettet. Überprüfen Sie für den Anmeldenamen die Umgebungsvariablen in der Reihenfolge LOGNAME`` USER`` LNAME`` USERNAME
und verwenden Sie die mit dem ersten nicht leeren Zeichensatz (die zu überprüfende Umgebungsvariable ist Python (Siehe getpass.getuser ()
). Wenn nichts festgelegt ist, verwenden Sie "noname".
Der erzwungene Blitz von "-F" wurde hinzugefügt, damit er zeichenweise angezeigt werden kann, da das Programm des selbst erstellten Mandelbrot-Sets, das ich zu Beginn erstellt habe, ziemlich langsam war und ich nicht wusste, ob es mit einem zeilenweisen Blitz funktioniert Hat.
Der kompilierte Ausführungsbefehl akzeptiert die folgenden Optionen
-f
oder --file
: Eingabedatei-m
oder --message
: Nachricht eingeben-o
oder-output
: Ausgabedatei-v
oder --version
: Versionsinformationen anzeigen-h
oder --help
: Hilfemeldung anzeigenWenn die Brainf * ck-Quelldatei jedoch nicht den Befehl ,
verwendet, können Sie keine Optionen für die Eingabedatei und die Eingabenachricht angeben.
Ebenso können Sie keine Ausgabedateioptionen angeben, wenn der Befehl .
nicht verwendet wird.
Außerdem werden die Versionsinformationen und die Hilfemeldung auf die gleiche Weise angezeigt.
Wenn die Eingabedatei und die Eingabenachricht nicht angegeben sind, wird die Standardeingabe gelesen. Wenn keine Ausgabedatei angegeben ist, wird in die Standardausgabe geschrieben.
Wenn das Standardargument des Ausführungsbefehls zur Kompilierungszeit angegeben wurde (-I
oder -M
oder -O
), kann nur ein Argument ohne Option angegeben werden.
Die Anweisungen von Brainf * ck sind einfach, aber Sie können mehrere kombinieren, um einen ganzen Job zu erledigen. Es gibt verschiedene Beschreibungen dieses Levels in Qiitas Artikel, daher werde ich es in Eile kurz erklären.
Sie können den Wert nicht direkt in der Zelle festlegen, aber Sie können einen beliebigen Wert festlegen, indem Sie "+" und "-" kombinieren.
Sie können den Wert der Zelle, auf die Sie gerade zeigen, löschen, indem Sie etwas wie "[-]" ausführen.
Um die Funktion zu realisieren, die der "while" -Anweisung einer allgemeinen Sprache entspricht, ist es möglich, sie zu realisieren, indem eine Zelle für die Steuerung vorbereitet wird.
Wenn beispielsweise die Zelle links von einer die Kontrollzelle ist, können Sie die Schleife mit <[> (Verarbeitung in der Schleife) <]>
verarbeiten.
Am Ende von "Processing in Loop" muss der Zeiger jedoch wieder in die Ausgangsposition gebracht werden.
Wenn sich der Zähler auf der linken Seite befindet, lautet die Funktion, die der Anweisung "for" entspricht, die die angegebene Anzahl von Schleifen durchläuft, "<[-> (Verarbeitung in der Schleife) <]>". In diesem Fall wurde der Zähler während der Verarbeitung dekrementiert und ist am Ende der Schleife 0. Es muss im Voraus kopiert werden, um eine Zerstörung des Zählers zu vermeiden.
Um beispielsweise den Wert, auf den der aktuelle Zeiger zeigt, zu dem Wert daneben hinzuzufügen, können Sie [-> + <]
verwenden.
1
Wenn Sie dies beispielsweise anwenden, um es zu verdoppeln, wäre dies "[-> ++ <]".
Verwenden Sie [-> +> + <<]
, um zwei Stellen (auf der rechten und auf der rechten Seite) hinzuzufügen.
Sie können den Wert kopieren, vorausgesetzt, die rechte Seite enthält alle Nullen. Geben Sie zuerst den gleichen Wert eins neben eins und zwei daneben als "[-> +> + <<]" ein.
Danach können Sie den Wert kopieren, indem Sie zu den nächsten beiden wechseln und den Wert an der ursprünglichen Position hinzufügen (destruktive Version) [- << +] <<
.
Die Verarbeitung von Brainf * ck ist grundsätzlich destruktiv, so dass das Kopieren von Daten sehr häufig ist.
Bedingte Verzweigungen wie die if-Anweisung in allgemeinen Sprachen werden ebenfalls in Form einer einmaligen Schleife realisiert.
Wenn Sie beispielsweise (dann) Prozess 1 ausführen möchten, wenn der aktuelle Wert nicht 0 ist, und (sonst) Prozess 2, wenn er 0 ist, bereiten Sie eine Zelle als Arbeit vor und verwenden Sie sie als Flag. Hier verwenden wir das nächste als Flag für else und nehmen an, dass der Wert 0 ist.
Setzen Sie zuerst das else-Flag auf 1 > + <
.
Und Schleife mit dem aktuellen Wert [
. Wenn der aktuelle Wert nicht Null ist, wird er in die Schleife eingegeben, und wenn er 0 ist, wird er nicht in die Schleife eingegeben.
Löschen Sie innerhalb der Schleife den aktuellen Wert auf 0, [-]
und löschen Sie auch das else-Flag > - <
. Danach wird Prozess 1 ausgeführt und die Schleife wird "]" verlassen.
Dann Schleife basierend auf dem else-Flag > [
. Löschen Sie das Flag für else, -
, führen Sie Prozess 2 aus und verlassen Sie die Schleife] <
.
Dies ist der gleiche Vorgang wie "wenn". Am Ende des Prozesses sind sowohl der aktuelle Wert als auch das Flag 0.
Zusammenfassend ist > + <[[-]> - <(Prozess 1)]> [- (Prozess 2)] <
Es sieht so aus.
Wenn Sie die oben genannten Inhalte zusammenstellen, können Sie verschiedene Prozesse ausführen.
Aber ehrlich gesagt, du kannst es nicht tun, oder?
Aus diesem Grund habe ich beschlossen, eine bestimmte Menge an zusammenhängender Verarbeitung als Makro vorzubereiten und für die Programmierung zu verwenden.
Betrachten Sie vorerst ein Makro, das eine Funktion (oder Methode) aufruft und die entsprechende Brainf * ck-Anweisung zurückgibt.
Ich habe das Makro vorerst in Python geschrieben.
Ein Makro, das einen Zeiger bewegt, sieht beispielsweise so aus.
def move_ptr(pos: int) -> str:
"""
Zeiger bewegen
>>> move_ptr(0)
''
>>> move_ptr(2)
'>>'
>>> move_ptr(-3)
'<<<'
"""
return ">" * pos if 0 <= pos else "<" * (-pos)
Wenn Sie dies tun, sieht das Makro (vorläufig), das an der angegebenen Position verarbeitet wird und nach Abschluss an die ursprüngliche Position zurückkehrt, folgendermaßen aus.
def exec_pos(pos: int, statement: str) -> str:
"Führen Sie die Verarbeitung an der angegebenen Position aus"
return move_ptr(pos) + statement + move_ptr(-pos)
Das Programmieren einer Kombination von Makros kann jedoch zu unnötiger Verarbeitung führen.
Beispielsweise gibt ein Prozess wie "exec_pos (3," + ") + exec_pos (4," - ")" das Ergebnis ">>> + <<< >>>> - <<<<" aus Ich werde am Ende. Das <<< >>>
auf dem Weg ist nutzlos, nicht wahr?
Also werden wir zuerst einige grundlegende Makros erstellen.
import re
def delete_useless(statement: str) -> str:
"""
Nutzlose Bewegung/Berechnungen versetzen und löschen
>>> delete_useless("+++--<<>>>")
'+>'
>>> delete_useless("---++>><<<")
'-<'
>>> delete_useless(">++++[-][-]")
'>[-]'
>>> delete_useless(">--[-]++[-]")
'>[-]'
"""
while True:
if "<>" in statement:
#Kompensation nutzloser Bewegungen, Teil 1
statement = statement.replace("<>", "")
continue
if "><" in statement:
#Kompensation nutzloser Bewegungen, Teil 2
statement = statement.replace("><", "")
continue
if "+-" in statement:
#Gegen nutzlose Addition und Subtraktion, Teil 1
statement = statement.replace("+-", "")
continue
if "-+" in statement:
#Gegen nutzlose Addition und Subtraktion, Teil 2
statement = statement.replace("-+", "")
continue
if "+[-]" in statement or "-[-]" in statement:
#Entfernen Sie Addition / Subtraktion, bevor Sie Null löschen
statement = re.sub(r'[-+]+\[-\]', "[-]", statement)
continue
if "[-][-]" in statement:
#Mehrere Nulllöschungen gleichzeitig
statement = statement.replace("[-][-]", "[-]")
continue
break
return statement
def delete_useless_all(statement: str) -> str:
"""
Nutzlose Bewegung/Berechnungen versetzen und löschen. Löschen Sie den letzten nutzlosen Prozess
>>> delete_useless_all("[+>-<]>++++[-][-]")
'[+>-<]'
>>> delete_useless_all("[-<+>][-]>>++")
'[-<+>]'
>>> delete_useless_all("[-<+>][-]<<--")
'[-<+>]'
"""
statement = delete_useless(statement)
while statement:
if statement[-1] in "-+><":
#Am Ende"+" "-" ">" "<"Ist gelöscht
statement = re.sub(r'[-+><]+$', "", statement)
continue
if statement.endswith("[-]"):
#Am Ende"[-]"Ist gelöscht
statement = re.sub(r'\[-\]$', "", statement)
continue
break
return statement
def block_of(*statements: str) -> str:
"""
Kombinieren Sie mehrere Anweisungen
>>> block_of("[", "-", "]")
'[-]'
"""
return delete_useless("".join(statements))
def program_of(*statements: str) -> str:
source = delete_useless_all("".join(statements))
source = re.sub(r'(.{1,72})', "\\1\n", source)
return source
Irgendwie gibt es eine verrückte Logik, aber es ist mir egal, weil es sowieso ein Einwegcode ist. Der Funktionsname ist ebenfalls unklar, aber ich kann nichts dafür, weil ich kein Englisch verstehe.
Kurz gesagt, es gibt Makros, die unnötige Bewegungen und Berechnungen löschen, die leicht zu verstehen sind, und Makros blockieren, die das gemeinsame Schreiben von Makros erleichtern.
Mit diesen kann das vorherige exec_pos
auch wie folgt umgeschrieben werden.
def exec_pos(pos: int, statement: str) -> str:
"""
Führen Sie die Verarbeitung an der angegebenen Position aus
>>> exec_pos(3, "+")
'>>>+<<<'
>>> exec_pos(3, "<+>")
'>>+<<'
"""
return block_of(
move_ptr(pos),
statement,
move_ptr(-pos)
)
Das Werteinstellungsmakro sieht wie folgt aus.
def inc_pos(pos: int) -> str:
"""
Erhöhen Sie die angegebene Position
>>> inc_pos(2)
'>>+<<'
"""
return exec_pos(pos, "+")
def dec_pos(pos: int) -> str:
"""
Verringern Sie die angegebene Position
>>> dec_pos(3)
'>>>-<<<'
"""
return exec_pos(pos, "-")
def clear_pos(pos: int) -> str:
"""
Löschen Sie die angegebene Position
>>> clear_pos(3)
'>>>[-]<<<'
"""
return exec_pos(pos, "[-]")
def set_value(pos: int, value: int) -> str:
"""
Stellen Sie den angegebenen Wert ein
>>> set_value(2, 3)
'>>[-]+++<<'
>>> set_value(2, -1)
'>>[-]-<<'
"""
(op, value) = ("+", value) if 0 < value else ("-", -value)
return block_of(
clear_pos(pos),
exec_pos(pos, op * value)
)
Die Logik von set_value
scheint ein wenig verrückt zu sein, aber es sieht so aus, unter der Voraussetzung, dass die Arbeit nicht verwendet werden kann.
Wenn Sie beispielsweise den Anfangswert festlegen möchten (alle Nullen auf der rechten Seite können für die Arbeit verwendet werden), können Sie ihn mit etwas mehr Gedanken festlegen.
import math
def _init_value_sub(value: int) -> str:
"Anfangswerteinstellung.Voraussetzung ist, dass es nach dem nächsten für die Arbeit verwendet werden kann"
(op1, op2) = ("+", "-")
if value < 0:
value = -value
(op1, op2) = ("-", "+")
if value < 16:
return op1 * value
len0 = value
str0 = op1 * value
xmin = int(math.sqrt(value))
xmax = int(math.ceil(value / 2.0))
for x in range(xmin, xmax + 1):
strx = _init_value_sub(x)
lenx = len(strx)
# len0 = x * y1 +In c1-Form unterteilt
y1 = value // x
c1 = value % x
len1 = lenx + y1 + c1 + 7
if len1 < len0:
len0 = len1
str0 = ">" + strx + "[<" + op1 * y1 + ">-]<" + op1 * c1
if c1 != 0:
# len0 = x * y2 -In c1-Form unterteilt
y2 = y1 + 1
c2 = x - c1
len2 = lenx + y2 + c2 + 7
if len2 < len0:
len0 = len2
str0 = ">" + strx + "[<" + op1 * y2 + ">-]<" + op2 * c2
return str0
def init_value(pos: int, value: int) -> str:
"""
Anfangswerteinstellung.
Voraussetzung ist, dass es nach dem nächsten für die Arbeit verwendet werden kann.Seien Sie in der Reihenfolge der Initialisierung vorsichtig
"""
return delete_useless(exec_pos(pos, _init_value_sub(value)))
Unter der Annahme, dass Sie in die Sprache C übersetzen und dann die Optimierung anwenden, kann es sein, dass "set_value ()", was verrückt zu sein scheint, effizienter ist.
Es ist ein Schleifensystem.
def while_loop(pos: int, *statements: str) -> str:
"""
while-Schleife.
>>> while_loop(3, ">>+<<")
'>>>[<+>]<<<'
"""
return block_of(
exec_pos(pos, "["),
block_of(*statements),
exec_pos(pos, "]")
)
def for_loop(pos: int, *statements: str) -> str:
"""
Wiederholung.Zerstörerische Version
>>> for_loop(3, "+")
'>>>[-<<<+>>>]<<<'
"""
return block_of(
exec_pos(pos, "[-"),
block_of(*statements),
exec_pos(pos, "]")
)
def for_safe(pos: int, work1: int, statement: str) -> str:
"""
Wiederholung.Zerstörungsfreie Version(Aktualisieren Sie jedoch den Verweis auf pos während der Schleife nicht.)
>>> for_safe(3, 4, "+")
'>>>[->+<<<<+>>>]>[-<+>]<<<<'
"""
return block_of(
for_loop(pos, inc_pos(work1), statement),
move_data(work1, pos)
)
Die zerstörungsfreie Version der "for" -Schleife wird zerstört und dann zum Ende zurückgebracht.
Wenn Sie zwei Werke haben, können Sie den Wert kopieren und dann am Kopierziel eine Schleife ausführen. Derzeit müssen Sie jedoch nur den Wert in der Schleife anzeigen. Dies ist also der erste Schritt.
Daten verschieben oder kopieren.
def move_data(source: int, destination: int) -> str:
"1 Byte verschieben/Oder hinzufügen."
return for_loop(source, inc_pos(destination))
def copy_data(source: int, destination: int, work1: int) -> str:
"1-Byte-Kopie."
return block_of(
clear_pos(destination),
for_safe(source, work1, inc_pos(destination))
)
def override_data(source: int, destination: int) -> str:
"1 Byte verschieben."
return block_of(
clear_pos(destination),
move_data(source, destination)
)
def swap_data(target1: int, target2: int, work1: int) -> str:
"Werte zwischen 1 Byte austauschen"
return block_of(
move_data(target1, work1),
move_data(target2, target1),
move_data(work1, target2)
)
Ein einfaches if-Anweisungsmakro.
Es ist Arbeit erforderlich, um die else-Klausel anzuhängen, also vorerst nur dann.
def if_nz_then(pos: int, then_statement: str) -> str:
"if_Zerstörerische Version von nz.Eine vereinfachte Version von nur dann"
return while_loop(
pos,
clear_pos(pos),
then_statement
)
def if_one_then(pos: int, then_statement: str) -> str:
"Angenommen, die Position von pos ist 1 oder 0.Prozess wenn 1(Zerstörerische Version)."
return while_loop(
pos,
dec_pos(pos),
then_statement
)
if_one_then
ist fast dasselbe wie if_nz_then
, aber wenn der Wert entweder 0 oder 1 ist, ist die Quellgröße etwas kleiner, also haben wir es vorbereitet.
Vielleicht macht es nicht viel Sinn.
Wir haben auch einige seltsame Makros. Die zerstörungsfreie Version der if-Anweisung, die mir in den Sinn kam, und die damit verbundene arithmetische Verarbeitung (Addition, Subtraktion, Multiplikation mit Carry).
Da die Arbeit jedoch an einer seltsamen Position erforderlich ist, ist es ein etwas verwirrendes Makro, dass sich die Bewegung des Zeigers im Fall der "then" -Klausel und im Fall der "else" -Klausel unterscheidet (schließlich gleich wird).
Vielleicht ist es ein Hindernis für die Optimierung in der C-Sprache.
def if_nz_tricky(
pos: int,
n: int,
m: int,
then_statement: str,
else_statement: str = "") -> str:
"""
if_Zerstörungsfreie Version von nz.Ein bisschen knifflig
Voraussetzungen
*(ptr + pos + n) == 0
*(ptr + pos + m) == 0
*(ptr + pos + n + m) == 0
※ n ==m ist OK
"""
return block_of(
move_ptr(pos),
inc_pos(n), # pos+n =Flagge für sonst
"[", #Für Neuseeland
dec_pos(n), #Deaktivieren Sie das else-Flag
exec_pos(-pos, then_statement),
move_ptr(m), #NZ ist pos+m /Z bleibt pos
"c]",
move_ptr(n), #NZ ist pos+n+m /Z ist pos+n
"[-", #Für Z.
exec_pos(-pos - n, else_statement),
move_ptr(m), #NZ ist pos+n+Bleib m/Z auch pos+n+Gehe zu m
"c]",
move_ptr(-pos - n - m)
)
def if_z_tricky(
pos: int,
n: int,
m: int,
then_statement: str,
else_statement: str = "") -> str:
"""
if_Zerstörungsfreie Version von z.Ein bisschen knifflig
Voraussetzungen
*(ptr + pos + n) == 0
*(ptr + pos + m) == 0
*(ptr + pos + n + m) == 0
※ n ==m ist OK
"""
return if_nz_tricky(pos, n, m, else_statement, then_statement)
def inc_data_tricky(pos: int, digit: int) -> str:
"""
Inkrement mit tragen.Ein bisschen knifflig
Voraussetzungen
*(ptr + pos + 1) == 0
*(ptr + pos + 2) == 0
"""
if 1 < digit:
#Müssen tragen
return block_of(
inc_pos(pos),
if_z_tricky(pos, 1, 1,
inc_data_tricky(pos - 1, digit - 1))
)
else:
#Keine Notwendigkeit zu tragen
return inc_pos(pos)
def dec_data_tricky(pos: int, digit: int) -> str:
"""
Dekrement mit Verschleppung.Ein bisschen knifflig
Voraussetzungen
*(ptr + pos + 1) == 0
*(ptr + pos + 2) == 0
"""
if 1 < digit:
return block_of(
if_z_tricky(pos, 1, 1,
dec_data_tricky(pos - 1, digit - 1)),
dec_pos(pos)
)
else:
return dec_pos(pos)
def add_data_tricky(source: int, pos: int, work: int, digit: int) -> str:
"""
1 Byte Addition. pos += source.Es gibt einen Carry.Zerstörungsfreie Version.Ein bisschen knifflig
Voraussetzungen
*(ptr + pos + 1) == 0
*(ptr + pos + 2) == 0
"""
return for_safe(source, work, inc_data_tricky(pos, digit))
def multi_data_tricky(
source1: int,
source2: int,
pos: int,
digit: int) -> str:
"""
1-Byte-Multiplikation. pos = source1 * source2.Es gibt einen Carry.Zerstörungsfreie Version.Ein bisschen knifflig
Voraussetzungen
*(ptr + pos + 1) == 0
*(ptr + pos + 2) == 0
*(ptr + pos + 3) == 0
*(ptr + pos + 4) == 0
"""
return for_safe(
source1,
pos + 3,
add_data_tricky(source2, pos, pos + 4, digit)
)
Möglicherweise haben Sie bemerkt, dass im ersten Makro Doctest aufgeführt war, jedoch nicht in der Mitte.
Bei einfachen Makros handelt es sich um einen einfachen Code, sodass ich das Ergebnis leicht mit doctest überprüfen kann. Bei Makros mit Bewegung muss jedoch überprüft werden, ob der Ausgabecode funktioniert und nicht wie er ist. es gibt.
Aus diesem Grund habe ich in Python einen Brainf * ck-Simulator (wie einen Interpreter) erstellt und dessen Funktion mit unittest überprüft (da ich mit zunehmender Kompliziertheit nicht weiß, was falsch ist, wenn das Makro nicht richtig funktioniert).
Unit Test wichtig.
Ich habe verschiedene Programme mit dem obigen Makro erstellt.
Es war mühsam darüber nachzudenken, wo ich die Arbeit ablegen sollte, was mir das Gefühl gab, sie tatsächlich zusammengestellt zu haben. Wenn es zu weit entfernt ist, ist der Quellcode nutzlos groß.
Aufgrund verschiedener Überlegungen ist die Idee, dass es besser ist, eine Stapelmaschine (Stapelverarbeitungssprache) zu verwenden. Nun, es ist ein sprachähnliches Makro zur Stapelverarbeitung.
Auch im Fall der Mandelbrot-Menge ist Addition / Subtraktion / Multiplikation von Brüchen erforderlich, aber zunächst haben wir in der zweiten Komplementdarstellung Festkommafraktionen verwendet. Die Berechnungsgeschwindigkeit war jedoch sehr langsam (obwohl die Wahrscheinlichkeit eines logischen Problems hoch ist), daher habe ich diesmal versucht, einen Festkomma-Bruch von Vorzeichen + Absolutwert zu verwenden.
Festkommafraktionen sind 1 Byte für den ganzzahligen Teil und 1 Byte für den Bruchteil (dh sie haben eine Genauigkeit von 1/256).
Da ich den Festkomma-Bruch in Vorzeichen + ganzzahliger Teil (1 Byte) + Bruchteil (1 Byte) geändert habe, habe ich versucht, ein Element des Stapels 4 Byte zu machen.
1-Byte-Ganzzahlen werden am Anfang von 4-Byte gespeichert.
Bei Festkommafraktionen ist das erste 1-Byte leer, das 2. Byte ist der ganzzahlige Teil, das 3. Byte ist der Bruchteil und das 4. Byte ist das Vorzeichen (positiv für 0, negativ für 1).
… Warum habe ich das Schild hinter mich gebracht?
Es wird auch angenommen, dass die obigen Makros in der Datei "bf_core.py" registriert sind.
import bf_core as c
#Es gibt zwei Arten von Zahlen:
#・ 1-Byte-Ganzzahl(Ohne Vorzeichen) {VALUE, 0, 0, 0}
#・ 3 Byte fester Dezimalpunkt. {0,Ganzzahliger Teil,Dezimalteil,Code 0/1}Vorzeichen + absoluter Wert
#Ein Element des Stapels ist auf 4 Bytes festgelegt
ELEMENT_SIZE = 4
#Die Oberseite des Stapels ist ein Element vor
TOP = ELEMENT_SIZE * (-1)
#Der zweite Teil des Stapels besteht aus zwei Elementen
SECOND = ELEMENT_SIZE * (-2)
#Die aktuelle Stapelposition ist leer
NOW = 0
#Bewegen Sie ein Element zurück, wenn Sie es auf den Stapel laden
NEXT = ELEMENT_SIZE * (1)
#Platzierung von 1-Byte-Ganzzahlen{Integer Wert, 0, 0, 0 }
IDX_BYTE = 0
IDX_DMY1 = 1
IDX_DMY2 = 2
IDX_DMY3 = 3
#Platzierung von Fixpunktfraktionen{ 0,Ganzzahliger Teil,Dezimalteil,Code(0=+/1=-) }
IDX_DMY = 0
IDX_INT = 1
IDX_DEC = 2
IDX_SGN = 3
def push_byte(value: int) -> str:
"Setzen Sie eine 1-Byte-Ganzzahl oben auf den Stapel"
value = int(value) & 0xff
return c.block_of(
c.init_value(NOW + IDX_BYTE, value & 0xff),
c.move_ptr(NEXT)
)
def push_decimal(value: float) -> str:
"Setzen Sie einen festen 3-Byte-Dezimalpunkt oben auf den Stapel"
(sign, value) = (0, value) if 0 <= value else (1, -value)
value = int(value * 256) & 0xffff
return c.block_of(
c.init_value(NOW + IDX_INT, (value >> 8) & 0xff),
c.init_value(NOW + IDX_DEC, value & 0xff),
c.init_value(NOW + IDX_SGN, sign),
c.move_ptr(NEXT)
)
def drop() -> str:
"Entsorgen Sie die Oberseite des Stapels"
return c.block_of(
c.clear_pos(TOP + 3),
c.clear_pos(TOP + 2),
c.clear_pos(TOP + 1),
c.clear_pos(TOP + 0),
c.move_ptr(TOP)
)
def dup(num: int) -> str:
"Kopieren Sie die Elemente des Stapels und stapeln Sie sie oben auf dem Stapel. num=0 kopiert den Anfang"
pos = -ELEMENT_SIZE * (num + 1)
return c.block_of(
c.copy_data(pos + 0, NOW + 0, NOW + 1),
c.copy_data(pos + 1, NOW + 1, NOW + 2),
c.copy_data(pos + 2, NOW + 2, NOW + 3),
c.copy_data(pos + 3, NOW + 3, NEXT),
c.move_ptr(NEXT)
)
def swap(num: int) -> str:
"Tauschen Sie das erste Element des Stapels gegen das entsprechende Element des Stapels aus. 1<=Sei num"
pos = -ELEMENT_SIZE * (num + 1)
work = NOW
return c.block_of(
c.swap_data(pos + 0, TOP + 0, work),
c.swap_data(pos + 1, TOP + 1, work),
c.swap_data(pos + 2, TOP + 2, work),
c.swap_data(pos + 3, TOP + 3, work)
)
def override(num: int) -> str:
"Überschreiben Sie das entsprechende Element des Stapels mit dem ersten Element des Stapels. 1<=Sei num."
pos = -ELEMENT_SIZE * (num + 1)
return c.block_of(
c.override_data(TOP + 3, pos + 3),
c.override_data(TOP + 2, pos + 2),
c.override_data(TOP + 1, pos + 1),
c.override_data(TOP + 0, pos + 0),
c.move_ptr(TOP)
)
drop
, um die Oberseite des Stapels zu entfernenEs ist wie es ist.
Wenn eine 1-Byte-Ganzzahl oben im Stapel geladen ist, wird die Anzahl der Schleifen wiederholt.
Wenn die Schleife endet, wird die erste 1-Byte-Ganzzahl auf dem Stapel verworfen.
loop_last
wird verwendet, um die Schleifenverarbeitung zu unterbrechen, aber im Gegensatz zur allgemeinen Bremse
wird die Verarbeitung selbst bis zum Ende der Schleife ausgeführt]
(stellen Sie sich vor, Sie setzen nur das End-Flag). Kato. Eigentlich habe ich gerade die Steuervariable auf 0 gesetzt.
def loop_of(*statements: str) -> str:
"Schleife für 1 Byte Ganzzahl von TOP"
return c.block_of(
c.for_loop(TOP, *statements),
c.move_ptr(TOP)
)
def loop_last(num: int) -> str:
"Machen Sie sich bereit, um die Schleife zu beenden.num ist die Position der Steuervariablen in der Schleife.Die Verarbeitung wird fortgesetzt"
pos = -ELEMENT_SIZE * (num + 1)
return c.clear_pos(pos + IDX_BYTE)
Addition und Subtraktion von 1 Byte.
Wenn sich zwei 1-Byte-Ganzzahlen auf dem Stapel befinden, addieren oder subtrahieren Sie die beiden. Nach der Berechnung werden die beiden ursprünglichen Elemente gelöscht und durch das Berechnungsergebnis ersetzt (dh ein Element des Stapels wird reduziert).
def add_byte() -> str:
"1 Byte Addition"
return c.block_of(
c.for_loop(TOP + IDX_BYTE, c.inc_pos(SECOND + IDX_BYTE)),
c.move_ptr(TOP)
)
def sub_byte() -> str:
"1-Byte-Subtraktion"
return c.block_of(
c.for_loop(TOP + IDX_BYTE, c.dec_pos(SECOND + IDX_BYTE)),
c.move_ptr(TOP)
)
Eine 1-Byte-if-Anweisung.
Nach dem Ende der if-Anweisung wird die 1-Byte-Ganzzahl am Anfang des Stapels verworfen.
Da die Arbeit den anderen Teil des 1-Byte-Elements verwendet (verbleibende 3 Bytes), Die Tiefe des Stapels ändert sich nicht.
def if_nz(then_statement: str, else_statement: str = "") -> str:
"Wenn das erste Byte des Stapels NZ ist.Werfen Sie die Oberseite des Stapels nach dem Ende weg"
else_statement = c.delete_useless(else_statement)
if else_statement != "":
else_flag = TOP + IDX_DMY1
return c.block_of(
c.set_value(else_flag, 1),
c.if_nz_then(
TOP + IDX_BYTE,
c.dec_pos(else_flag) + then_statement),
c.if_one_then(
else_flag,
else_statement),
c.move_ptr(TOP)
)
else:
return c.block_of(
c.if_nz_then(
TOP + IDX_BYTE,
then_statement),
c.move_ptr(TOP)
)
def if_z(then_statement: str, else_statement: str = "") -> str:
"Wenn das erste Byte des Stapels Z ist.Werfen Sie die Oberseite des Stapels nach dem Ende weg"
return if_nz(else_statement, then_statement)
def if_eq(value: int, then_statement: str, else_statement: str = "") -> str:
"Wenn das erste Byte des Stapels gleich dem Wert ist.Werfen Sie die Oberseite des Stapels nach dem Ende weg"
return c.block_of(
push_byte(value),
sub_byte(),
if_z(then_statement, else_statement)
)
Festkomma-Bruchberechnungen (Addition, Subtraktion, Multiplikation) und if-Anweisungen.
Aufgrund der Darstellung von Vorzeichen + Absolutwert ist das Addieren und Subtrahieren unerwartet problematisch.
Ich denke, dass es vielleicht besser ist, die Karatsuba-Methode für die Multiplikation zu verwenden, aber ich mache mir Sorgen, dass sie sich nicht ändert, da Addition und Subtraktion langsamer als erwartet zu sein scheinen, aber jetzt berechne ich mit der Pinselberechnungsmethode.
def if_nz_decimal(then_statement: str, else_statement: str = "") -> str:
"Wenn der feste 3-Byte-Dezimalpunkt NZ ist.Werfen Sie die Oberseite des Stapels nach dem Ende weg"
nz_flag = TOP + IDX_DMY
else_flag = TOP + IDX_INT
then_flag = TOP + IDX_DEC
return c.block_of(
c.clear_pos(TOP + IDX_SGN),
c.if_nz_then(TOP + IDX_DEC, c.inc_pos(nz_flag)),
c.if_nz_then(TOP + IDX_INT, c.inc_pos(nz_flag)),
c.inc_pos(else_flag),
c.if_nz_then(nz_flag, c.dec_pos(else_flag) + c.inc_pos(then_flag)),
c.if_one_then(then_flag, then_statement),
c.if_one_then(else_flag, else_statement),
c.move_ptr(TOP)
)
def if_z_decimal(then_statement: str, else_statement: str = "") -> str:
"Wenn der feste 3-Byte-Dezimalpunkt NZ ist.Werfen Sie die Oberseite des Stapels nach dem Ende weg"
return if_nz_decimal(else_statement, then_statement)
def if_negative_decimal(then_statement: str, else_statement: str = "") -> str:
"Wenn der feste 3-Byte-Dezimalpunkt eine negative Zahl ist.Werfen Sie die Oberseite des Stapels nach dem Ende weg"
then_flag = TOP + IDX_DEC
else_flag = TOP + IDX_INT
return c.block_of(
c.clear_pos(then_flag),
c.set_value(else_flag, 1),
c.if_nz_then(
TOP + IDX_SGN,
c.dec_pos(else_flag) + c.inc_pos(then_flag)
),
c.if_one_then(then_flag, then_statement),
c.if_one_then(else_flag, else_statement),
c.move_ptr(TOP)
)
def _add_abs() -> str:
"Addition von Absolutwerten des festen 3-Byte-Dezimalpunkts"
# SECOND/Angenommen, die Symbole von TOP sind gleich
return c.block_of(
c.clear_pos(TOP + IDX_SGN),
c.for_loop(SECOND + IDX_DEC, c.inc_data_tricky(TOP + IDX_DEC, 2)),
c.for_loop(SECOND + IDX_INT, c.inc_pos(TOP + IDX_INT)),
c.override_data(TOP + IDX_DEC, SECOND + IDX_DEC),
c.override_data(TOP + IDX_INT, SECOND + IDX_INT)
)
def _dec_both_abs_int() -> str:
"Dekrementieren Sie beide Ganzzahlen, bis eine 0 wird"
count = NOW
work1 = NOW + 1
return c.block_of(
c.copy_data(SECOND + IDX_INT, count, work1),
c.for_loop(
count,
c.if_z_tricky(
TOP + IDX_INT,
ELEMENT_SIZE, # work2 = NOW + IDX_INT
ELEMENT_SIZE, # work3 = NEXT + IDX_INT
then_statement=loop_last(count),
else_statement=c.block_of(
c.dec_pos(SECOND + IDX_INT),
c.dec_pos(TOP + IDX_INT)
)
)
)
)
def _dec_both_abs_decimal() -> str:
"Verringern Sie beide Brüche, bis einer zu 0 wird"
count = NOW
work1 = NOW + 1
return c.block_of(
c.copy_data(SECOND + 2, count, work1),
c.for_loop(
count,
c.if_z_tricky(
TOP + IDX_DEC,
ELEMENT_SIZE, # work2 = NOW + IDX_DEC
ELEMENT_SIZE, # work3 = NEXT + IDX_DEC
then_statement=loop_last(count),
else_statement=c.block_of(
c.dec_pos(SECOND + IDX_DEC),
c.dec_pos(TOP + IDX_DEC)
)
)
)
)
def _if_nz_int_swap() -> str:
"Wenn der ganzzahlige Teil von SECOND nicht 0 ist, TOP/ZWEITEN umdrehen"
work = NOW
return c.if_nz_tricky(
SECOND + IDX_INT,
ELEMENT_SIZE * 2, # work2 = NOW + IDX_INT
ELEMENT_SIZE * 2, # work3 = NEXT + NEXT + IDX_INT
then_statement=c.block_of(
c.swap_data(SECOND + IDX_INT, TOP + IDX_INT, work),
c.swap_data(SECOND + IDX_DEC, TOP + IDX_DEC, work),
c.swap_data(SECOND + IDX_SGN, TOP + IDX_SGN, work)
)
)
def _if_top_decimal_is_nz_then_override() -> str:
"Wenn der Bruchteil von TOP nicht 0 ist, verschieben Sie TOP auf SECOND"
return c.if_z_tricky(
TOP + IDX_DEC,
ELEMENT_SIZE, # work1 = NOW + IDX_DEC
ELEMENT_SIZE, # work2 = NEXT + IDX_DEC
then_statement=c.clear_pos(TOP + IDX_SGN),
else_statement=c.block_of(
c.override_data(TOP + IDX_SGN, SECOND + IDX_SGN),
c.move_data(TOP + IDX_DEC, SECOND + IDX_DEC)
)
)
def _top_minus_second() -> str:
"Subtrahieren Sie von TOP um den Bruchteil von SECOND und bewegen Sie sich zur Position von SECOND"
return c.block_of(
#Zeichen verschieben(Bewegen Sie sich zuerst mit kniffligen Maßnahmen)
c.override_data(TOP + IDX_SGN, SECOND + IDX_SGN),
#Dekrementiere nur einen kleinen Teil von SECOND
c.for_loop(
SECOND + IDX_DEC,
c.dec_data_tricky(TOP + IDX_DEC, 2)
),
#Verschieben Sie die Ergebnisse auf ZWEITEN
c.move_data(TOP + IDX_DEC, SECOND + IDX_DEC),
c.move_data(TOP + IDX_INT, SECOND + IDX_INT)
# TODO -0.0 bis 0.Sollte es in 0 konvertiert werden?
)
def _sub_abs() -> str:
"Subtraktion des Absolutwerts des festen 3-Byte-Dezimalpunkts"
#Dez, bis entweder 0 wird.Die Antwort bleibt(Einschließlich des Codes)
return c.block_of(
#Dekrementieren Sie beide Ganzzahlen, bis eine 0 wird
_dec_both_abs_int(),
#Wenn der ganzzahlige Teil von SECOND nicht 0 ist, TOP/ZWEITEN umdrehen
_if_nz_int_swap(),
c.if_nz_tricky(
TOP + IDX_INT,
ELEMENT_SIZE, # work1 = NEXT + IDX_INT
ELEMENT_SIZE, # work2 = NEXT + NEXT + IDX_INT
then_statement=_top_minus_second(),
else_statement=c.block_of(
# TOP/Wenn beide SECONDs einen ganzzahligen Teil von 0 haben
_dec_both_abs_decimal(),
_if_top_decimal_is_nz_then_override()
)
)
)
def add_decimal() -> str:
"Zugabe von Festkommafraktionen"
#Wenn die Vorzeichen gleich sind, addieren Sie die absoluten Werte
#Absolute Subtraktion, wenn die Vorzeichen unterschiedlich sind
work = SECOND + IDX_DMY
diff_work = TOP + IDX_DMY
same_flag = SECOND + IDX_DMY
return c.block_of(
c.for_safe(SECOND + IDX_SGN, work, c.inc_pos(diff_work)),
c.for_safe(TOP + IDX_SGN, work, c.dec_pos(diff_work)),
c.inc_pos(same_flag),
c.if_nz_then(diff_work, c.dec_pos(same_flag) + _sub_abs()),
c.if_nz_then(same_flag, _add_abs()),
drop()
)
def sub_decimal() -> str:
"Festpunktbruch-Subtraktion"
#Das Zeichen umkehren und hinzufügen(A-B => A+(-B))
plus_flag = NOW + IDX_DMY
return c.block_of(
c.inc_pos(plus_flag),
c.if_one_then(TOP + IDX_SGN, c.dec_pos(plus_flag)),
c.if_one_then(plus_flag, c.inc_pos(TOP + IDX_SGN)),
add_decimal()
)
def _multi_decimal_abs() -> str:
"Multiplikation der Absolutwerte des festen 3-Byte-Dezimalpunkts"
#Der ganzzahlige Teil und der Bruchteil werden in Byteeinheiten berechnet, ähnlich wie bei der Pinselberechnung.
# A1. A2
# x B1. B2
# ---------------------
# .A1xB2 A2xB2
# + A1xB1.A2xB1
# ----------------------
# R1 . R2 R3
# <--------->Erforderliche Reichweite
idx_a1 = SECOND + IDX_INT
idx_a2 = SECOND + IDX_DEC
idx_b1 = TOP + IDX_INT
idx_b2 = TOP + IDX_DEC
idx_r1 = NOW + IDX_INT
idx_r2 = NOW + IDX_DEC
idx_r3 = NOW + IDX_DEC + 1
#Berechnet aus der oberen Ziffer aufgrund der Übertragsverarbeitung
return c.block_of(
c.multi_data_tricky(idx_a1, idx_b1, idx_r1, 1), # AxC (1 Byte)
c.multi_data_tricky(idx_a1, idx_b2, idx_r2, 2), # AxD (2 Bytes inklusive Carry)
c.multi_data_tricky(idx_a2, idx_b1, idx_r2, 2), # BxC (2 Bytes inklusive Carry)
c.multi_data_tricky(idx_a2, idx_b2, idx_r3, 3), # BxD (3 Bytes inklusive Carry)
c.clear_pos(idx_r3), #Löschen Sie R3, da nur die Übertragung erforderlich ist
)
def _xor_sign() -> str:
#+ Wenn die Vorzeichen gleich sind, minus, wenn sie unterschiedlich sind
idx_as = SECOND + IDX_SGN
idx_bs = TOP + IDX_SGN
idx_rs = NOW + IDX_SGN
sign_work = NEXT
return c.block_of(
c.for_loop(idx_as, c.inc_pos(sign_work)),
c.for_loop(idx_bs, c.dec_pos(sign_work)),
c.if_nz_then(sign_work, c.inc_pos(idx_rs))
)
def multi_decimal() -> str:
"3-Byte-Festkommamultiplikation"
# A * B => R
return c.block_of(
_multi_decimal_abs(), #Finden Sie den absoluten Wert von R.
_xor_sign(), #Finde das Zeichen von R.
c.move_ptr(NEXT), #Der Status des Stapels{A, B, R}
override(2), #Überschreiben Sie R auf Position A.
drop() #Löschen B.
)
def if_lt_decimal(then_statement: str, else_statement: str = "") -> str:
return c.block_of(
swap(1), # {A, B} -> {B, A}In der Reihenfolge der. then/Um die Anzahl der Stapel bei der Ausführung von else nicht zu ändern
dup(1), # {B, A, B}
sub_decimal(), # {B, R (= A - B)}
#A wenn R negativ ist< B
if_negative_decimal(then_statement, else_statement),
drop() # drop B
)
def if_ge_decimal(then_statement: str, else_statement: str = "") -> str:
return if_lt_decimal(else_statement, then_statement)
def if_gt_decimal(then_statement: str, else_statement: str = "") -> str:
return c.block_of(
swap(1),
if_lt_decimal(then_statement, else_statement)
)
def if_le_decimal(then_statement: str, else_statement: str = "") -> str:
return if_gt_decimal(else_statement, then_statement)
Ich schneide Ecken an einer Stelle und es gibt zwei Arten von Nullen, + 0.0
und -0.0
.
Daher beurteilt if_negative_decimal`` -0.0
als negative Zahl.
Da diese Funktion verwendet wird, um die Divergenz des Mandelbrot-Satzes zu beurteilen, kann sie von Zeit zu Zeit versehentlich beurteilt werden. Nun, es liegt im Fehlerbereich, nicht wahr?
Eine Zeichenausgabe und ein (albernes) Zeichenfolgenausgabemakro.
def put_char() -> str:
"1 Byte am Anfang des Stapels(Ein Charakter)Ausgabe"
return c.block_of(
c.exec_pos(TOP, "."),
drop()
)
def put_str(message: str) -> str:
result = c.clear_pos(NOW)
for ch in message:
result += c.init_value(NOW, ord(ch))
#TODO ↑ Es ist besser zu prüfen, ob die Differenz zum vorherigen Wert kürzer ist
result += c.exec_pos(NOW, ".")
result += c.clear_pos(NOW)
return c.delete_useless(result)
Es ist endlich das Hauptthema.
Weitere Informationen zum Mandelbrot-Set finden Sie weiter unten.
Wäre es so, wenn es in C-Sprache geschrieben wäre?
#include <stdio.h>
#define X_BEGIN -2.0
#define X_END 1.0
#define Y_BEGIN -0.9375
#define Y_END 0.9375
//Anzahl der Wiederholungen
#define C_MAX 26
//Divergenzschwelle(2.Quadratischer Wert von 0)
#define THRESHOLD2 4
void main(void)
{
int columns = 128;
int rows = 40;
//X-Achsen-Additionswert
float x_step = (X_END - X_BEGIN) / (columns - 1);
//Additionswert der Y-Achse
float y_step = (Y_END - Y_BEGIN) / (rows - 1);
float y0 = Y_BEGIN;
for (int y = 0; y < rows; y++)
{
float x0 = X_BEGIN;
for (int x = 0; x < columns; x++)
{
float cx = x0;
float cy = y0;
float zx = 0;
float zy = 0;
char ch = ' ';
char count = 'A' - 1;
for (int c = 0; c < C_MAX; c++)
{
float zx2 = zx * zx;
float zy2 = zy * zy;
float size2 = zx2 + zy2;
if (size2 > 4.0)
{
//Abweichungen
ch = count;
break;
}
else
{
float zx_next = zx2 - zy2 + cx;
float zy_next = zx * zy * 2 + cy;
if (zx_next == zx && zy_next == zy)
{
//Konvergenz
break;
}
else
{
zx = zx_next;
zy = zy_next;
count++;
}
}
}
putchar(ch);
x0 += x_step;
}
putchar('\n');
y0 += y_step;
}
}
Ich denke, es gibt viele andere Punkte, wie zum Beispiel, ob der Bruch "float" sein kann oder der Vergleich zwischen "float" mit "==", aber ich werde es vorerst belassen.
Um das Schreiben mit Brainf * ck so einfach wie möglich zu machen, wird die Beurteilung, ob der Absolutwert der komplexen Zahl "2,0" überschreitet, durch die Beurteilung ersetzt, ob der Quadratwert "4,0" direkt überschreitet, ohne "sqrt" zu verwenden. Ich bin.
Schreiben wir das mit Brainf * ck.
Übrigens wird angenommen, dass Stapelmakros in bf_stack.py
gespeichert sind.
import bf_core as c
import bf_stack as s
#X-Achsenbereich
(X_BEGIN, X_END) = (-2.0, 1.0)
#Y-Achsenbereich
(Y_BEGIN, Y_END) = (-0.9375, 0.9375)
#Anzahl der Wiederholungen
C_MAX = 26
#Divergenzschwelle(2.Quadratischer Wert von 0)
THRESHOLD2 = 4
def mandelbrot(columns: int, rows: int) -> str:
#X-Achsen-Additionswert
x_step = (X_END - X_BEGIN) / (columns - 1)
#Additionswert der Y-Achse
y_step = (Y_END - Y_BEGIN) / (rows - 1)
return c.program_of(
# #0: y0 = y_begin
s.push_decimal(Y_BEGIN),
# #1: y = rows
s.push_byte(rows),
s.loop_of( # for(y)
# #2: x0 = x_begin
s.push_decimal(X_BEGIN),
# #3: x = columns
s.push_byte(columns),
s.loop_of( # for(x)
s.dup(1), # #4: cx = x0
s.dup(4), # #5: cy = y0
s.push_decimal(0), # #6: zx = 0
s.push_decimal(0), # #7: zy = 0
s.push_byte(ord(" ")), # #8: ch = ' '
s.push_byte(ord("A") - 1), # #9: count = 'A'-1
s.push_byte(C_MAX), # #10: c = 26
s.loop_of( # for(c)
# #11: zx2 = zx * zx
s.dup(4),
s.dup(0),
s.multi_decimal(),
# #12: zy2 = zy * zy
s.dup(4),
s.dup(0),
s.multi_decimal(),
# #13: size2 = zx2 + zy2
s.dup(1),
s.dup(1),
s.add_decimal(),
# #14: THRESHOLD2
s.push_decimal(THRESHOLD2),
# if size2 > 4.0
s.if_gt_decimal(
then_statement=c.block_of(
#Abweichungen
# ch = count
s.dup(5), # #15
s.override(7),
# for_c break
s.loop_last(4)
),
else_statement=c.block_of(
# #15: zx_next = zx2 - zy2 + cx
s.dup(3),
s.dup(3),
s.sub_decimal(),
s.dup(11),
s.add_decimal(),
# #16: zy_next = zx * zy * 2 + cy
s.dup(9),
s.dup(9),
s.multi_decimal(),
s.dup(0),
s.add_decimal(),
s.dup(11),
s.add_decimal(),
# #17: if zx_next == zx && zy_next == zy
s.dup(1), # zx_next
s.dup(11), # zx
s.sub_decimal(),
# #17: 0(zx_next == zx) or 1(zx_next != zx)
s.if_nz_decimal(
s.push_byte(1) + s.swap(1),
s.push_byte(0) + s.swap(1)),
s.dup(1), # zy_next
s.dup(11), # zy
s.sub_decimal(),
# #18: 0(zx_next == zx) or 1(zx_next != zx)
s.if_nz_decimal(
s.push_byte(1) + s.swap(1),
s.push_byte(0) + s.swap(1)),
# #17: 0(zx_next == zx && zy_next == zy) or other
s.add_byte(),
s.if_z(
then_statement=c.block_of(
#Konvergenz
s.swap(1),
s.drop(), # drop zy_next
s.swap(1),
s.drop(), # drop zx_next
s.loop_last(5) # last c
),
else_statement=c.block_of(
# zx = zx_next
s.swap(1),
s.override(10),
# zy = zy_next
s.swap(1),
s.override(10),
# count += 1
s.dup(6),
s.push_byte(1),
s.add_byte(),
s.override(7)
)
)
)
),
s.drop() * 2 # drop zy2, zx2
),
s.drop(), # drop count
s.put_char(), # putchar(ch)
s.drop() * 4, # drop zy, zx, cy, cx
# #4: x0 += x_step
s.dup(1),
s.push_decimal(x_step),
s.add_decimal(),
s.override(2)
),
# drop x0
s.drop(),
# #2: putchar("\n")
s.push_byte(ord("\n")),
s.put_char(),
# #2: y0 += y_step
s.dup(1),
s.push_decimal(y_step),
s.add_decimal(),
s.override(2)
)
)
if __name__ == '__main__':
program = mandelbrot(128, 40)
print(program)
Wenn Sie das Stapelverarbeitungsmakro tatsächlich verwenden, ist dies schwierig, da Sie die Elemente des Stapels an relativen Positionen schreiben müssen.
Speichern Sie dies auf jeden Fall als "mandelbrot.py" und führen Sie es aus.
python3 -B mandelbrot.py > mandelbrot.bf
./bf2c -F mandelbrot.bf
gcc -O2 -o mandelbrot mandelbrot.c
./mandelbrot
Ich denke, die Kompilierung wird bald abgeschlossen sein, aber es wird ungefähr 5-7 Sekunden dauern, bis sie ausgeführt wird. langsam.
Ich denke übrigens, dass Sie normal mit einem allgemeinen Brainf * ck-Compiler kompilieren können, aber bitte verwenden Sie einen Compiler, der so weit wie möglich optimiert ist.
Ich denke auch, dass der Brainf * ck-Dolmetscher viel Zeit in Anspruch nehmen wird. Zumindest denke ich, dass es besser ist, einen Interpreter zu verwenden, der für die Optimierung funktioniert.
Es gibt Implementierung von Mandelbrot im berühmten Brainf * ck, aber es dauert nur 1 bis 2 Sekunden, daher ist es immer noch verbessert. Es scheint Platz zu geben.
Um genau zu sein, ist die Mandelbrot-Menge eine Menge komplexer Zahlen, die nicht voneinander abweichen. Der schwarze Teil des Ergebnisses ist also die Mandelbrot-Menge.
Nach der allgemeinen App zum Zeichnen von Mandelbrot-Sets werde ich sie jedoch entsprechend der Häufigkeit der Abweichungen einfärben.
Die Quelle ist einfach und wird daher weggelassen (in Github als Beispiel für den Compiler registriert). Wenn Sie es auf ein Terminal verschieben, das die ANSI-Escape-Sequenz verwenden kann, wird es farbig angezeigt.
Danach können Sie Julia Set etc. mit nur einer kleinen Modifikation zeichnen.
Recommended Posts