Edited at

brainfuckで関数スタイルとクラススタイルの比較

@silver801 さんが投稿された「Pythonでbrainfuckインタプリタ(楔形文字も出る)」を拝見しました。


Pythonにはswitch-caseみたいなやつがないのでif-else祭りになりました。if-else節が長くなるときは一般にはdictを使えと言われてるけど、こういうとき(パターンマッチの後にいろいろ処理を書きたいとき)はどうするのがいいんだろうか?


とのことなので、処理を関数化してdictにしてみました。


関数スタイル

import sys

class Brain:
pass

def inc(brain):
brain.mem[brain.ptr] += 1

def dec(brain):
brain.mem[brain.ptr] -= 1

def fore(brain):
brain.ptr += 1
if brain.ptr > len(brain.mem):
print("overflow!")
sys.exit(1)

def back(brain):
if brain.ptr == 0:
print("Can't decrement anymore")
brain.ptr -= 1

def putc(brain):
# chr: char -> code point
print(chr(brain.mem[brain.ptr]), end="") # no line break

def getc(brain):
# ord: code point -> char
brain.mem[brain.ptr] = ord(sys.stdin.buffer.read(1))

def begin(brain):
if brain.mem[brain.ptr] == 0:
nest = 1
while nest != 0:
brain.head += 1
if brain.head == len(brain.code):
print("']' is missing")
sys.exit(1)
if brain.code[brain.head] == '[':
nest += 1
elif brain.code[brain.head] == ']':
nest -= 1

def end(brain):
if brain.mem[brain.ptr] != 0:
nest = 1
while nest != 0:
brain.head -= 1
if brain.head < 0:
print("'[' is missing")
if brain.code[brain.head] == ']':
nest += 1
elif brain.code[brain.head] == '[':
nest -= 1

def nop(brain):
pass # ignore other symbol

def brainfuck(code, mem_size=30000):
operations = {
"+": inc, # increment the value at the pointer.
"-": dec, # decrement the value at the pointer.
">": fore, # increment the pointer.
"<": back, # decrement the pointer.
".": putc, # output the value at the pointer as utf-8 character.
",": getc, # accept one byte of input, storing its value in the mem at the pointer.
"[": begin, # if the byte at the pointer is zero, then jump it to the matching ']'
"]": end, # if the byte at the pointer is nonzero, then jump it buck to the matching '['
}
brain = Brain()
brain.code = code
brain.mem = [0] * mem_size
brain.ptr = 0
brain.head = 0 # (tape reading) head
while brain.head < len(code):
operations.get(code[brain.head], nop)(brain)
brain.head += 1

def main():
args = sys.argv
if len(args) < 2:
code = sys.stdin.read()
else:
path = args[1]
with open(path) as f:
code = f.read()
brainfuck(code)

if __name__ == "__main__":
main()

データと関数がバラバラに定義されています。

データと関数をまとめてクラスにしてみます。


クラススタイル

import sys

class Brain:

def inc(self):
self.mem[self.ptr] += 1

def dec(self):
self.mem[self.ptr] -= 1

def fore(self):
self.ptr += 1
if self.ptr > len(self.mem):
print("overflow!")
sys.exit(1)

def back(self):
if self.ptr == 0:
print("Can't decrement anymore")
self.ptr -= 1

def putc(self):
# chr: char -> code point
print(chr(self.mem[self.ptr]), end="") # no line break

def getc(self):
# ord: code point -> char
self.mem[self.ptr] = ord(sys.stdin.buffer.read(1))

def begin(self):
if self.mem[self.ptr] == 0:
nest = 1
while nest != 0:
self.head += 1
if self.head == len(self.code):
print("']' is missing")
sys.exit(1)
if self.code[self.head] == '[':
nest += 1
elif self.code[self.head] == ']':
nest -= 1

def end(self):
if self.mem[self.ptr] != 0:
nest = 1
while nest != 0:
self.head -= 1
if self.head < 0:
print("'[' is missing")
if self.code[self.head] == ']':
nest += 1
elif self.code[self.head] == '[':
nest -= 1

def nop(self):
pass # ignore other symbol

def fuck(self, code, mem_size=30000):
operations = {
"+": self.inc, # increment the value at the pointer.
"-": self.dec, # decrement the value at the pointer.
">": self.fore, # increment the pointer.
"<": self.back, # decrement the pointer.
"[": self.begin, # if the byte at the pointer is zero, then jump it to the matching ']'
"]": self.end, # if the byte at the pointer is nonzero, then jump it buck to the matching '['
".": self.putc, # output the value at the pointer as utf-8 character.
",": self.getc, # accept one byte of input, storing its value in the mem at the pointer.
}
self.code = code
self.mem = [0] * mem_size
self.ptr = 0
self.head = 0
while self.head < len(code):
operations.get(code[self.head], self.nop)()
self.head += 1

def main():
args = sys.argv
if len(args) < 2:
code = sys.stdin.read()
else:
path = args[1]
with open(path) as f:
code = f.read()
Brain().fuck(code)

if __name__ == "__main__":
main()


比較

横並びにして比較してみましょう。

import sys                                                  import sys

class Brain: class Brain:
pass

def inc(brain): def inc(self):
brain.mem[brain.ptr] += 1 self.mem[self.ptr] += 1

def dec(brain): def dec(self):
brain.mem[brain.ptr] -= 1 self.mem[self.ptr] -= 1

def fore(brain): def fore(self):
brain.ptr += 1 self.ptr += 1
if brain.ptr > len(brain.mem): if self.ptr > len(self.mem):
print("overflow!") print("overflow!")
sys.exit(1) sys.exit(1)

def back(brain): def back(self):
if brain.ptr == 0: if self.ptr == 0:
print("Can't decrement anymore") print("Can't decrement anymore")
brain.ptr -= 1 self.ptr -= 1

def putc(brain): def putc(self):
# chr: char -> code point # chr: char -> code point
print(chr(brain.mem[brain.ptr]), end="") print(chr(self.mem[self.ptr]), end="")

def getc(brain): def getc(self):
# ord: code point -> char # ord: code point -> char
brain.mem[brain.ptr] = ord(sys.stdin.buffer.read(1)) self.mem[self.ptr] = ord(sys.stdin.buffer.read(1))

def begin(brain): def begin(self):
if brain.mem[brain.ptr] == 0: if self.mem[self.ptr] == 0:
nest = 1 nest = 1
while nest != 0: while nest != 0:
brain.head += 1 self.head += 1
if brain.head == len(brain.code): if self.head == len(self.code):
print("']' is missing") print("']' is missing")
sys.exit(1) sys.exit(1)
if brain.code[brain.head] == '[': if self.code[self.head] == '[':
nest += 1 nest += 1
elif brain.code[brain.head] == ']': elif self.code[self.head] == ']':
nest -= 1 nest -= 1

def end(brain): def end(self):
if brain.mem[brain.ptr] != 0: if self.mem[self.ptr] != 0:
nest = 1 nest = 1
while nest != 0: while nest != 0:
brain.head -= 1 self.head -= 1
if brain.head < 0: if self.head < 0:
print("'[' is missing") print("'[' is missing")
if brain.code[brain.head] == ']': if self.code[self.head] == ']':
nest += 1 nest += 1
elif brain.code[brain.head] == '[': elif self.code[self.head] == '[':
nest -= 1 nest -= 1

def nop(brain): def nop(self):
pass pass

def brainfuck(code, mem_size=30000): def fuck(self, code, mem_size=30000):
operations = { operations = {
"+": inc, "+": self.inc,
"-": dec, "-": self.dec,
">": fore, ">": self.fore,
"<": back, "<": self.back,
".": putc, "[": self.begin,
",": getc, "]": self.end,
"[": begin, ".": self.putc,
"]": end, ",": self.getc,
} }
brain = Brain()
brain.code = code self.code = code
brain.mem = [0] * mem_size self.mem = [0] * mem_size
brain.ptr = 0 self.ptr = 0
brain.head = 0 self.head = 0
while brain.head < len(code): while self.head < len(code):
operations.get(code[brain.head], nop)(brain) operations.get(code[self.head], self.nop)()
brain.head += 1 self.head += 1

def main(): def main():
args = sys.argv args = sys.argv
if len(args) < 2: if len(args) < 2:
code = sys.stdin.read() code = sys.stdin.read()
else: else:
path = args[1] path = args[1]
with open(path) as f: with open(path) as f:
code = f.read() code = f.read()
brainfuck(code) Brain().fuck(code)

if __name__ == "__main__": if __name__ == "__main__":
main() main()

Brainの中のbrainは自分自身なのでselfという名称にするのが一般的ですが、

brain のままにすれば、ほとんど一緒ですね。