quine는 프로그램의 소스 코드의 동일한 출력을 생성하는 프로그램이다. 이 웹 사이트에서 우리는 일반적으로 적절한 quines 에 대해서만 관심을 갖습니다 (작성 당시 현재 정의는 "출력의 일부는 프로그램의 다른 부분에 의해 인코딩됩니다").
적절한 quine을 작성하거나 quine와 유사한 속성을 가진 프로그램에 대해 어떤 조언이 있습니까? 평소와 같이 각 팁은 다른 답변으로 작성해야합니다.
quine는 프로그램의 소스 코드의 동일한 출력을 생성하는 프로그램이다. 이 웹 사이트에서 우리는 일반적으로 적절한 quines 에 대해서만 관심을 갖습니다 (작성 당시 현재 정의는 "출력의 일부는 프로그램의 다른 부분에 의해 인코딩됩니다").
적절한 quine을 작성하거나 quine와 유사한 속성을 가진 프로그램에 대해 어떤 조언이 있습니까? 평소와 같이 각 팁은 다른 답변으로 작성해야합니다.
답변:
eval
코드 복사 필요성을 줄이기 위해 사용대부분의 퀴네에는 두 개의 코드 사본이 필요합니다. 하나는 데이터로 실행됩니다. 이로 인해 소스 코드 길이가 두 배로 늘어나고 유지 관리가 어려워지고 코드 골프 경쟁을 위해 퀴네를 작성하면 점수가 떨어질 수 있습니다.
두 사본을 병합한다는 것은 하나의 정보가 두 가지 목적으로 사용되어야 함을 의미합니다. 코드를 데이터로 취급하려고 시도하는 것은 종종 불가능하며 일반적으로 부정 행위로 간주됩니다. 그러나 데이터를 코드로 취급하는 것은 일반적으로이라는 내장을 사용하여 여러 언어로 수행 할 수 있습니다 eval
. 따라서 quine은 기본적으로 quine의 본체를 변수에 저장 한 후 (두 번 이상 참조 할 수 있도록) 변수를 평가하는 것으로 구성됩니다.
다음은 이것이 작동하는 방법에 대한 예입니다 (이 예제는 Python으로 작성되었지만 다른 많은 언어에서도 작동합니다).
d='print("d="+repr(d)+"\\neval(d)")'
eval(d)
quine을 만드는 가장 쉬운 방법 중 하나는 문자열을 정의한 다음 문자열 형식으로 문자열을 그 안에 넣는 것입니다.
s='s=%r;print s%%s';print s%s
이 예제에서 파이썬 quine, 우리는 첫 번째 부분이 문자열 앞에있는 것과 같은 문자열을 선언 한 s=
다음 문자열을 형식화하여 삽입 %r
하고 마지막으로 문자열 뒤에 오는 것을 넣어서 서식을 지정 합니다. . 후행 줄 바꿈은 후행 줄 바꿈을 print
인쇄 하기 때문 입니다.
따라서 템플릿은 파이썬에서 실제로 이것입니다.
<A>'<A>%r<B>'<B>
더 많은 코드로 기존 quine을 확장하려면 :
<more A>s='<more A>s=%r;print s%%s<more B>';print s%s<more B>
여러 언어에서 함수 객체 (또는 동등한 구문)는 소스 코드를 암시 적으로 저장하고 문자열로 변환 할 때 반환합니다. 이것은 문자열 eval 을 사용 하지 않고 간단한 quine을 허용 합니다 . 이러한 언어의 주목할만한 예는 JavaScript입니다.
function f(){console.log(f+"f()")}f()
이 코드는 호출 f
될 때 자체 소스 코드를 인쇄 한 후 자신을 호출하는 함수 를 정의하고 호출 합니다. 프로그램에서 복제해야하는 유일한 부분은 함수 호출 f()
입니다. 물론 함수 본문에는 함수가 호출 될 때 실행될 코드의 임의 "페이로드"가 포함될 수 있습니다.
동일한 트릭의보다 컴팩트 한 버전은 골프 언어 GolfScript 에서 작동합니다 .
{".~"}.~
및 CJam :
{"_~"}_~
이러한 각 quine은 먼저 JavaScript의 함수 객체와 유사하게 동작하는 익명 코드 블록 (중괄호로 묶음)을 정의합니다.이 코드는 실행될 수 있으며, 문자열 화 된 경우 소스 코드를 반환합니다. 그런 다음 나머지 코드 ( .~
GolfScript 또는 _~
CJam)는 블록의 복사본을 스택에 남기면서 블록을 실행합니다. 블록 내부의 코드는 스택 외부로 코드를 반복하는 문자열을 문자열로 푸시합니다. 인터프리터가 종료되면 자동으로 문자열을 묶고 스택에 남아있는 모든 것을 인쇄합니다. JavaScript 예제와 마찬가지로 이러한 코드 블록은 코드를 복제하지 않고도 추가 코드의 임의 페이로드를 전달하고 실행할 수도 있습니다.
종종, 키네를 쓰는 데있어 가장 어려운 부분 중 하나는 탈출 단계입니다. 이것은 거의 모든 quine에서 필요합니다. 문제는 어떻게 든 데이터를 저장하고 있으며 데이터를 quine의 출력에 저장하는 코드를 복제해야한다는 것입니다. 이 코드에는 이스케이프 된 형식의 데이터가 포함되므로 프로그램에서 이스케이프되지 않은 형식이 표시되므로 다시 이스케이프해야합니다.
이스케이프 해제 단계를 처리하는 가장 쉬운 방법은 이스케이프되고 이스케이프되지 않은 데이터 형식이 문자열 구분 기호가 있거나없는 경우에만 다릅니다. 따라서 이스케이프는 문자열 주위에 새로운 문자열 구분 기호 쌍을 추가하는 간단한 문제입니다. 불행하게도, 문자열 구분 기호 자체가 이스케이프되지 않고 데이터로 표현 될 수있는 경우에만이 기능이 명확하게 작동 할 수 있습니다.
Perl은이 트릭이 작동하는 언어의 좋은 예입니다. 일반적인 문자열 구분 기호는 있지만"…"
or '…'
이지만 덜 일반적으로 사용되는 q(…)
중첩 으로 다음과 같은 종류의 키네를 작성할 수 있습니다.
$_=q($_=q(0);s/\d/$_/;print);s/\d/$_/;print
이것은 코드 + 데이터 퀴입니다. s///
정규식 문자열 교체 작업입니다. 우리가 사용하는 0
마커로, 그리고 정규식 내에서 일치 \d
( "임의의 숫자"), 더 다른 최적화로, 우리가 실제로 바로 사용할 수도 있지만 (두 번 이상 마커를 사용 방지하기 위해 0
펄이 있기 때문에, 다시 s///
단지에서 첫 번째 항목을 대체 태만). 여기에는 명시적인 이스케이프 단계가 필요하지 않습니다.q(…)
구분자가 데이터 문자열에 그대로 포함될 수 있으므로 .
quine의 가장 일반적인 구조는 다음 의사 코드와 같습니다.
data = " 전체 프로그램의 이스케이프 된 버전, 이 문자열을 마커로 대체 "" 프로그램 = data.replace ( 마커로 평가되지만 언급하지 않은 표현식 , 탈출 (데이터) 인쇄 프로그램;
이 구조는 대부분의 언어에서 (공정하게 순진한) 퀴니를 작성하는 데 사용될 수 있습니다. 그러나 프로그램 전체를 두 번 작성해야하기 때문에 대부분의 점수 시스템에서 점수가 상당히 나빠지는 경향이 있습니다. 그러나 대부분의 키네 구조는이 구조의 최적화로 간주 될 수 있습니다.
이것에는 미묘한 점이 있습니다. 일부 언어에서이 작업을 수행하는 데있어 가장 어려운 부분은 이스케이프 코드를 작성하는 것입니다. 많은 언어에서 이름을 언급하지 않고 마커를 생성하는 것은 어렵습니다. 일부 난해한 언어에서는 고유 한 문자열 리터럴을 발명해야합니다. 그러나 세 가지 작업 모두 너무 많은 문제를 일으키지 않는 경향이 있습니다.
예를 들어,을 사용하여 문자열을 이스케이프 처리 repr
하고 2 자로 된 x"
문자열 ( 문자열 자체의 문자열 표현에서 "x\""
시퀀스 x"
를 사용하지 않는 것으로 표시됨)을 마커로 사용하여 Python quine을 작성할 수 있습니다 .
d='d=x"\nprint(str.replace(d,"x\\"",repr(d)))'
print(str.replace(d,"x\"",repr(d)))
아주 소수의 언어 (주로 2D 언어)에서 소스 코드가 줄 바꿈 될 수 있습니다. 특정 상황 (예 : Befunge-98, 프로그램이 한 줄짜리 인 경우)에서 프로그램 끝을 지나면 프로그램 시작으로 돌아갑니다. 이런 종류의 비선형 동작은 문자열 리터럴 내부와 외부에있는 코드를 동시에 작성할 수 있다는 것을 의미합니다. 일치하지 않는 "
(또는 문자열 구분 기호가 무엇이든) 프로그램의 나머지 부분을 포함하는 문자열을 효과적으로 제공합니다 ( "
자체 제외 ).
이 트릭을 사용할 때의 한 가지 문제 "
는 프로그램 시작이 아닌 ( 의 관점에서) 문자열을 얻을 수 있다는 것입니다. 따라서 "
시작 또는 끝에 표시 되도록 프로그램을 다시 정렬하는 것이 가장 쉽습니다 . 이것은 종종 프로그램을 여러 조각으로 자르고 언어에있는 흥미롭고 특이한 흐름 제어 명령을 사용한다는 것을 의미합니다 (대부분의 언어는 문자열 리터럴을 프로그램을 감싸는 데 도움이됩니다).
좋은 예는 @Justin의 Befunge-98 quine입니다 .
<@,+1!',k9"
타의 추종을 불허하는 "
프로그램의 끝에 그렇게 (때문에 왼쪽에서 오른쪽에 실행, 문자열 리터럴에 프로그램 전체를 감싸는 <
출력 프로그램 (우리가해야 할 모든 것입니다 시작시) 9k
(,)를 큰 따옴표를 한 후 출력 '!1+,
) 및 종료 ( @
). 이렇게하면 두 개의 프로그램 사본 (하나는 코드, 하나는 데이터)이 필요하지 않습니다. 두 사본은 동일한 코드 조각이며 방금 다른 방식으로 해석됩니다.
나는 quines를 2가 아닌 세 부분 으로 생각하고 싶습니다 .
이것은 quine에 대해 더 쉽게 생각할 수있게합니다. 다음은 각 줄이 한 부분에 해당하는 Python quine입니다.
s = "print(\"s = \" + repr(s))\nprint(s)"
print("s = " + repr(s))
print(s)
때로는 eval
중복을 제거 하기 위해 또는 이와 유사한 것을 사용 하지만 일반적으로 간단한 quine을 작성하는 데 도움이된다는 것을 알았습니다.
서로 다른 두 가지 언더로드 퀴를 살펴 보겠습니다. 이것이 첫 번째입니다 :
(:aSS):aSS
첫 번째 부분은 (:aSS)
데이터 표현을 생성하는입니다. 두 번째는 :aS
인쇄하는 것 (:aSS)
입니다. 세 번째 부분은 S
인쇄 :aSS
됩니다.
두 번째 quine은 다음과 같습니다.
(:aS(:^)S):^
처음에는 이것이 맞지 않는 것 같습니다. 그러나 quine을 확장하면 다음과 같은 결과가 나타납니다.
(:aS(:^)S):aS(:^)S
이제 (:aS(:^)S)
1 :aS
부, 2 (:^)S
부, 3 부입니다.