Brainf * ck a 8 types d'instructions
>
Incrémente le pointeur. Si le pointeur est ptr, il correspond à "ptr ++;
" en langage C.<
Décrémente le pointeur. Équivalent à "ptr-;
" en langage C.+
Incrémente la valeur pointée par le pointeur. Équivalent à "(* ptr) ++;
" en langage C.-
Décrémente la valeur pointée par le pointeur. Équivalent à "(* ptr) -;
" en langage C..
Ecrivez la valeur pointée par le pointeur vers la sortie. Équivalent à "putchar (* ptr);
" en langage C.,
Lire 1 octet de l'entrée et l'affecter à la destination pointée par le pointeur. Équivaut à "* ptr = getchar ();` "en langage C.[
Si la valeur pointée par le pointeur est 0, sautez immédiatement après le ]
correspondant. Équivaut à " while (* ptr) {
" en langage C.]
Si la valeur pointée par le pointeur est différente de zéro, sautez immédiatement après le [
correspondant. Équivalent à "}
" en langage C.L'explication est empruntée à la section Brainfuck (ja) de wikipedia.
Brainf * ck a un tableau d'au moins 30000 cellules et un flux d'octets d'entrée / sortie, qui peuvent être manipulés pour réaliser le traitement.
Selon la version anglaise de wikipedia Brainfuck (en), il y a quatre problèmes avec la portabilité de Brainf * ck:
Cell size
La taille de la cellule pointée par le pointeur est généralement de 8 bits, mais il existe également d'autres tailles.
Il semble y avoir des implémentations 16 bits, 32 bits ou autres.
Array size
Dans l'implémentation classique, le tableau se compose de 30000 cellules, avec le premier pointeur sur le bord gauche (position 0).
Cependant, il semble y avoir une implémentation qui a une cellule sur le côté gauche (l'indice du tableau est négatif).
En outre, il semble qu'il existe des implémentations qui provoquent une erreur lors du déplacement au-delà de la plage du tableau, des implémentations qui étendent le tableau, des implémentations qui se déplacent dans la direction opposée du tableau et des implémentations qui ne vérifient pas la plage (mousse).
End-of-line code
Non limité à Brainf * ck, mais le code de saut de ligne est souvent différent.
La plupart traitent LF (\ n
) comme un code de saut de ligne, mais il existe des implémentations (ou OS d'exploitation) qui utilisent CR + LF ( \ r \ n
) ou CR uniquement (\ r
).
End-of-file behavior
Il semble qu'il y ait trois comportements (principalement?) Lors de la lecture de la fin du fichier, en fonction de l'implémentation.
Afin de créer un programme avec certaines fonctions avec Brainf * ck, j'ai pensé qu'il serait préférable d'ajouter des fonctions au compilateur, j'ai donc créé le compilateur bf2c
en langage C.
C'est un compilateur, ou plus exactement un traducteur qui traduit la source de Brainf * ck en C. L'optimisation du code est laissée au compilateur C.
Il a les caractéristiques suivantes
Les options suivantes peuvent être spécifiées pour bf2c
.
-o
ou-output
: Spécifiez le fichier de sortie (source C)-F
ou --force-flush
: Forcer le rinçage pour chaque caractère-O
ou --output-default
: Définit l'argument par défaut de la commande d'exécution comme chemin de sortie-I
ou --input-default
: Définit l'argument par défaut de la commande d'exécution comme chemin d'entrée-M
ou --message-default
: Définit l'argument par défaut de la commande d'exécution en tant que message-s
ou --size
: Spécifie le nombre de tableaux de mémoire (par défaut 30000)-1
ou --cell-chear
: fait du tableau mémoire un tableau de char (par défaut)-2
ou --cell-short
: Rendre le tableau mémoire un tableau court-4
ou --cell-int
: Soit le tableau mémoire un tableau int-z
ou --eof-zero
: Mettre EOF à 0-m
ou --eof-minus
: EOF est -1 (par défaut)-n
ou --eof-no-effect
: ne charge pas EOF-C
ou --copyright
: Spécifiez l'affichage du copyright ou l'affichage de la licence-V
ou --version-string
: Spécifiez les informations de version-v
ou --version
: Affiche la version du compilateur-h
ou --help
: Afficher le message d'aideMême si vous spécifiez «-2» ou «-4», cela dépend du compilateur C si la taille de la cellule est de 16 bits ou 32 bits. Utilisez respectivement «short» et «int »comme spécifications.
Si vous ne spécifiez pas les informations de version avec -V
, les informations par défaut sont incorporées en fonction du nom du fichier source, de la date actuelle, du nom de connexion, etc. Pour le nom de connexion, vérifiez les variables d'environnement dans l'ordre de LOGNAME
ʻUSER`` LNAME ʻUSERNAME
, et utilisez celle avec la première chaîne de caractères non vide (la variable d'environnement à vérifier est python(Reportez-vous à getpass.getuser ()
). Si rien n'est défini, utilisez «" noname "».
Le flash forcé de -F
a été ajouté pour qu'il puisse être affiché caractère par caractère car le programme de l'ensemble de Mandelbrot que j'ai créé au début était assez lent et je n'étais pas sûr s'il fonctionnait avec un flash ligne par ligne. Fait.
La commande d'exécution compilée accepte les options suivantes
-f
ou --file
: fichier d'entrée-m
ou --message
: Message d'entrée-o
ou-output
: fichier de sortie-v
ou --version
: Affiche les informations de version-h
ou --help
: Afficher le message d'aideCependant, si le fichier source Brainf * ck n'utilise pas la commande ,
, vous ne pourrez pas spécifier les options de fichier d'entrée et de message d'entrée.
De même, vous ne pouvez pas spécifier les options du fichier de sortie si la commande .
n'est pas utilisée.
En outre, les informations de version et le message d'aide seront affichés de la même manière.
Si le fichier d'entrée et le message d'entrée ne sont pas spécifiés, l'entrée standard est lue. De même, si aucun fichier de sortie n'est spécifié, il écrit dans la sortie standard.
Si l'argument par défaut de la commande d'exécution a été spécifié au moment de la compilation (-I
ou -M
ou -O
), un seul argument peut être spécifié sans aucune option.
Les instructions de Brainf * ck sont simples, mais vous pouvez en combiner plusieurs pour faire un travail complet. Il y a différentes descriptions de ce niveau dans l'article de Qiita, je vais donc l'expliquer brièvement rapidement.
Vous ne pouvez pas définir la valeur directement dans la cellule, mais vous pouvez définir n'importe quelle valeur en combinant «+» et «-».
Vous pouvez effacer la valeur de la cellule sur laquelle vous pointez actuellement en faisant quelque chose comme «[-]».
Pour réaliser la fonction équivalente à l'instruction «while» d'un langage général, il est possible de la réaliser en préparant une cellule pour le contrôle.
Par exemple, si la cellule à gauche de l'une est la cellule de contrôle, vous pouvez traiter la boucle en utilisant <[> (Traitement dans la boucle) <]>
.
Cependant, à la fin de "Traitement en boucle", le pointeur doit être ramené à la position de départ.
De même, si le compteur est sur le côté gauche, la fonction équivalente à l'instruction for
qui effectue une boucle le nombre de fois spécifié sera <[-> (traitement dans la boucle) <]>
.
Dans ce cas, le compteur a été décrémenté pendant le traitement et est à 0 à la fin de la boucle.
Il doit être copié à l'avance pour éviter de détruire le compteur.
Par exemple, pour ajouter la valeur pointée par le pointeur actuel à la valeur à côté, vous pouvez utiliser [-> + <]
.
1
Appliquer ceci, par exemple, pour le doubler serait [-> ++ <]
.
Pour ajouter à deux endroits (sur le côté droit et sur le côté droit), utilisez [-> +> + <<]
.
Vous pouvez copier la valeur, en supposant que le côté droit est entièrement à 0. Tout d'abord, entrez la même valeur une à côté de un et deux à côté d'elle comme [-> +> + <<]
.
Après cela, si vous passez aux deux suivants et ajoutez la valeur à la position d'origine (version destructive) [>>
, vous pouvez copier la valeur [- << +] <<
.
Le traitement de Brainf * ck est fondamentalement destructeur, donc la copie de données est très fréquente.
Le branchement conditionnel tel que l'instruction ʻif` dans les langages généraux est également réalisé sous la forme d'une boucle ponctuelle.
Par exemple, si vous voulez exécuter (alors) le processus 1 si la valeur actuelle est différente de 0, et (sinon) le processus 2 si elle est 0, préparez une cellule en tant que travail et utilisez-la comme indicateur. Ici, nous utiliserons le suivant comme indicateur pour else et supposerons que la valeur est 0.
Tout d'abord, définissez l'indicateur else sur 1 > + <
.
Et boucle avec la valeur actuelle [
. Si la valeur actuelle est différente de zéro, elle entrera dans la boucle, et si elle est égale à 0, elle n'entrera pas dans la boucle.
À l'intérieur de la boucle, effacez la valeur actuelle à 0, [-]
, et effacez également l'indicateur else > - <
. Après cela, le processus 1 est exécuté et la boucle est quittée ]
.
Puis boucle basée sur l'indicateur else > [
. Effacez le drapeau pour else, -
, exécutez le processus 2 et quittez la boucle] <
.
C'est le même processus que ʻif`. À la fin du processus, la valeur actuelle et l'indicateur sont 0.
En résumé, `> + <[[-]> - <(Process 1)]> [- (Process 2)] <ʻIl ressemble à ceci.
Si vous assemblez le contenu ci-dessus, vous serez en mesure d'effectuer divers processus.
Mais honnêtement, vous ne pouvez pas le faire, non?
Par conséquent, j'ai décidé de préparer une certaine quantité de traitement cohésif sous forme de macro et de l'utiliser pour la programmation.
Pour le moment, considérons une macro qui appelle une fonction (ou une méthode) et renvoie l'instruction Brainf * ck correspondante.
J'ai écrit la macro en Python pour le moment.
Par exemple, une macro qui déplace un pointeur ressemble à ceci.
def move_ptr(pos: int) -> str:
"""
Déplacer le pointeur
>>> move_ptr(0)
''
>>> move_ptr(2)
'>>'
>>> move_ptr(-3)
'<<<'
"""
return ">" * pos if 0 <= pos else "<" * (-pos)
Si vous faites cela, la macro (provisoire) qui traite à la position spécifiée et revient à la position d'origine une fois terminé ressemblera à ceci.
def exec_pos(pos: int, statement: str) -> str:
"Exécuter le traitement à la position spécifiée"
return move_ptr(pos) + statement + move_ptr(-pos)
Cependant, la programmation d'une combinaison de macros peut entraîner un traitement inutile.
Par exemple, un processus comme ʻexec_pos (3, "+") + exec_pos (4, "-") ʻoutmet le résultat>>> + <<< >>>> - <<<<
. Je vais finir. Le <<< >>>
sur le chemin est inutile, n'est-ce pas?
Donc, nous allons d'abord créer quelques macros de base.
import re
def delete_useless(statement: str) -> str:
"""
Mouvement inutile/Décalage et suppression des calculs
>>> delete_useless("+++--<<>>>")
'+>'
>>> delete_useless("---++>><<<")
'-<'
>>> delete_useless(">++++[-][-]")
'>[-]'
>>> delete_useless(">--[-]++[-]")
'>[-]'
"""
while True:
if "<>" in statement:
#Compenser les mouvements inutiles, partie 1
statement = statement.replace("<>", "")
continue
if "><" in statement:
#Compenser les mouvements inutiles, partie 2
statement = statement.replace("><", "")
continue
if "+-" in statement:
#Compenser l'addition et la soustraction inutiles, partie 1
statement = statement.replace("+-", "")
continue
if "-+" in statement:
#Compenser l'addition et la soustraction inutiles, partie 2
statement = statement.replace("-+", "")
continue
if "+[-]" in statement or "-[-]" in statement:
#Supprimer l'addition / soustraction avant la suppression de zéro
statement = re.sub(r'[-+]+\[-\]', "[-]", statement)
continue
if "[-][-]" in statement:
#Plusieurs zéro efface à la fois
statement = statement.replace("[-][-]", "[-]")
continue
break
return statement
def delete_useless_all(statement: str) -> str:
"""
Mouvement inutile/Décalage et suppression des calculs. Supprimer le dernier processus inutile
>>> delete_useless_all("[+>-<]>++++[-][-]")
'[+>-<]'
>>> delete_useless_all("[-<+>][-]>>++")
'[-<+>]'
>>> delete_useless_all("[-<+>][-]<<--")
'[-<+>]'
"""
statement = delete_useless(statement)
while statement:
if statement[-1] in "-+><":
#À la fin"+" "-" ">" "<"Est supprimé
statement = re.sub(r'[-+><]+$', "", statement)
continue
if statement.endswith("[-]"):
#À la fin"[-]"Est supprimé
statement = re.sub(r'\[-\]$', "", statement)
continue
break
return statement
def block_of(*statements: str) -> str:
"""
Combinez plusieurs instructions
>>> 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
D'une manière ou d'une autre, il y a une logique folle, mais je m'en fiche car c'est un code jetable de toute façon. Le nom de la fonction n'est pas clair non plus, mais je ne peux pas m'en empêcher car je ne comprends pas l'anglais.
En bref, il existe des macros qui suppriment les mouvements inutiles et les calculs faciles à comprendre, et bloquent les macros qui facilitent l'écriture de macros ensemble.
En utilisant ceux-ci, le précédent ʻexec_pos` peut également être réécrit comme suit.
def exec_pos(pos: int, statement: str) -> str:
"""
Exécuter le traitement à la position spécifiée
>>> exec_pos(3, "+")
'>>>+<<<'
>>> exec_pos(3, "<+>")
'>>+<<'
"""
return block_of(
move_ptr(pos),
statement,
move_ptr(-pos)
)
La macro de définition de valeur ressemble à ce qui suit.
def inc_pos(pos: int) -> str:
"""
Incrémenter la position spécifiée
>>> inc_pos(2)
'>>+<<'
"""
return exec_pos(pos, "+")
def dec_pos(pos: int) -> str:
"""
Décrémenter la position désignée
>>> dec_pos(3)
'>>>-<<<'
"""
return exec_pos(pos, "-")
def clear_pos(pos: int) -> str:
"""
Effacer la position spécifiée
>>> clear_pos(3)
'>>>[-]<<<'
"""
return exec_pos(pos, "[-]")
def set_value(pos: int, value: int) -> str:
"""
Définir la valeur spécifiée
>>> 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)
)
La logique de set_value
semble être un peu folle, mais cela ressemble à ceci en partant du principe que le travail ne peut pas être utilisé.
Par exemple, si vous souhaitez définir la valeur initiale (tous les 0 sur le côté droit peuvent être utilisés pour le travail), vous pouvez la définir avec un peu plus de réflexion.
import math
def _init_value_sub(value: int) -> str:
"Réglage de la valeur initiale.Prémisse qu'il peut être utilisé pour le travail après le prochain"
(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 +Divisé en forme c1
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 -Divisé en forme c1
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:
"""
Réglage de la valeur initiale.
Prémisse qu'il peut être utilisé pour le travail après le prochain.Soyez prudent dans l'ordre d'initialisation
"""
return delete_useless(exec_pos(pos, _init_value_sub(value)))
Cependant, en supposant que vous traduisez en langage C puis appliquez l'optimisation, il se peut que set_value ()
, qui semble fou, soit plus efficace.
C'est un système en boucle.
def while_loop(pos: int, *statements: str) -> str:
"""
boucle while.
>>> while_loop(3, ">>+<<")
'>>>[<+>]<<<'
"""
return block_of(
exec_pos(pos, "["),
block_of(*statements),
exec_pos(pos, "]")
)
def for_loop(pos: int, *statements: str) -> str:
"""
répétition.Version destructrice
>>> 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:
"""
répétition.Version non destructive(Cependant, ne mettez pas à jour la référence à pos pendant la boucle.)
>>> for_safe(3, 4, "+")
'>>>[->+<<<<+>>>]>[-<+>]<<<<'
"""
return block_of(
for_loop(pos, inc_pos(work1), statement),
move_data(work1, pos)
)
La version non destructive de la boucle for
est détruite puis renvoyée à la fin.
Si vous avez deux œuvres, vous pouvez copier la valeur, puis effectuer une boucle à la destination de la copie, mais pour le moment, vous n'avez qu'à regarder la valeur dans la boucle, c'est donc la première étape.
Déplacer ou copier des données.
def move_data(source: int, destination: int) -> str:
"Mouvement d'1 octet/Ou ajouter."
return for_loop(source, inc_pos(destination))
def copy_data(source: int, destination: int, work1: int) -> str:
"Copie 1 octet."
return block_of(
clear_pos(destination),
for_safe(source, work1, inc_pos(destination))
)
def override_data(source: int, destination: int) -> str:
"Mouvement d'1 octet."
return block_of(
clear_pos(destination),
move_data(source, destination)
)
def swap_data(target1: int, target2: int, work1: int) -> str:
"Permutation des valeurs entre 1 octets"
return block_of(
move_data(target1, work1),
move_data(target2, target1),
move_data(work1, target2)
)
Une simple macro d'instruction ʻif`.
Un travail est nécessaire pour joindre la clause else, donc pour le moment, seulement alors.
def if_nz_then(pos: int, then_statement: str) -> str:
"if_Version destructrice de nz.Une version simplifiée d'alors seulement"
return while_loop(
pos,
clear_pos(pos),
then_statement
)
def if_one_then(pos: int, then_statement: str) -> str:
"En supposant que la position de pos est 1 ou 0.Traiter si 1(Version destructrice)."
return while_loop(
pos,
dec_pos(pos),
then_statement
)
ʻIf_one_then est presque le même que ʻif_nz_then
, mais si la valeur est 0 ou 1, la taille de la source sera légèrement plus petite, donc nous l'avons préparée.
Cela n'a peut-être pas beaucoup de sens.
Nous avons également des macros étranges. La version non destructive de l'instruction ʻif` qui m'est venue à l'esprit et le traitement arithmétique qui l'utilise (addition, soustraction, multiplication avec report).
Cependant, comme le travail est requis à une position étrange, le mouvement du pointeur est différent (éventuellement le même) entre la clause then
et la clause ʻelse`, ce qui est un peu une macro déroutante.
C'est peut-être un frein à l'optimisation en langage C.
def if_nz_tricky(
pos: int,
n: int,
m: int,
then_statement: str,
else_statement: str = "") -> str:
"""
if_Version non destructive de nz.Un peu délicat
Conditions préalables
*(ptr + pos + n) == 0
*(ptr + pos + m) == 0
*(ptr + pos + n + m) == 0
※ n ==m est OK
"""
return block_of(
move_ptr(pos),
inc_pos(n), # pos+n =Marquer pour autre
"[", #Pour NZ
dec_pos(n), #Effacer l'indicateur else
exec_pos(-pos, then_statement),
move_ptr(m), #NZ est pos+m /Z reste pos
"c]",
move_ptr(n), #NZ est pos+n+m /Z est pos+n
"[-", #Pour Z
exec_pos(-pos - n, else_statement),
move_ptr(m), #NZ est pos+n+Reste m/Z également pos+n+Déplacer vers 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_Version non destructive de z.Un peu délicat
Conditions préalables
*(ptr + pos + n) == 0
*(ptr + pos + m) == 0
*(ptr + pos + n + m) == 0
※ n ==m est OK
"""
return if_nz_tricky(pos, n, m, else_statement, then_statement)
def inc_data_tricky(pos: int, digit: int) -> str:
"""
Incrément avec report.Un peu délicat
Conditions préalables
*(ptr + pos + 1) == 0
*(ptr + pos + 2) == 0
"""
if 1 < digit:
#Besoin de porter
return block_of(
inc_pos(pos),
if_z_tricky(pos, 1, 1,
inc_data_tricky(pos - 1, digit - 1))
)
else:
#Pas besoin de porter
return inc_pos(pos)
def dec_data_tricky(pos: int, digit: int) -> str:
"""
Décrémenter avec report.Un peu délicat
Conditions préalables
*(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 octet supplémentaire. pos += source.Il y a un report.Version non destructive.Un peu délicat
Conditions préalables
*(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:
"""
Multiplication de 1 octet. pos = source1 * source2.Il y a un report.Version non destructive.Un peu délicat
Conditions préalables
*(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)
)
Vous avez peut-être remarqué que la première macro avait doctest répertorié, mais pas au milieu.
Pour les macros de base, c'est un code simple, donc je pourrais facilement vérifier le résultat avec doctest, mais pour les macros avec mouvement, il est nécessaire de vérifier que le code de sortie fonctionne plutôt que ce qu'il est. il y a.
Par conséquent, j'ai créé un simulateur Brainf * ck (comme un interpréteur) en python et vérifié son fonctionnement avec unittest (car à mesure que cela se complique, je ne sais pas ce qui ne va pas si la macro ne fonctionne pas correctement).
Test unitaire important.
J'ai fait divers programmes avec la macro ci-dessus.
C'était difficile de réfléchir à l'endroit où placer le travail, ce qui m'a donné l'impression de l'avoir assemblé. S'il est trop loin, le code source sera inutilement volumineux.
À la suite de diverses réflexions, l'idée est qu'il serait préférable d'utiliser une machine à pile (langage de traitement de pile). Eh bien, c'est une macro de type langage de traitement de pile.
De plus, dans le cas de l'ensemble de Mandelbrot, une addition / soustraction / multiplication de fractions est nécessaire, mais au départ, nous avons utilisé des fractions à virgule fixe dans la deuxième représentation du complément. Cependant, la vitesse de calcul était très lente (bien qu'il y ait une forte possibilité d'un problème logique), donc cette fois j'ai essayé d'utiliser une fraction de signe à virgule fixe + valeur absolue.
Les fractions à virgule fixe sont de 1 octet pour la partie entière et de 1 octet pour la partie fraction (c'est-à-dire qu'elles ont une précision de 1/256).
Depuis que j'ai changé la fraction à virgule fixe en signe + partie entière (1 octet) + partie fraction (1 octet), j'ai essayé de créer un élément de la pile de 4 octets.
Les entiers de 1 octet sont stockés au début de 4 octets.
Pour les fractions à virgule fixe, le premier octet est vide, le deuxième octet est la partie entière, le troisième octet est la partie fractionnaire et le quatrième octet est le signe (positif pour 0, négatif pour 1).
… Pourquoi ai-je mis le panneau derrière?
De plus, on suppose que les macros ci-dessus sont enregistrées dans le fichier bf_core.py
.
import bf_core as c
#Il existe deux types de nombres:
#・ Entier 1 octet(Non signé) {VALUE, 0, 0, 0}
#・ Point décimal fixe de 3 octets. {0,Partie entière,Partie décimale,Code 0/1}Signe + valeur absolue
#Un élément de la pile est fixé à 4 octets
ELEMENT_SIZE = 4
#Le haut de la pile est un élément avant
TOP = ELEMENT_SIZE * (-1)
#Le deuxième de la pile est deux éléments avant
SECOND = ELEMENT_SIZE * (-2)
#La position actuelle de la pile est vide
NOW = 0
#Reculer d'un élément lorsqu'il est chargé sur la pile
NEXT = ELEMENT_SIZE * (1)
#Placement d'entiers de 1 octet{Valeur entière, 0, 0, 0 }
IDX_BYTE = 0
IDX_DMY1 = 1
IDX_DMY2 = 2
IDX_DMY3 = 3
#Placement des fractions à virgule fixe{ 0,Partie entière,Partie décimale,Code(0=+/1=-) }
IDX_DMY = 0
IDX_INT = 1
IDX_DEC = 2
IDX_SGN = 3
def push_byte(value: int) -> str:
"Mettez un entier de 1 octet en haut de la pile"
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:
"Placez un point décimal fixe de 3 octets en haut de la pile"
(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:
"Jeter le haut de la pile"
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:
"Copiez les éléments de la pile et empilez-les en haut de la pile. num=0 copie le début"
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:
"Échangez le premier élément de la pile avec l'élément correspondant de la pile. 1<=Soyez 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:
"Écraser l'élément correspondant de la pile par le premier élément de la pile. 1<=Soyez 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)
)
push
pour charger les données en haut de la piledrop
pour retirer le haut de la piledup
pour charger l'élément à la position spécifiée sur la pile en haut de la pile.swap
pour échanger l'élément à la position spécifiée avec le haut de la pile.C'est comme ça.
Avec un entier de 1 octet chargé en haut de la pile, il boucle le nombre de fois.
Lorsque la boucle se termine, le premier entier de 1 octet de la pile est rejeté.
loop_last
est utilisé pour interrompre le traitement de la boucle, mais contrairement au frein
général, le traitement lui-même est exécuté jusqu'à la fin de la boucle ]
(pensez-y comme simplement définir le drapeau de fin). Kato. En fait, je viens de réinitialiser la variable de contrôle à 0).
def loop_of(*statements: str) -> str:
"Boucle pour un entier de 1 octet de TOP"
return c.block_of(
c.for_loop(TOP, *statements),
c.move_ptr(TOP)
)
def loop_last(num: int) -> str:
"Préparez-vous à terminer la boucle.num est la position de la variable de contrôle dans la boucle.Le traitement continue"
pos = -ELEMENT_SIZE * (num + 1)
return c.clear_pos(pos + IDX_BYTE)
Addition et soustraction de 1 octet.
Si deux entiers de 1 octet se trouvent sur la pile, ajoutez ou soustrayez les deux. Après le calcul, les deux éléments d'origine sont supprimés et remplacés par le résultat du calcul (c'est-à-dire qu'un élément de la pile est réduit).
def add_byte() -> str:
"1 octet supplémentaire"
return c.block_of(
c.for_loop(TOP + IDX_BYTE, c.inc_pos(SECOND + IDX_BYTE)),
c.move_ptr(TOP)
)
def sub_byte() -> str:
"Soustraction de 1 octet"
return c.block_of(
c.for_loop(TOP + IDX_BYTE, c.dec_pos(SECOND + IDX_BYTE)),
c.move_ptr(TOP)
)
Ceci est une instruction ʻif` sur 1 octet.
Après la fin de l'instruction ʻif`, l'entier de 1 octet au début de la pile est ignoré.
De plus, puisque le travail utilise l'autre partie de l'élément 1 octet (3 octets restants), La profondeur de la pile ne change pas.
def if_nz(then_statement: str, else_statement: str = "") -> str:
"Lorsque le premier octet de la pile est NZ.Jeter le haut de la pile après la fin"
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:
"Lorsque le premier octet de la pile est Z.Jeter le haut de la pile après la fin"
return if_nz(else_statement, then_statement)
def if_eq(value: int, then_statement: str, else_statement: str = "") -> str:
"Lorsque le premier octet de la pile est égal à value.Jeter le haut de la pile après la fin"
return c.block_of(
push_byte(value),
sub_byte(),
if_z(then_statement, else_statement)
)
Calculs de fraction à virgule fixe (addition, soustraction, multiplication) et instructions «si».
En raison de la représentation signe + valeur absolue, l'addition et la soustraction sont inopinément gênantes.
Je pense qu'il vaut peut-être mieux utiliser la méthode Karatsuba pour la multiplication, mais je crains que cela ne change pas car l'addition et la soustraction semblent être plus lentes que prévu, mais maintenant je calcule avec la méthode de calcul du pinceau.
def if_nz_decimal(then_statement: str, else_statement: str = "") -> str:
"Lorsque la virgule décimale fixe de 3 octets est NZ.Jeter le haut de la pile après la fin"
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:
"Lorsque la virgule décimale fixe de 3 octets est NZ.Jeter le haut de la pile après la fin"
return if_nz_decimal(else_statement, then_statement)
def if_negative_decimal(then_statement: str, else_statement: str = "") -> str:
"Lorsque la virgule décimale fixe de 3 octets est un nombre négatif.Jeter le haut de la pile après la fin"
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:
"Ajout de valeurs absolues de virgule décimale fixe de 3 octets"
# SECOND/En supposant que les symboles de TOP sont les mêmes
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:
"Décrémenter les deux entiers jusqu'à ce que l'un devienne 0"
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:
"Décrémenter les deux fractions jusqu'à ce que l'une devienne 0"
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:
"Si la partie entière de SECOND est différente de 0, TOP/Retournez DEUXIÈME"
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:
"Si la partie fractionnaire de TOP est différente de 0, déplacez TOP à 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:
"Soustrayez de TOP par la partie fractionnaire de SECOND et passez à la position SECOND"
return c.block_of(
#Déplacer le signe(Déplacez-vous d'abord avec des mesures délicates)
c.override_data(TOP + IDX_SGN, SECOND + IDX_SGN),
#Décrémenter seulement une petite partie de SECOND
c.for_loop(
SECOND + IDX_DEC,
c.dec_data_tricky(TOP + IDX_DEC, 2)
),
#Déplacer les résultats vers SECOND
c.move_data(TOP + IDX_DEC, SECOND + IDX_DEC),
c.move_data(TOP + IDX_INT, SECOND + IDX_INT)
# TODO -0.0 à 0.Doit-il être converti en 0?
)
def _sub_abs() -> str:
"Soustraction de la valeur absolue de la virgule décimale fixe de 3 octets"
#Diminuer jusqu'à ce que l'un ou l'autre devienne 0.La réponse est celle qui reste(Y compris le code)
return c.block_of(
#Décrémenter les deux entiers jusqu'à ce que l'un devienne 0
_dec_both_abs_int(),
#Si la partie entière de SECOND est différente de 0, TOP/Retournez DEUXIÈME
_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/Lorsque les deux SECONDES ont une partie entière de 0
_dec_both_abs_decimal(),
_if_top_decimal_is_nz_then_override()
)
)
)
def add_decimal() -> str:
"Ajout de fractions à virgule fixe"
#Si les signes sont identiques, ajoutez les valeurs absolues
#Soustraction absolue si les signes sont différents
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:
"Soustraction de fraction à virgule fixe"
#Inversez le signe et ajoutez(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:
"Multiplication des valeurs absolues de la virgule décimale fixe de 3 octets"
#La partie entière et la partie fraction sont calculées en unités d'octets, comme le calcul de la brosse.
# A1. A2
# x B1. B2
# ---------------------
# .A1xB2 A2xB2
# + A1xB1.A2xB1
# ----------------------
# R1 . R2 R3
# <--------->Plage requise
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
#Calculé à partir du chiffre supérieur en raison du traitement de report
return c.block_of(
c.multi_data_tricky(idx_a1, idx_b1, idx_r1, 1), # AxC (1 octet)
c.multi_data_tricky(idx_a1, idx_b2, idx_r2, 2), # AxD (2 octets, y compris le report)
c.multi_data_tricky(idx_a2, idx_b1, idx_r2, 2), # BxC (2 octets, y compris le report)
c.multi_data_tricky(idx_a2, idx_b2, idx_r3, 3), # BxD (3 octets, y compris le report)
c.clear_pos(idx_r3), #Effacez R3 car seul le report est nécessaire
)
def _xor_sign() -> str:
#+ Si les signes sont les mêmes, moins s'ils sont différents
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:
"Multiplication à virgule fixe 3 octets"
# A * B => R
return c.block_of(
_multi_decimal_abs(), #Trouvez la valeur absolue de R
_xor_sign(), #Trouvez le signe de R
c.move_ptr(NEXT), #L'état de la pile{A, B, R}
override(2), #Écraser R en position A
drop() #Supprimer B
)
def if_lt_decimal(then_statement: str, else_statement: str = "") -> str:
return c.block_of(
swap(1), # {A, B} -> {B, A}Dans l'ordre de. then/Pour ne pas changer le nombre de piles lors de l'exécution d'autre
dup(1), # {B, A, B}
sub_decimal(), # {B, R (= A - B)}
#A si R est négatif< 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)
Je coupe les coins ronds au même endroit, et il existe deux types de 0, «+ 0.0» et «-0.0».
Par conséquent, ʻif_negative_decimaljugera
-0.0` comme un nombre négatif.
En fait, puisque cette fonction est utilisée pour juger de la divergence de l'ensemble de Mandelbrot, elle peut être jugée par erreur de temps en temps. Eh bien, c'est dans la plage d'erreur, n'est-ce pas?
Une sortie de caractère et une macro de sortie de chaîne de caractères (idiote).
def put_char() -> str:
"1 octet au début de la pile(Un personnage)Production"
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 ↑ Il vaut mieux vérifier si la différence avec la valeur précédente est plus courte
result += c.exec_pos(NOW, ".")
result += c.clear_pos(NOW)
return c.delete_useless(result)
C'est finalement le sujet principal.
Voir ci-dessous pour plus d'informations sur l'ensemble de Mandelbrot.
Serait-ce comme ça s'il était écrit en langage C?
#include <stdio.h>
#define X_BEGIN -2.0
#define X_END 1.0
#define Y_BEGIN -0.9375
#define Y_END 0.9375
//Nombre de répétitions
#define C_MAX 26
//Seuil de divergence(2.Valeur au carré de 0)
#define THRESHOLD2 4
void main(void)
{
int columns = 128;
int rows = 40;
//Valeur d'ajout sur l'axe X
float x_step = (X_END - X_BEGIN) / (columns - 1);
//Valeur d'ajout sur l'axe Y
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)
{
//Divergence
ch = count;
break;
}
else
{
float zx_next = zx2 - zy2 + cx;
float zy_next = zx * zy * 2 + cy;
if (zx_next == zx && zy_next == zy)
{
//convergence
break;
}
else
{
zx = zx_next;
zy = zy_next;
count++;
}
}
}
putchar(ch);
x0 += x_step;
}
putchar('\n');
y0 += y_step;
}
}
Je pense qu'il y a beaucoup d'autres points, comme si la fraction peut être «float» ou la comparaison entre «float» avec «==», mais je vais laisser ça pour l'instant.
Pour rendre l'écriture aussi facile que possible avec Brainf * ck, le jugement de savoir si la valeur absolue du nombre complexe dépasse «2,0» est remplacé par le jugement de savoir si la valeur au carré dépasse directement «4,0» sans utiliser «sqrt». Je suis.
Écrivons ceci avec Brainf * ck.
Au fait, les macros de pile sont supposées être enregistrées dans bf_stack.py
.
import bf_core as c
import bf_stack as s
#Gamme de l'axe X
(X_BEGIN, X_END) = (-2.0, 1.0)
#Plage de l'axe Y
(Y_BEGIN, Y_END) = (-0.9375, 0.9375)
#Nombre de répétitions
C_MAX = 26
#Seuil de divergence(2.Valeur au carré de 0)
THRESHOLD2 = 4
def mandelbrot(columns: int, rows: int) -> str:
#Valeur d'ajout sur l'axe X
x_step = (X_END - X_BEGIN) / (columns - 1)
#Valeur d'ajout sur l'axe Y
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(
#Divergence
# 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(
#convergence
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)
Lorsque vous utilisez réellement la macro de traitement de pile, cela est difficile car vous devez écrire les éléments de la pile dans des positions relatives.
Quoi qu'il en soit, enregistrez-le sous mandelbrot.py
et exécutez-le.
python3 -B mandelbrot.py > mandelbrot.bf
./bf2c -F mandelbrot.bf
gcc -O2 -o mandelbrot mandelbrot.c
./mandelbrot
Je pense que la compilation se terminera bientôt, mais cela prendra environ 5 à 7 secondes pour s'exécuter. lent.
Au fait, je pense que vous pouvez compiler normalement avec un compilateur Brainf * ck général, mais veuillez utiliser un compilateur optimisé autant que possible.
De plus, je pense que l'interprète Brainf * ck prendra beaucoup de temps. À tout le moins, je pense qu'il vaut mieux utiliser un interpréteur qui fonctionne pour l'optimisation.
Il y a Implémentation de Mandelbrot dans le célèbre Brainf * ck, mais cela ne prend que 1 à 2 secondes, donc il est encore amélioré. Il semble y avoir de la place.
Pour être précis, l'ensemble de Mandelbrot est un ensemble de nombres complexes qui ne divergent pas, donc la partie noire du résultat est l'ensemble de Mandelbrot.
Cependant, en suivant l'application générale de dessin de Mandelbrot, je vais la colorer en fonction du nombre de fois où elle diverge.
La source est simple, elle est donc omise (enregistrée dans github en tant qu'échantillon du compilateur). Si vous le déplacez sur un terminal qui peut utiliser la séquence d'échappement ANSI, il sera coloré.
Après cela, vous pouvez dessiner l'ensemble de Julia, etc. avec juste une petite modification.
Recommended Posts