소개
인터넷을 돌아다니다가 뜬금없이 Quine이라는 것을 발견했습니다. 콰인이란 어떤 프로그램 소스 코드로, 실행시켰을 때 그 자신을 출력하는 것을 말합니다. 가장 자명한 콰인은 아무것도 없는 소스코드입니다. 아무것도 없는 소스코드는 아무것도 출력하지 않으므로, 자기 자신을 출력한다고 볼 수 있습니다. 당연하지만, eval
, exec
류의 string
실행 함수, reflection
, 그냥 파일 읽기, 외부 라이브러리 등을 사용하면 안 됩니다.
소스코드
저는 아래와 같은 python 코드로 콰인을 작성했습니다.
def decode(s):
s = s.replace("\\", "\\\\")
s = s.replace('"', '\\"')
s = s.replace("'", "\\'")
s = s.replace('''
''', '\\n')
return s
def self_print(x):
print(f'''{x}
self_print(
"{decode(x)}")''')
self_print(
"def decode(s):\n s = s.replace(\"\\\\\", \"\\\\\\\\\")\n s = s.replace(\'\"\', \'\\\\\"\')\n s = s.replace(\"\'\", \"\\\\\'\")\n s = s.replace(\'\'\'\n\'\'\', \'\\\\n\')\n return s\n\n\ndef self_print(x):\n print(f\'\'\'{x}\n\nself_print(\n \"{decode(x)}\")\'\'\')")
위 코드를 실행하면 자기 자신과 완벽히 똑같은 출력을 내보냅니다.
논리적 도출
저는 콰인을 구현하는 방법에 대해 한 번도 들어본 적이 없기 때문에 순수하게 바닥에서부터 출발했고, 따라서 다른 코드들과 방향성이 좀 다를 수 있습니다.
먼저 위 함수는 다음과 같은 아이디어에서 출발했습니다.
만약 언어 자체에 self_print(x) 라는 함수가 있어서, 이 함수의 출력이
self_print("x")
라면 어떨까?
만약 그렇다면 다음과 같은 코드는 콰인이 됩니다.
self_print("아무 문자열")
그러나 당연하게도 파이썬에는 저런 함수가 없습니다. 그러므로 self_print
함수를 직접 정의할 필요가 있습니다.
def self_print(x):
# ~~~
sef_print("아무 문자열")
그런데 여기서 문제가 발생합니다. self_print
함수의 정의 자체는 출력하지 않기 때문에, 이렇게 되면 콰인이 아니게 됩니다. 이것을 콰인으로 만드려면 출력에 self_print
함수에 대한 정의 자체가 출력에 포함되어야 합니다. 그러기 위해서 두 가지 방법이 있습니다.
self_print
함수 내부에self_print
함수를 정의하는 문자열을 넣는다.self_print
함수 외부에self_print
함수를 정의하는 문자열을 넣는다.
이중 1번은 생각해보면 불가능함을 알 수 있습니다. 왜냐하면 self_print
함수를 정의하는 데 개의 문자가 필요하다고 하면, 이것을 따옴표로 감싸기만 해도 n+2
개의 문자가 필요합니다. 그런데 이것이 다시 self_print
함수의 정의에 포함되어야 하므로 이 되어 모순이 발생하기 때문입니다.
따라서 self_print
함수 외부에 그런 문자열이 있어야 하며, 그것을 self_print
내부로 전달해야 합니다. 그러기 위해서 자명히 self_print
함수의 파라매터를 사용할 수 있습니다.
그러면 이제 이 함수에 대한 정의를 함수 안에서 알 수 있게 됩니다. 이것을 출력에 포함시키기만 하면 완성입니다. 즉, 다음과 같이 하면 됩니다.
def self_print(x):
print(f'''{x}
self_print(
"{x}")''')
이렇게 하면 self_print
의 인자로 self_print
의 정의를 나타내는 문자열을 받았을 경우 콰인이 됩니다.
다만 이렇게 할 때 사소한 문제는 문자열 escape입니다. 큰따옴표로 선언된 문자열 리터럴 내부에 큰따옴표를 집어넣거나, 줄바꿈을 표현하려면 escape를 해야 합니다. 그런데 print
를 사용하여 출력할 때에는 escape
문자가 전부 처리된 후 출력되기 때문에, escape한 문자열을 다시 역-escape해주는 부분을 추가해야 합니다. 그 부분(위의 소스코드에서는 decode
함수)위의 소스코드와 같은 결과를 얻게 됩니다.