Coding/Python 삽질기2024. 3. 7. 00:45

Python으로 작성한 SQL문에서 대량의 insert를 수행시 느려지는 문제가 있어서, 

DB전문가에게 문의를 하니, 간단히 답을 알려 주셨다.

해결책은 "BEGIN TRANSACTION"을 사용하면 되는 것이었다.

이 한줄을 넣고 나니, 10배 이상 속도가 빨라졌다;;

[기존코드]

    def insert(self, data):
        insert_memo_sql = '''INSERT INTO minim(title, memo) VALUES(?, ?);'''
        try:
            cur = self.db_conn.cursor()
            cur.execute(insert_memo_sql, (data[0], data[1]))
            self.db_conn.commit()
            print(cur.lastrowid)
            return cur.lastrowid
        except sqlite3.Error as e:
            print(e)
        return -1

[개선코드]

    def insert_bigdata(self, big_data):
        insert_memo_sql = '''INSERT INTO minim(title, memo) VALUES(?, ?);'''
        try:
            cur = self.db_conn.cursor()
            cur.execute("BEGIN TRANSACTION")
            for data in big_data:
                cur.execute(insert_memo_sql, (data[0], data[1]))
            self.db_conn.commit()
            print(cur.lastrowid)
            return cur.lastrowid
        except sqlite3.Error as e:
            print(e)
            self.db_conn.rollback()  # rollback the transaction if there's any error
        return -1

 

'Coding > Python 삽질기' 카테고리의 다른 글

Cardioid 그리기  (0) 2024.02.17
[Python] PNG to ICO 변환 하기  (0) 2024.01.04
[Python] 프랙탈 나무 만들기  (0) 2023.09.15
Posted by chobocho
Coding/Python 삽질기2024. 2. 17. 19:16
import math
import pygame


def init_game():
    pygame.init()
    pygame.key.set_repeat(0)
    canvas = pygame.display.set_mode((800, 600))
    fps_clock = pygame.time.Clock()
    return canvas, fps_clock


def main():
    canvas, fps_clock = init_game()

    title = 'Cardioid'
    pygame.display.set_caption(title)
    max_dots = 300

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                return

        draw_cardioid(canvas, max_dots)
        pygame.display.update()
        fps_clock.tick(30)


def draw_cardioid(canvas, max_dots):

    canvas.fill((0, 0, 0))
    start_x = canvas.get_width() // 2
    start_y = canvas.get_height() // 2
    radius = 250

    for i in range(max_dots):
        radian = (math.pi / 180) * (360 / max_dots) * i
        sx = start_x + int(math.cos(radian) * radius)
        sy = start_y + int(math.sin(radian) * radius)

        ex = start_x + int(math.cos(radian * 2) * radius)
        ey = start_y + int(math.sin(radian * 2) * radius)

        pygame.draw.line(canvas, (0, 255, 0), (sx, sy), (ex, ey))


if __name__ == '__main__':
    main()

100개의 점을 사용한 경우
200개의 점을 사용한 경우
300개의 점을 사용한 경우

'Coding > Python 삽질기' 카테고리의 다른 글

[SQL] BEGIN TRANSACTION  (0) 2024.03.07
[Python] PNG to ICO 변환 하기  (0) 2024.01.04
[Python] 프랙탈 나무 만들기  (0) 2023.09.15
Posted by chobocho
Coding/Python 삽질기2023. 9. 6. 23:44

파이썬 소스 코드를 빌드해서 python.exe  를 만들어 보자

1. 빌드 환경 구성

  • Visusal Studio를 설치한다
  • Python 3.10버전을 설치한다

 

2. 파이썬 소스를 다운 받는다 (우리는 3.11을 사용한다)

https://www.python.org/ftp/python/3.11.5/Python-3.11.5.tgz

 

3. 적당한 폴더에 압축을 푼다

4. 설치경로\PCbuild\build.bat 를 실행한다

5. 잠시 뒤 

설치경로\PCbuild\amd64\python.exe 가 생성 됨을 볼수있다.

'Coding > Python 삽질기' 카테고리의 다른 글

[Python] 프랙탈 나무 만들기  (0) 2023.09.15
HTTP Protocol  (0) 2023.08.18
[AI] Stable Diffusion 설치 하기  (0) 2023.06.09
Posted by chobocho
Coding/Python 삽질기2023. 4. 5. 01:25

1. 아래 사이트에서  설치해야 하는 PyTorch 버전 체크

https://pytorch.org/get-started/locally/

 

PyTorch

An open source machine learning framework that accelerates the path from research prototyping to production deployment.

pytorch.org

2. Command를 복사하여 실행한다.

pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117

3.  Python을 실행하여 확인 한다.

 

Posted by chobocho
Coding/Python 삽질기2023. 3. 17. 01:20

2부에 이어서, 3부를 시작 합니다.

 

8. 이제 생성된 코드를 리팩토링 해달라고 부탁 합니다.

from enum import Enum

class Command(Enum):
    PUSH = 'PUSH'
    POP = 'POP'
    ADD = 'ADD'
    SUB = 'SUB'
    MUL = 'MUL'
    DIV = 'DIV'
    MOD = 'MOD'
    DUP = 'DUP'
    SWAP = 'SWAP'
    JMP = 'JMP'
    JG = 'JG'
    JL = 'JL'
    JZ = 'JZ'
    JNZ = 'JNZ'
    CMP = 'CMP'
    MOV = 'MOV'
    NOP = 'NOP'
    HALT = 'HALT'


class StackMachine:
    """
    PUSH <value>: Push a value onto the stack. e.g., PUSH 10
    POP: Remove and return the top value from the stack.
    ADD: Remove the top two values from the stack, calculate their sum, and push the result onto the stack.
    SUB: Remove the top two values from the stack, calculate their difference, and push the result onto the stack.
    MUL: Remove the top two values from the stack, calculate their product, and push the result onto the stack.
    DIV: Remove the top two values from the stack, calculate their division, and push the result onto the stack.
    MOD: Remove the top two values from the stack, calculate their remainder, and push the result onto the stack.
    DUP: Copy the top value from the stack and push it onto the stack.
    SWAP: Swap the top two values on the stack.
    JMP <label>: Move program execution to the given label. e.g., JMP LOOP
    JG <label>: If the top value on the stack is greater than 0, move program execution to the given label.
    JL <label>: If the top value on the stack is less than 0, move program execution to the given label.
    JZ <label>: If the top value on the stack is 0, move program execution to the given label.
    JNZ <label>: If the top value on the stack is not 0, move program execution to the given label.
    CMP: Compare the top two values on the stack and push the result onto the stack.
    (-1: First value is smaller, 0: Both values are equal, 1: First value is larger)
    MOV <register>: Move the top value from the stack to the specified register (AX, BX, CX, or DX).
    NOP: Perform no operation. This command is ignored in the program.
    HALT: Stop program execution.
    """

    def __init__(self):
        self.stack = []
        self.program = []
        self.labels = {}
        self.registers = {'AX': 0, 'BX': 0, 'CX': 0, 'DX': 0}
        self.instruction_pointer = 0

    def load_program(self, program):
        self.program = program
        self.labels = {line[:-1]: i for i, line in enumerate(program) if line.endswith(':')}

    def parse_command(self, command_string):
        command, *args = command_string.split()
        if command not in Command.__members__:
            raise ValueError(f"Invalid command: {command}")
        return Command[command], args

    def execute_command(self, command, args):
        if command == Command.PUSH:
            value = int(args[0]) if 'X' not in args[0] else self.registers[args[0]]
            self.stack.append(value)
        elif command == Command.POP:
            self.stack.pop()
        elif command == Command.ADD:
            self.stack.append(self.stack.pop() + self.stack.pop())
        elif command == Command.SUB:
            b, a = self.stack.pop(), self.stack.pop()
            self.stack.append(a - b)
        elif command == Command.MUL:
            self.stack.append(self.stack.pop() * self.stack.pop())
        elif command == Command.DIV:
            b, a = self.stack.pop(), self.stack.pop()
            self.stack.append(a // b)
        elif command == Command.MOD:
            b, a = self.stack.pop(), self.stack.pop()
            self.stack.append(a % b)
        elif command == Command.DUP:
            self.stack.append(self.stack[-1])
        elif command == Command.SWAP:
            self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
        elif command == Command.JMP:
            self.instruction_pointer = self.labels[args[0]]
            return
        elif command == Command.JG:
            if self.stack.pop() == 1:
                self.instruction_pointer = self.labels[args[0]]
                return
        elif command == Command.JL:
            if self.stack.pop() == -1:
                self.instruction_pointer = self.labels[args[0]]
                return
        elif command == Command.JZ:
            if self.stack.pop() == 0:
                self.instruction_pointer = self.labels[args[0]]
                return
        elif command == Command.JNZ:
            if self.stack.pop() != 0:
                self.instruction_pointer = self.labels[args[0]]
                return
        elif command == Command.CMP:
            b, a = self.stack.pop(), self.stack.pop()
            self.stack.append((a > b) - (a < b))
        elif command == Command.MOV:
            self.registers[args[0]] = self.stack[-1]
        elif command == Command.NOP:
            pass
        elif command == Command.HALT:
            return
        else:
            raise ValueError(f"Unknown command: {command}")

        self.instruction_pointer += 1

    def run(self):
        for _ in range(20000):
            if self.instruction_pointer >= len(self.program):
                break
            command_string = self.program[self.instruction_pointer]
            command, args = self.parse_command(command_string)
            self.execute_command(command, args)

생성된 코드를 돌려보면, 역시 안됩니다. 

디버깅 하면, Label 처리가 안되었을 알 수 있습니다. 

 

9. Label 처리를  위한 코드를 손으로 추가 하여 수정 합니다.

from enum import Enum


class Command(Enum):
    PUSH = 'PUSH'
    POP = 'POP'
    ADD = 'ADD'
    SUB = 'SUB'
    MUL = 'MUL'
    DIV = 'DIV'
    MOD = 'MOD'
    DUP = 'DUP'
    SWAP = 'SWAP'
    JMP = 'JMP'
    JG = 'JG'
    JL = 'JL'
    JZ = 'JZ'
    JNZ = 'JNZ'
    CMP = 'CMP'
    MOV = 'MOV'
    NOP = 'NOP'
    HALT = 'HALT'
    LABEL = 'LABEL'


class StackMachine:
    """
    PUSH <value>: Push a value onto the stack. e.g., PUSH 10
    POP: Remove and return the top value from the stack.
    ADD: Remove the top two values from the stack, calculate their sum, and push the result onto the stack.
    SUB: Remove the top two values from the stack, calculate their difference, and push the result onto the stack.
    MUL: Remove the top two values from the stack, calculate their product, and push the result onto the stack.
    DIV: Remove the top two values from the stack, calculate their division, and push the result onto the stack.
    MOD: Remove the top two values from the stack, calculate their remainder, and push the result onto the stack.
    DUP: Copy the top value from the stack and push it onto the stack.
    SWAP: Swap the top two values on the stack.
    JMP <label>: Move program execution to the given label. e.g., JMP LOOP
    JG <label>: If the top value on the stack is greater than 0, move program execution to the given label.
    JL <label>: If the top value on the stack is less than 0, move program execution to the given label.
    JZ <label>: If the top value on the stack is 0, move program execution to the given label.
    JNZ <label>: If the top value on the stack is not 0, move program execution to the given label.
    CMP: Compare the top two values on the stack and push the result onto the stack.
    (-1: First value is smaller, 0: Both values are equal, 1: First value is larger)
    MOV <register>: Move the top value from the stack to the specified register (AX, BX, CX, or DX).
    NOP: Perform no operation. This command is ignored in the program.
    HALT: Stop program execution.
    """

    def __init__(self):
        self.stack = []
        self.program = []
        self.labels = {}
        self.registers = {'AX': 0, 'BX': 0, 'CX': 0, 'DX': 0}
        self.instruction_pointer = 0

    def load_program(self, program):
        self.program = program
        self.labels = {line[:-1]: i for i, line in enumerate(program) if line.endswith(':')}

    def parse_command(self, command_string):
        command, *args = command_string.split()
        if command not in Command.__members__:
            if ':' in command:
                return Command['LABEL'], args
            else:
                raise ValueError(f"Unknown command: {command}")
        return Command[command], args

    def execute_command(self, command, args):
        if command == Command.PUSH:
            value = int(args[0]) if 'X' not in args[0] else self.registers[args[0]]
            self.stack.append(value)
        elif command == Command.POP:
            self.stack.pop()
        elif command == Command.ADD:
            self.stack.append(self.stack.pop() + self.stack.pop())
        elif command == Command.SUB:
            b, a = self.stack.pop(), self.stack.pop()
            self.stack.append(a - b)
        elif command == Command.MUL:
            self.stack.append(self.stack.pop() * self.stack.pop())
        elif command == Command.DIV:
            b, a = self.stack.pop(), self.stack.pop()
            self.stack.append(a // b)
        elif command == Command.MOD:
            b, a = self.stack.pop(), self.stack.pop()
            self.stack.append(a % b)
        elif command == Command.DUP:
            self.stack.append(self.stack[-1])
        elif command == Command.SWAP:
            self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
        elif command == Command.JMP:
            self.instruction_pointer = self.labels[args[0]]
            return
        elif command == Command.JG:
            if self.stack.pop() == 1:
                self.instruction_pointer = self.labels[args[0]]
                return
        elif command == Command.JL:
            if self.stack.pop() == -1:
                self.instruction_pointer = self.labels[args[0]]
                return
        elif command == Command.JZ:
            if self.stack.pop() == 0:
                self.instruction_pointer = self.labels[args[0]]
                return
        elif command == Command.JNZ:
            if self.stack.pop() != 0:
                self.instruction_pointer = self.labels[args[0]]
                return
        elif command == Command.CMP:
            b, a = self.stack.pop(), self.stack.pop()
            self.stack.append((a > b) - (a < b))
        elif command == Command.MOV:
            self.registers[args[0]] = self.stack[-1]
        elif command == Command.NOP:
            pass
        elif command == Command.HALT:
            return
        elif command == Command.LABEL:
            pass
        else:
            raise ValueError(f"Unknown command: {command}")

        self.instruction_pointer += 1

    def run(self):
        for _ in range(20000):
            if self.instruction_pointer >= len(self.program):
                break
            command_string = self.program[self.instruction_pointer]
            command, args = self.parse_command(command_string)
            self.execute_command(command, args)

 

10.  execute_command() 함수 안의 수 많은 if - else 조합을 없애기 위해서 약간 코드를 고칩니다.

    def command_push(self, args):
        value = int(args[0]) if 'X' not in args[0] else self.registers[args[0]]
        self.stack.append(value)

    def command_pop(self, args):
        self.stack.pop()

위와 같이 함수를 2개 추가하고, 코드를 약간 고쳐 줍니다.

 

11. 수정 된 코드를 넣고, 리팩토링을 부탁합니다.

from enum import Enum


class Command(Enum):
    PUSH = 'PUSH'
    POP = 'POP'
    ADD = 'ADD'
    SUB = 'SUB'
    MUL = 'MUL'
    DIV = 'DIV'
    MOD = 'MOD'
    DUP = 'DUP'
    SWAP = 'SWAP'
    JMP = 'JMP'
    JG = 'JG'
    JL = 'JL'
    JZ = 'JZ'
    JNZ = 'JNZ'
    CMP = 'CMP'
    MOV = 'MOV'
    NOP = 'NOP'
    HALT = 'HALT'
    LABEL = 'LABEL'


class StackMachine:
    """
    PUSH <value>: Push a value onto the stack. e.g., PUSH 10
    POP: Remove and return the top value from the stack.
    ADD: Remove the top two values from the stack, calculate their sum, and push the result onto the stack.
    SUB: Remove the top two values from the stack, calculate their difference, and push the result onto the stack.
    MUL: Remove the top two values from the stack, calculate their product, and push the result onto the stack.
    DIV: Remove the top two values from the stack, calculate their division, and push the result onto the stack.
    MOD: Remove the top two values from the stack, calculate their remainder, and push the result onto the stack.
    DUP: Copy the top value from the stack and push it onto the stack.
    SWAP: Swap the top two values on the stack.
    JMP <label>: Move program execution to the given label. e.g., JMP LOOP
    JG <label>: If the top value on the stack is greater than 0, move program execution to the given label.
    JL <label>: If the top value on the stack is less than 0, move program execution to the given label.
    JZ <label>: If the top value on the stack is 0, move program execution to the given label.
    JNZ <label>: If the top value on the stack is not 0, move program execution to the given label.
    CMP: Compare the top two values on the stack and push the result onto the stack.
    (-1: First value is smaller, 0: Both values are equal, 1: First value is larger)
    MOV <register>: Move the top value from the stack to the specified register (AX, BX, CX, or DX).
    NOP: Perform no operation. This command is ignored in the program.
    HALT: Stop program execution.
    """

    def __init__(self):
        self.stack = []
        self.program = []
        self.labels = {}
        self.registers = {'AX': 0, 'BX': 0, 'CX': 0, 'DX': 0}
        self.command_list = {}
        self.instruction_pointer = 0
        self._init_command_list()

    def _init_command_list(self):
        self.command_list[Command.PUSH] = self.command_push
        self.command_list[Command.POP] = self.command_pop
        self.command_list[Command.ADD] = self.command_add
        self.command_list[Command.SUB] = self.command_sub
        self.command_list[Command.MUL] = self.command_mul
        self.command_list[Command.DIV] = self.command_div
        self.command_list[Command.MOD] = self.command_mod
        self.command_list[Command.DUP] = self.command_dup
        self.command_list[Command.SWAP] = self.command_swap
        self.command_list[Command.JMP] = self.command_jmp
        self.command_list[Command.JG] = self.command_jg
        self.command_list[Command.JL] = self.command_jl
        self.command_list[Command.JZ] = self.command_jz
        self.command_list[Command.JNZ] = self.command_jnz
        self.command_list[Command.CMP] = self.command_cmp
        self.command_list[Command.MOV] = self.command_mov
        self.command_list[Command.NOP] = self.command_nop
        self.command_list[Command.HALT] = self.command_halt
        self.command_list[Command.LABEL] = self.command_label

    def load_program(self, program):
        self.program = program
        self.labels = {line[:-1]: i for i, line in enumerate(program) if line.endswith(':')}

    def parse_command(self, command_string):
        command, *args = command_string.split()
        if command not in Command.__members__:
            if ':' in command:
                return Command['LABEL'], args
            else:
                raise ValueError(f"Unknown command: {command}")
        return Command[command], args

    def execute_command(self, command, args):
        if command in self.command_list:
            self.command_list[command](args)
        else:
            raise ValueError(f"Unknown command: {command}")

        self.instruction_pointer += 1

    def command_push(self, args):
        value = int(args[0]) if 'X' not in args[0] else self.registers[args[0]]
        self.stack.append(value)

    def command_pop(self, args):
        self.stack.pop()

    def command_add(self, args):
        self.stack.append(self.stack.pop() + self.stack.pop())

    def command_sub(self, args):
        b, a = self.stack.pop(), self.stack.pop()
        self.stack.append(a - b)

    def command_mul(self, args):
        self.stack.append(self.stack.pop() * self.stack.pop())

    def command_div(self, args):
        b, a = self.stack.pop(), self.stack.pop()
        self.stack.append(a // b)

    def command_mod(self, args):
        b, a = self.stack.pop(), self.stack.pop()
        self.stack.append(a % b)

    def command_dup(self, args):
        self.stack.append(self.stack[-1])

    def command_swap(self, args):
        self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]

    def command_jmp(self, args):
        self.instruction_pointer = self.labels[args[0]]

    def command_jg(self, args):
        if self.stack.pop() == 1:
            self.instruction_pointer = self.labels[args[0]]

    def command_jl(self, args):
        if self.stack.pop() == -1:
            self.instruction_pointer = self.labels[args[0]]

    def command_jz(self, args):
        if self.stack.pop() == 0:
            self.instruction_pointer = self.labels[args[0]]

    def command_jnz(self, args):
        if self.stack.pop() != 0:
            self.instruction_pointer = self.labels[args[0]]

    def command_cmp(self, args):
        b, a = self.stack.pop(), self.stack.pop()
        self.stack.append((a > b) - (a < b))

    def command_mov(self, args):
        self.registers[args[0]] = self.stack[-1]

    def command_nop(self, args):
        pass

    def command_halt(self, args):
        pass

    def command_label(self, args):
        pass

    def run(self):
        for _ in range(20000):
            if self.instruction_pointer >= len(self.program):
                break
            command_string = self.program[self.instruction_pointer]
            command, args = self.parse_command(command_string)
            self.execute_command(command, args)

위와 같이 아름답게 코드를 고쳐 주십니다.

그래서 나온 최종 버전은 아래에 있습니다.

https://github.com/chobocho/simple_vm

 

GitHub - chobocho/simple_vm: stack based simple stack machine

stack based simple stack machine. Contribute to chobocho/simple_vm development by creating an account on GitHub.

github.com

 

결론: 

1) 웬만하면 구글링 해서 open source 를 쓰자.

2) ChatGPT-4도 완전한 코드를 짜주지는 않는다 (디버깅은 필수)

Posted by chobocho
Coding/Python 삽질기2023. 3. 17. 01:08

1부에 이어서, 2부를 시작 합니다.

4.  ChatGPT님이 주신 코드를 디버깅 해서 돌아가게 만듭니다.

class StackMachine:
    def __init__(self):
        self.stack = []
        self.program = []
        self.labels = {}
        self.ip = 0
        self.ax = 0
        self.bx = 0
        self.cx = 0
        self.dx = 0

    def load_program(self, program):
        self.program = program
        for i, line in enumerate(program):
            if line.endswith(':'):
                self.labels[line[:-1]] = i

    def run(self):
        count = 20000
        while self.ip < len(self.program) and count > 0:
            count -= 1
            cmd = self.program[self.ip].split()
            op = cmd[0].upper()

            if op == 'PUSH':
                self.stack.append(int(cmd[1]))
            elif op == 'POP':
                self.stack.pop()
            elif op == 'ADD':
                self.stack.append(self.stack.pop() + self.stack.pop())
            elif op == 'SUB':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a - b)
            elif op == 'MUL':
                self.stack.append(self.stack.pop() * self.stack.pop())
            elif op == 'DIV':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a // b)
            elif op == 'MOD':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a % b)
            elif op == 'DUP':
                self.stack.append(self.stack[-1])
            elif op == 'SWAP':
                self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
            elif op == 'JMP':
                self.ip = self.labels[cmd[1]]
                continue
            elif op == 'JG':
                if self.stack.pop() == 1:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'JZ':
                if self.stack.pop() == 0:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'JNZ':
                if self.stack.pop() != 0:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'CMP':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append((a > b) - (a < b))
            elif op == 'NOP':
                pass
            elif op == 'HALT':
                break

            self.ip += 1

아래와 같이 올바른 결과가 나오는 것을 확인 합니다.

C:\Users\chobo\AppData\Local\Programs\Python\Python38\python.exe C:\github\simple_vm\simple_vm\main.py 
Result: 3628800

5. 코드를 약간 리팩토링 합니다.

6.  수정한 코드를 ChatGPT님에게 드리고 주석을 영어로 바꿔달라고 합니다. 

주석은 Step1의 명령어 정리하기로 생성된 걸 그대로 붙였습니다.

class StackMachine:
    """
    PUSH <value>: Push a value onto the stack. e.g., PUSH 10
    POP: Remove and return the top value from the stack.
    ADD: Remove the top two values from the stack, calculate their sum, and push the result onto the stack.
    SUB: Remove the top two values from the stack, calculate their difference, and push the result onto the stack.
    MUL: Remove the top two values from the stack, calculate their product, and push the result onto the stack.
    DIV: Remove the top two values from the stack, calculate their division, and push the result onto the stack.
    MOD: Remove the top two values from the stack, calculate their remainder, and push the result onto the stack.
    DUP: Copy the top value from the stack and push it onto the stack.
    SWAP: Swap the top two values on the stack.
    JMP <label>: Move program execution to the given label. e.g., JMP LOOP
    JZ <label>: If the top value on the stack is 0, move program execution to the given label.
    JNZ <label>: If the top value on the stack is not 0, move program execution to the given label.
    CMP: Compare the top two values on the stack and push the result onto the stack.
         (-1: First value is smaller, 0: Both values are equal, 1: First value is larger)
    CALL <label>: Save the current position and move program execution to the given label.
    RET: Return to the previously saved position and continue program execution.
    NOP: Perform no operation. This command is ignored in the program.
    HALT: Stop program execution.
    """

    def __init__(self):
        self.stack = []
        self.program = []
        self.labels = {}
        self.register = {'AX': 0, 'BX': 0, 'CX': 0, 'DX': 0}
        self.ip = 0

    def load_program(self, program):
        self.program = program
        for i, line in enumerate(program):
            if line.endswith(':'):
                self.labels[line[:-1]] = i

    def run(self):
        count = 20000
        while self.ip < len(self.program) and count > 0:
            count -= 1
            cmd = self.program[self.ip].split()
            op = cmd[0].upper()

            if op == 'PUSH':
                if type(cmd[1]) == int:
                    self.stack.append(int(cmd[1]))
                else:
                    self.stack.append(int(self.register[cmd[1]]))

            elif op == 'POP':
                self.stack.pop()
            elif op == 'MOV':
                if cmd[1] == 'AX':
                    self.ax = self.stack[-1]
                elif cmd[1] == 'BX':
                    self.bx = self.stack[-1]
                elif cmd[1] == 'CX':
                    self.cx = self.stack[-1]
                elif cmd[1] == 'DX':
                    self.dx = self.stack[-1]
            elif op == 'ADD':
                self.stack.append(self.stack.pop() + self.stack.pop())
            elif op == 'SUB':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a - b)
            elif op == 'MUL':
                self.stack.append(self.stack.pop() * self.stack.pop())
            elif op == 'DIV':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a // b)
            elif op == 'MOD':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a % b)
            elif op == 'DUP':
                self.stack.append(self.stack[-1])
            elif op == 'SWAP':
                self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
            elif op == 'JMP':
                self.ip = self.labels[cmd[1]]
                continue
            elif op == 'JG':
                if self.stack.pop() == 1:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'JL':
                if self.stack.pop() == -1:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'JZ':
                if self.stack.pop() == 0:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'JNZ':
                if self.stack.pop() != 0:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'CMP':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append((a > b) - (a < b))
            elif op == 'NOP':
                pass
            elif op == 'HALT':
                break

            self.ip += 1
            # print(f"{self.stack} / {self.ax}")

 

7. 주석과 코드가 안 맞습니다. 주석을 코드 베이스로 업데이트 해달라고 요청합니다.

class StackMachine:
    """
    PUSH <value>: Push a value onto the stack. e.g., PUSH 10
    POP: Remove and return the top value from the stack.
    ADD: Remove the top two values from the stack, calculate their sum, and push the result onto the stack.
    SUB: Remove the top two values from the stack, calculate their difference, and push the result onto the stack.
    MUL: Remove the top two values from the stack, calculate their product, and push the result onto the stack.
    DIV: Remove the top two values from the stack, calculate their division, and push the result onto the stack.
    MOD: Remove the top two values from the stack, calculate their remainder, and push the result onto the stack.
    DUP: Copy the top value from the stack and push it onto the stack.
    SWAP: Swap the top two values on the stack.
    JMP <label>: Move program execution to the given label. e.g., JMP LOOP
    JG <label>: If the top value on the stack is greater than 0, move program execution to the given label.
    JL <label>: If the top value on the stack is less than 0, move program execution to the given label.
    JZ <label>: If the top value on the stack is 0, move program execution to the given label.
    JNZ <label>: If the top value on the stack is not 0, move program execution to the given label.
    CMP: Compare the top two values on the stack and push the result onto the stack.
    (-1: First value is smaller, 0: Both values are equal, 1: First value is larger)
    MOV <register>: Move the top value from the stack to the specified register (AX, BX, CX, or DX).
    NOP: Perform no operation. This command is ignored in the program.
    HALT: Stop program execution.
    """

    def __init__(self):
        self.stack = []
        self.program = []
        self.labels = {}
        self.register = {'AX': 0, 'BX': 0, 'CX': 0, 'DX': 0}
        self.ip = 0

    def load_program(self, program):
        self.program = program
        for i, line in enumerate(program):
            if line.endswith(':'):
                self.labels[line[:-1]] = i

    def run(self):
        count = 20000
        while self.ip < len(self.program) and count > 0:
            count -= 1
            cmd = self.program[self.ip].split()
            op = cmd[0].upper()

            if op == 'PUSH':
                if 'X' in cmd[1]:
                    self.stack.append(int(self.register[cmd[1]]))
                else:
                    self.stack.append(int(cmd[1]))
            elif op == 'POP':
                self.stack.pop()
            elif op == 'MOV':
                self.register[cmd[1]] = self.stack[-1]
            elif op == 'ADD':
                self.stack.append(self.stack.pop() + self.stack.pop())
            elif op == 'SUB':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a - b)
            elif op == 'MUL':
                self.stack.append(self.stack.pop() * self.stack.pop())
            elif op == 'DIV':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a // b)
            elif op == 'MOD':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a % b)
            elif op == 'DUP':
                self.stack.append(self.stack[-1])
            elif op == 'SWAP':
                self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
            elif op == 'JMP':
                self.ip = self.labels[cmd[1]]
                continue
            elif op == 'JG':
                if self.stack.pop() == 1:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'JL':
                if self.stack.pop() == -1:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'JZ':
                if self.stack.pop() == 0:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'JNZ':
                if self.stack.pop() != 0:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'CMP':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append((a > b) - (a < b))
            elif op == 'NOP':
                pass
            elif op == 'HALT':
                break

            self.ip += 1
            # print(f"{self.stack} / {self.ax}")

 

3부에서 계속 됩니다.

Posted by chobocho
Coding/Python 삽질기2023. 3. 17. 00:51

ChatGPT의 도움을 받아서,  Python으로  간단한 Stack 기반의  virtual machine을 만들어 보기로 했다.

1. 명령어 정의 하기

Q: stack 기반 simple vm 용 어셈블리 명령어 리스트를 만들어줘

A:
아래는 스택 기반의 간단한 가상 머신 (VM)을 위한 어셈블리 명령어 리스트입니다. 
이러한 기본 명령어를 통해 작은 프로그램을 구현하고 실행할 수 있습니다.

PUSH <value>: 스택에 값을 푸시합니다. 예: PUSH 10
POP: 스택의 맨 위 값을 제거하고 반환합니다.
ADD: 스택의 맨 위 두 개의 값을 제거하고 합계를 계산한 후, 스택에 결과를 푸시합니다.
SUB: 스택의 맨 위 두 개의 값을 제거하고 차이를 계산한 후, 스택에 결과를 푸시합니다.
MUL: 스택의 맨 위 두 개의 값을 제거하고 곱을 계산한 후, 스택에 결과를 푸시합니다.
DIV: 스택의 맨 위 두 개의 값을 제거하고 나눗셈을 계산한 후, 스택에 결과를 푸시합니다. 
(0으로 나누는 경우에 대한 오류 처리를 고려하세요.)
MOD: 스택의 맨 위 두 개의 값을 제거하고 나머지를 계산한 후, 스택에 결과를 푸시합니다.
DUP: 스택의 맨 위 값을 복사하여 스택에 푸시합니다.
SWAP: 스택의 맨 위 두 개의 값을 교환합니다.
JMP <label>: 주어진 레이블로 프로그램 실행을 이동합니다. 예: JMP LOOP
JZ <label>: 스택의 맨 위 값이 0이면 주어진 레이블로 프로그램 실행을 이동합니다.
JNZ <label>: 스택의 맨 위 값이 0이 아니면 주어진 레이블로 프로그램 실행을 이동합니다.
CMP: 스택의 맨 위 두 개의 값을 비교하고 결과를 스택에 푸시합니다. 
(-1: 첫 번째 값이 작음, 0: 두 값이 같음, 1: 첫 번째 값이 큼)
CALL <label>: 현재 위치를 저장하고 주어진 레이블로 프로그램 실행을 이동합니다.
RET: 이전에 저장된 위치로 돌아가 프로그램 실행을 계속합니다.
NOP: 아무 작업도 수행하지 않습니다. 이 명령은 프로그램에서 무시됩니다.
HALT: 프로그램 실행을 중지합니다.
이 명령어들은 가장 기본적인 스택 기반 VM을 위한 것이며, 
필요에 따라 추가 명령어를 구현하여 더 복잡한 기능을 수행할 수 있습니다.

 

2. 명령어를 실행 할 수 있는 VM 만들기

위 명령어로 1부터 10까지 곱하는 어셈블리 프로그램을 짜줘

1부터 10까지 곱하는 스택 기반 가상 머신의 어셈블리 프로그램을 작성해 보겠습니다. 
이 프로그램은 10! (10 팩토리얼)을 계산하는 것과 동일합니다. 결과는 스택의 맨 위에 있게 됩니다.

vbnet
Copy code
; 팩토리얼 계산 시작
  PUSH 1         ; 스택에 초기 결과 (1)을 푸시
  PUSH 1         ; 스택에 초기 반복 횟수 (1)을 푸시

; 루프 시작
LOOP:
  DUP           ; 반복 횟수를 복사
  PUSH 10       ; 10을 스택에 푸시
  CMP           ; 반복 횟수와 10을 비교
  JG END_LOOP   ; 반복 횟수가 10보다 크면 루프 종료
  DUP           ; 반복 횟수를 복사
  MUL           ; 현재 결과와 반복 횟수를 곱하기
  PUSH 1        ; 1을 스택에 푸시
  ADD           ; 반복 횟수 증가
  JMP LOOP      ; 루프로 돌아가기

; 루프 종료
END_LOOP:
  NOP           ; 여기서 프로그램이 끝납니다. 필요한 경우 HALT 명령어를 사용할 수 있습니다.
이 프로그램을 실행하면 스택의 맨 위에 10! (3,628,800)이라는 결과가 남게 됩니다.

이렇게 버그가 포함된 코드를 짜주셨다. 이걸 실행 하면, 26이 나온다.

왜냐면 아래 코드가 잘못되었기 때문이다. 

  DUP           ; 반복 횟수를 복사
  MUL           ; 현재 결과와 반복 횟수를 곱하기

야간의 삽질 끝에 고친 실제 동작하는 코드는 아래와 같다. (ChatGPT 의 코드를 디버깅 하고 있었다;;;)

; 팩토리얼 계산 시작
  PUSH 1         ; 스택에 초기 결과 (1)을 푸시
  PUSH 1         ; 스택에 초기 반복 횟수 (1)을 푸시

; 루프 시작
LOOP:
  DUP           ; 반복 횟수를 복사
  PUSH 10       ; 10을 스택에 푸시
  CMP           ; 반복 횟수와 10을 비교
  JG END_LOOP   ; 반복 횟수가 10보다 크면 루프 종료
  MOV AX        ; 반복 횟수를 AX 레지스터에 복사
  MUL           ; 현재 결과와 반복 횟수를 곱하기
  PUSH AX       ; AX 레지스터의 값을 스택에 푸시
  PUSH 1        ; 1을 스택에 푸시
  ADD           ; 반복 횟수 증가
  JMP LOOP      ; 루프로 돌아가기

; 루프 종료
END_LOOP:
  NOP           ; 여기서 프로그램이 끝납니다. 필요한 경우 HALT 명령어를 사용할 수 있습니다.
이 프로그램을 실행하면 스택의 맨 위에 10! (3,628,800)이라는 결과가 남게 됩니다.

 

3. 위  코드를 실행 할 수 있는 VM 코드 생성

Q: 위 스택머신을 이용해서 파이썬으로 gwbasic 인터프리터 만들어줘

A:
GW-BASIC 인터프리터를 파이썬으로 만드는 것은 복잡한 작업입니다. 
그러나 여기 간단한 스택 기반 가상 머신 인터프리터를 제공합니다. 
이 인터프리터는 위에서 제공한 어셈블리 명령어를 사용할 수 있습니다.

python
Copy code
class StackMachine:
    def __init__(self):
        self.stack = []
        self.program = []
        self.labels = {}
        self.ip = 0

    def load_program(self, program):
        self.program = program
        for i, line in enumerate(program):
            if line.endswith(':'):
                self.labels[line[:-1]] = i

    def run(self):
        while self.ip < len(self.program):
            cmd = self.program[self.ip].split()
            op = cmd[0].upper()

            if op == 'PUSH':
                self.stack.append(int(cmd[1]))
            elif op == 'POP':
                self.stack.pop()
            elif op == 'ADD':
                self.stack.append(self.stack.pop() + self.stack.pop())
            elif op == 'SUB':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a - b)
            elif op == 'MUL':
                self.stack.append(self.stack.pop() * self.stack.pop())
            elif op == 'DIV':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a // b)
            elif op == 'MOD':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append(a % b)
            elif op == 'DUP':
                self.stack.append(self.stack[-1])
            elif op == 'SWAP':
                self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
            elif op == 'JMP':
                self.ip = self.labels[cmd[1]]
                continue
            elif op == 'JZ':
                if self.stack.pop() == 0:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'JNZ':
                if self.stack.pop() != 0:
                    self.ip = self.labels[cmd[1]]
                    continue
            elif op == 'CMP':
                b, a = self.stack.pop(), self.stack.pop()
                self.stack.append((a > b) - (a < b))
            elif op == 'NOP':
                pass
            elif op == 'HALT':
                break

            self.ip += 1

# 예제 프로그램
program = [
    'PUSH 1',
    'PUSH 1',
    'LOOP:',
    'DUP',
    'PUSH 10',
    'CMP',
    'JG END_LOOP',
    'DUP',
    'MUL',
    'PUSH 1',
    'ADD',
    'JMP LOOP',
    'END_LOOP:',
    'NOP',
]

# 스택 머신 인스턴스 생성
stack_machine = StackMachine()

# 프로그램 로드 및 실행
stack_machine.load_program(program)
stack_machine.run()

# 결과 출력
print("계산 결과:", stack_machine.stack[-1])
이 코드는 간단한 스택 기반 가상 머신 인터프리터를 구현합니다. 
이것은 GW-BASIC 인터프리터가 아니지만, 이 인터프리터를 확장하여 
GW-BASIC 인터프리터를 구현하는 데 사용할 수 있습니다. 
GW-BASIC 인터프리터를 구현하려면 다음 단계를 수행하세요:

GW-BASIC 문법 정의: GW-BASIC의 문법을 정의하고 토큰화합니다. 
키워드, 상수, 변수, 연산자, 함수 등을 인식하도록 토크나이저를 작성해야 합니다.

파서 작성: 토크나이저에서 생성된 토큰을 사용하여 문법 트리를 작성하는 파서를 구현합니다. 
이 파서는 GW-BASIC 프로그램을 읽고 실행 가능한 중간 표현으로 변환합니다.

실행 엔진 확장: 현재 스택 기반 가상 머신을 확장하여 GW-BASIC의 
기능에 맞게 사용자 정의 변수, 프로시저, 함수, 조건문, 반복문 등을 처리할 수 있도록 해야 합니다.

입출력 및 에러 처리: GW-BASIC 인터프리터는 사용자 입력을 처리하고 출력을 표시해야 합니다. 
또한 실행 중 발생할 수 있는 에러를 적절하게 처리해야 합니다.

테스트: 다양한 GW-BASIC 프로그램을 사용하여 인터프리터를 테스트하고 버그를 수정합니다.

위 단계를 따르면 파이썬으로 GW-BASIC 인터프리터를 만드는 데 도움이 됩니다. 
그러나 이 작업은 상당한 시간과 노력이 필요합니다. 
이미 만들어진 BASIC 인터프리터가 여러 개 있으므로, 
이러한 프로젝트 중 하나를 참조하거나 사용하는 것도 좋은 방법입니다. 
이러한 프로젝트 중 하나는 pcbasic이며, 
다음 링크에서 확인할 수 있습니다: https://github.com/robhagemans/pcbasic

일단 위와 같이 대충 돌아가는 것 처럼 보이는 코드를 작성해 주신다. (물론 안돌아 간다)

그리고 ChatGPT 님 께서도, 오픈소스 가져다 쓰라고 추천 하신다.

2부에서 계속...

 

Posted by chobocho
Coding/Python 삽질기2023. 3. 14. 22:23

요즘 핫 한 ChatGPT를 유료 결제 하고, 이것 저것 질문을 해보았습니다.

Q:  python api 작명법

A: Python API 작명법은 일반적으로 PEP 8 스타일 가이드에 따라 작성됩니다. PEP 8은 파이썬의 공식 스타일 가이드로, 코드를 읽기 쉽고 이해하기 쉽게 만들어줍니다. API 작성에도 적용할 수 있는 일반적인 규칙은 다음과 같습니다.

  1. 함수와 메서드의 이름은 소문자와 밑줄을 사용하여 작성합니다.
  2. 변수와 함수 매개변수의 이름도 소문자와 밑줄을 사용하여 작성합니다.
  3. 클래스의 이름은 대문자로 시작하는 카멜 케이스(CamelCase)로 작성합니다.
  4. 모듈의 이름도 소문자와 밑줄을 사용하여 작성합니다.

예를 들어, 다음은 PEP 8에 따른 예시입니다.

# 함수와 메서드의 이름
def calculate_average(numbers_list):
    ...

class MyClass:
    def __init__(self):
        ...

# 변수와 함수 매개변수의 이름
my_variable = 42

def print_message(message):
    ...

# 모듈의 이름
import my_module

API 작성에는 더욱 구체적인 규칙이 존재할 수 있습니다. 따라서 해당 API의 문서를 참조하여 작명법을 따르는 것이 좋습니다.

  1. 상수의 이름은 모두 대문자와 밑줄을 사용하여 작성합니다.
  2. 함수나 메서드의 이름은 가능한 명사 또는 동사로 작성합니다. 함수나 메서드의 이름에서 명사와 동사는 함께 사용하여 기능을 명확하게 설명해야 합니다.
  3. 클래스의 이름은 명사로 작성하며, 클래스 이름이 여러 단어로 이루어진 경우 각 단어의 첫 글자를 대문자로 작성합니다. 이 스타일을 카멜 케이스(CamelCase)라고 합니다.
  4. 모듈의 이름은 짧고 명확해야 하며, 가능한 단일 단어로 작성하는 것이 좋습니다. 모듈의 이름도 카멜 케이스를 사용할 수 있지만, 보통은 소문자와 밑줄을 사용하여 작성합니다.

예를 들어, 다음은 PEP 8에 따른 예시입니다.

# 상수의 이름
MAXIMUM_NUMBER = 100

# 함수나 메서드의 이름
def calculate_average(numbers_list):
    ...

class MyClass:
    def __init__(self):
        ...

    def perform_action(self):
        ...

# 클래스의 이름
class MyClassName:
    ...

# 모듈의 이름
import my_module

API 작성 시에는 일관성이 매우 중요합니다. 같은 기능을 수행하는 함수나 메서드의 이름이 다른 경우, 사용자가 API를 이해하는 데 어려움을 겪을 수 있습니다. 따라서 API 작성 시에는 팀의 스타일 가이드를 따르거나, 기존 코드와 일관성 있게 작성하는 것이 좋습니다.

  1. 함수나 메서드의 이름에는 가능한한 매개변수의 자료형을 포함하지 않습니다. 함수나 메서드의 매개변수는 일반적으로 자료형에 대한 힌트(hint)를 포함할 수 있지만, 함수나 메서드의 이름에는 자료형에 대한 정보가 포함되어 있지 않아야 합니다.
  2. 함수나 메서드의 이름은 가능한한 짧게 작성하지 않습니다. 함수나 메서드의 이름은 가능한한 짧게 작성하는 것이 좋습니다. 그러나, 이름이 너무 짧으면 기능을 명확하게 설명하지 못할 수 있습니다. 따라서 이름은 가능한한 기능을 명확하게 설명할 수 있도록 작성하는 것이 좋습니다.

예를 들어, 다음은 PEP 8에 따른 예시입니다.

# 함수나 메서드의 이름에 자료형을 포함하지 않기
def calculate_average(numbers):
    ...

def add_numbers(x: int, y: int) -> int:
    ...

# 함수나 메서드의 이름이 너무 짧지 않게 작성하기
def calculate_average_of_numbers_in_list(numbers_list):
    ...

def add_two_numbers_and_return_result(first_number, second_number):
    ...

API 작성 시에는 사용자가 쉽게 이해할 수 있도록 기능을 명확하게 설명하는 것이 중요합니다. 또한, 코드의 가독성을 높이기 위해 이름을 가능한한 명확하게 작성하는 것이 좋습니다.

Posted by chobocho
Coding/Python 삽질기2023. 3. 9. 01:03

문명과 수학을 읽는 중 문득 Cycloid 곡선을 그리는 코드가 궁금해서, ChatGPT에게 물어보았다

Cycloid 곡선

 

import numpy as np
import matplotlib.pyplot as plt

# 파라미터 정의
r = 1  # 원의 반지름
a = 1  # 원주와 돌기의 간격

# 각도 범위 설정
t = np.linspace(0, 4*np.pi, 1000)

# 싸이클로이드의 수식
x = r * (t - np.sin(t))
y = r * (1 - np.cos(t)) - a

# 그래프 그리기
fig, ax = plt.subplots(figsize=(8, 8))

ax.plot(x, y, color='blue', linewidth=2)
ax.set_aspect('equal', adjustable='box')
ax.set_title('Cycloid')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.grid(True)

plt.show()

 

위 코드를 실행하면 아래 그림 같이 이쁘게 코드를 그려준다.

 

'Coding > Python 삽질기' 카테고리의 다른 글

[ChatGPT에게 묻다] python api 작명법  (0) 2023.03.14
[Python] Python 소소한 기능들  (0) 2023.02.04
[Design Pattern] Singleton pattern  (0) 2022.07.12
Posted by chobocho
Coding/Python 삽질기2023. 2. 4. 01:33

1.  f-string 예제

def f_string_test():
    rate = int(100_000_000 * 0.05 / 12)
    print(f'[{rate}]')
    # 3자리 마다 , 표시
    print(f'[{rate:,}]')
    # < : 좌측 정렬
    print(f'[{rate:<11}]')
    # < : 좌측 정렬, 빈칸은 _로 채우기
    print(f'[{rate:_<11,}]')
    # ^ : 가운데 정렬, 빈칸은 _로 채우기, 3자리 마다 , 표시
    print(f'[{rate:_^11,}]')
    # > : 우측 정렬, 빈칸은 _로 채우기, 3자리 마다 , 표시
    print(f'[{rate:_>11,}]')


if __name__ == '__main__':
    f_string_test()

2. ... 

def f_string_test():
    ...


if __name__ == '__main__':
    f_string_test()

'Coding > Python 삽질기' 카테고리의 다른 글

[Python] Cycloid 곡선 그리기  (0) 2023.03.09
[Design Pattern] Singleton pattern  (0) 2022.07.12
About Pyscript  (0) 2022.06.10
Posted by chobocho