[PYTHON] Zeichnen Sie ein Mandelbrot-Set mit Brainf * ck

Zeichnen Sie ein Mandelbrot-Set mit Brainf * ck

Einführung

mandelbrot(color)

Brainf * ck Sprachspezifikation

Brainf * ck hat 8 Arten von Anweisungen

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.

Probleme mit der Portabilität von Brainf * ck

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.

Implementierung des Brainf * ck-Compilers

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.

Selbst 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

Wenn 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.

Einführung in die Programmentwicklung mit Brainf * ck

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.

Wert einstellen

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.

Schleifenverarbeitung

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.

Daten verschieben, hinzufügen, kopieren

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 Verzweigung

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.

Brainf * ck-Programmierung mit Makros

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.

Grundlegendes Makro

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)
    )

Werteinstellungsmakro

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.

Schleifenmakro

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.

Kopieren / mobiles Makro

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)
    )

Bedingtes Verzweigungsmakro

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.

Ein kleines kniffliges Makro

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)
    )

Brainf * ck Simulator

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.

Brainf * ck Stapelmaschinenplanung

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).

Grundlegendes Stapeloperationsmakro

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)
    )

Es ist wie es ist.

Stapelversionsschleifenmakro

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)

Makro für das Additions- / Subtraktionssystem der Byteversion

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)
    )

Bedingtes Verzweigungsmakro für Byteversion

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-Bruchmakro

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?

Zeichenausgabemakro

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)

Zeichnen Sie Mandelbrot Set

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.

mandelbrot

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.

Färben Sie das Mandelbrot-Set

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.

mandelbrot(color)

Anwendung

Danach können Sie Julia Set etc. mit nur einer kleinen Modifikation zeichnen.

julia(color)

Recommended Posts

Zeichnen Sie ein Mandelbrot-Set mit Brainf * ck
Zeichnen Sie mit NetworkX ein Diagramm
Zeichnen Sie mit networkx ein Diagramm
Zeichnen Sie mit matplotlib ein loses Diagramm
Zeichne einen schönen Kreis mit Numpy
Zeichne ein Diagramm mit Julia + PyQtGraph (3)
Zeichnen Sie ein Diagramm mit Pandas + XlsxWriter
Zeichnen Sie ein Diagramm mit der PySimple-Benutzeroberfläche
Zeichnen Sie einfach eine Karte mit matplotlib.basemap
Richten Sie mit Docker einen Samba-Server ein
Zeichne mit PyCall ein Herz in Ruby
Zeichnen Sie ein Diagramm mit PyQtGraph Part 1-Drawing
Zeichnen Sie eine flache Oberfläche mit einem Matplotlib-3D-Diagramm
Richten Sie einen einfachen HTTPS-Server mit Asyncio ein
Zeichnen Sie in Jupyter ein Diagramm mit japanischen Beschriftungen
So zeichnen Sie ein 2-Achsen-Diagramm mit Pyplot
Richten Sie einen lokalen Server mit Go-File-Upload ein.
Zeichnen Sie ein Diagramm mit den PyQtGraph Part 3-PlotWidget-Einstellungen
Zeichnen Sie ein Diagramm, indem Sie es mit Pandas groupby verarbeiten
[Python] Zeichnen Sie mit Dash Cytoscape ein gerichtetes Diagramm
Versuchen Sie, mit Python eine Lebenskurve zu zeichnen
[Python] Zeichne eine Mickey Mouse mit Turtle [Anfänger]
Richten Sie einen lokalen Server mit Go-File-Download ein.
Zeichnen Sie ein Diagramm mit den Einstellungen von PyQtGraph Part 4-PlotItem
Zeichnen Sie ein Diagramm mit PyQtGraph Teil 6 - Anzeigen einer Legende
Zeichnen Sie mit PyQtGraph Teil 5 ein Diagramm. Erhöhen Sie die Y-Achse
[Python] Zeichnen Sie ein Qiita-Tag-Beziehungsdiagramm mit NetworkX
[Python] Wie zeichnet man mit Matplotlib ein Liniendiagramm?
Die Geschichte, eine harte Zeit mit der gemeinsamen Menge HTTP_PROXY = ~ zu haben
Zeichnen Sie gewaltsam so etwas wie ein Flussdiagramm mit Python, matplotlib
Zeichnen Sie ein Diagramm mit PyQtGraph Teil 2 - Detaillierte Ploteinstellungen
[Python] Wie zeichnet man mit Matplotlib ein Streudiagramm?
Richten Sie mit Sublime Text 2 eine Python-Entwicklungsumgebung ein
[Vagrant] Richten Sie einen einfachen API-Server mit Python ein
A4 Größe mit Python-Pptx
Mit Dekorateur dekorieren
Mathematik mit Python studieren: Zeichnen Sie mit matplotlib ein Sympy-Diagramm (Scipy-Diagramm)
Richten Sie eine Python-Entwicklungsumgebung mit Visual Studio Code ein
Richten Sie einen Webserver mit CentOS7 + Anaconda + Django + Apache ein
[Visualisierung] Ich möchte mit Plotly ein schönes Diagramm zeichnen