Анализ ошибки в коде для генерации фракталов с помощью системы Лиденмайера

Я разрабатывал класс для рисования фрактальных фигур, используя L-систему (систему Лиденмайера). В реализации возникла ошибка выполнения, требующая детального разбора.

Исходный код

Приведу ключевые фрагменты моего класса instinctTurtle, унаследованного от turtle.Turtle:

class instinctTurtle(turtle.Turtle):
    axiom = ''
    turtleX = 0
    turtleY = 0
    turtleRotate = 0.0
    teta = 0
    lenght = 0
    
    def __init__(self, shape="classic", undobuffersize=1000, visible=False):
        super().__init__(shape, undobuffersize, visible)
    
    def setDrawingParameters(self, teta=60, lenght=10, axiom='F++F++F', 
                             newF='F-F++F-F', newB='B', newK='K'):
        self.teta = teta
        self.lenght = lenght
        self.axiom = axiom
        self.newF = newF
        self.newB = newB
        self.newK = newK
    
    def createInstinct(self, deep=1):
        self.instinct = ''
        for i in range(deep):
            for j in range(len(self.axiom)):
                instinct = ''
                match self.axiom[j]:
                    case 'F':
                        instinct += self.newF
                    case 'B':
                        instinct += self.newB
                    case 'K':
                        instinct += self.newK
                    case _:
                        instinct += self.axiom[j]
                self.axiom = instinct
    
    def runInstinct(self):
        for i in range(len(self.axiom)):
            cmd = self.axiom[i]
            match cmd:
                case 'F':
                    self.forward(self.lenght)
                case 'B':
                    self.up()
                    self.forward(self.lenght)
                    self.down()
                case '+':
                    self.right(self.teta)
                case '-':
                    self.left(self.teta)
                case '[':
                    self.turtleX = self.xcor()
                    self.turtleY = self.ycor()
                    self.turtleRotate = self.heading()
                case ']':
                    self.setx(self.turtleX)
                    self.sety(self.turtleY)
                    self.setheading(self.turtleRotate)

Проблема при выполнении

При запуске следующего кода:

N = instinctTurtle()
N.setDrawingParameters()
N.createInstinct(3)
N.runInstinct()

Возникает ошибка:

Traceback (most recent call last):
  File "E:\Python\Библиотеки\turtleGeometry\src\turtleGeometry\main.py", line 142, in 
    N.createInstinct(3)
  File "E:\Python\Библиотеки\turtleGeometry\src\turtleGeometry\main.py", line 108, in createInstinct
    match self.axiom[j]:
IndexError: string index out of range

Диагностика проблемы

Основная ошибка находится в методе createInstinct. Рассмотрим логику работы этого метода:

  • Внешний цикл for i in range(deep) должен выполнить заданное количество итераций (в данном случае 3)
  • Внутренний цикл for j in range(len(self.axiom)) проходит по символам аксиомы
  • Проблема возникает из-за того, что внутри внутреннего цикла происходит перезапись self.axiom = instinct

Это приводит к следующему:

  1. На первой итерации внешнего цикла (i=0) длина аксиомы равна 7 ('F++F++F')
  2. Внутренний цикл начинает обрабатывать символы, но после обработки первого символа 'F' аксиома заменяется на строку 'F-F++F-F'
  3. Когда внутренний цикл пытается обратиться к self.axiom[1], строка уже имеет другую длину и содержимое
  4. Если новая строка короче исходной, возникает IndexError

Дополнительные наблюдения

Мои эксперименты подтверждают эту гипотезу:

  • При замене self.axiom[j] на self.axiom[0] программа рисует одну итерацию, затем зависает
  • При использовании индекса 1 возникает та же ошибка IndexError
  • Ошибка связана не с работой range, а с изменением строки во время итерации по ней

Рекомендации по исправлению

Для корректной работы метода createInstinct необходимо:

  1. Создавать новую строку для каждой итерации внешнего цикла
  2. Не изменять исходную строку во время итерации по ней
  3. Использовать временную переменную для накопления результата

Примечание: Перехожу на Python с JavaScript, поэтому в коде присутствует camelCase. Прошу отнестись с пониманием к стилистическим особенностям.