스택 고양이 , 62 + 4 = 66 바이트
*(>:^]*(*>{<-!<:^>[:((-<)<(<!-)>>-_)_<<]>:]<]]}*<)]*(:)*=<*)>]
-ln
명령 행 플래그 (따라서 +4 바이트) 로 실행해야합니다 . 0
복합 번호 및 1
소수를 인쇄 합니다 .
온라인으로 사용해보십시오!
나는 이것이 첫 번째 사소한 Stack Cats 프로그램이라고 생각합니다.
설명
빠른 스택 고양이 소개 :
- Stack Cats는 무한 스택 테이프에서 작동하며 테이프 헤드는 현재 스택을 가리 킵니다. 모든 스택은 처음에 무한한 양의 0으로 채워집니다. 나는 일반적으로 내 말에서 이러한 0을 무시할 것이므로 "스택의 맨 아래"라고 말하면 0이 아닌 가장 낮은 값을 의미하고 "스택이 비어 있습니다"라고 말하면 0이 있음을 의미합니다.
- 프로그램이 시작되기 전에 a
-1
가 초기 스택으로 푸시 된 다음 전체 입력이 맨 위에 푸시됩니다. 이 경우 -n
플래그 로 인해 입력을 10 진 정수로 읽습니다.
- 프로그램이 끝나면 현재 스택이 출력에 사용됩니다.
-1
하단에 a 가 있으면 무시됩니다. 다시 -n
플래그 로 인해 스택의 값은 단순히 줄 바꿈으로 구분 된 10 진수로 인쇄됩니다.
- Stack Cats는 가역적 인 프로그램 언어입니다. 모든 코드는 취소 할 수 있습니다 (Stack Cats없이 명시적인 기록을 추적하지 않음). 더 구체적으로 말하면, 코드 조각을 뒤집으려면 간단히 코드를 미러링하면됩니다 (예 :
<<(\-_)
됩니다) (_-/)>>
. 이 설계 목표는 언어에 존재하는 연산자 및 제어 흐름 구성의 종류와 전역 메모리 상태에서 계산할 수있는 함수의 종류에 대해 상당히 엄격한 제한을 둡니다.
무엇보다도 모든 Stack Cats 프로그램은 자체 대칭이어야합니다. 위의 소스 코드에는 해당되지 않습니다. 이것이 -l
플래그의 목적입니다 : 중앙의 첫 번째 문자를 사용하여 코드를 내재적으로 왼쪽으로 미러링합니다. 따라서 실제 프로그램은 다음과 같습니다
[<(*>=*(:)*[(>*{[[>[:<[>>_(_-<<(-!>)>(>-)):]<^:>!->}<*)*[^:<)*(>:^]*(*>{<-!<:^>[:((-<)<(<!-)>>-_)_<<]>:]<]]}*<)]*(:)*=<*)>]
전체 코드를 사용하여 효과적으로 프로그래밍하는 것은 매우 사소하고 직관적이지 않으며 인간이 어떻게 할 수 있는지 아직 알지 못했습니다. 우리는 더 간단한 작업을 위해 그러한 프로그램 을 강요 했지만 그 근처에 손을 댈 수 없었습니다. 다행히 프로그램의 절반을 무시할 수있는 기본 패턴을 찾았습니다. 이것은 확실히 차선책이지만 Stack Cats에서 효과적으로 프로그래밍하는 유일한 방법입니다.
따라서이 답변에서 위 패턴의 템플릿은 다음과 같습니다 (실행 방법에 약간의 차이가 있습니다).
[<(...)*(...)>]
프로그램이 시작되면 스택 테이프는 다음과 같이 나타납니다 (입력의 4
경우).
4
... -1 ...
0
^
[
이동 왼쪽으로 스택의 상단 (와 함께 테이프 헤드) - 우리는이 "밀어"호출합니다. 그리고 <
테이프 헤드 만 움직입니다. 따라서 처음 두 명령 후에 다음과 같은 상황이 발생합니다.
... 4 -1 ...
0 0 0
^
이제는 (...)
조건부로 아주 쉽게 사용할 수있는 루프입니다. 루프는 현재 스택의 맨 위가 양수일 때만 입력되고 남아 있습니다. 현재 0이므로 프로그램의 전체 절반을 건너 뜁니다. 이제 중심 명령은 *
입니다. 이것은 간단합니다 XOR 1
. 즉, 스택 상단의 최하위 비트를 토글하며,이 경우에는를로 0
변환합니다 1
.
... 1 4 -1 ...
0 0 0
^
이제 우리는 미러 이미지를 보게됩니다 (...)
. 이 시간은 스택의 상단은 긍정적이며, 우리는 할 코드를 입력하십시오. 괄호 안의 내용을 살펴보기 전에 끝에서 마무리하는 방법을 설명하겠습니다.이 블록의 끝에서 테이프 헤드가 다시 양수 값이되도록합니다. 루프는 단일 반복 후에 종료되며 간단히 선형 조건부로 사용됩니다.) 오른쪽 스택은 출력 을 보유하고 스택 오른쪽은 스택 을 보유합니다 -1
. 이 경우 루프를 떠나 >
출력 값으로 이동 한 다음 ]
푸시 하여 출력 -1
을위한 깨끗한 스택을 만듭니다.
그게 그거야. 이제 괄호 안에서 우리는 끝 부분의 이전 단락에서 설명한대로 설정을 유지하는 한 우선 순위를 확인하려는 모든 작업을 수행 할 수 있습니다 (이는 일부 밀기 및 테이프 헤드 이동으로 쉽게 수행 할 수 있음). 나는 먼저 윌슨 정리 문제를 해결하려고 시도했지만 실제로는 제곱 팩터 계산이 Stack Cats에서 상당히 비싸기 때문에 (최소한 짧은 길을 찾지 못했습니다) 100 바이트 이상이되었습니다. 그래서 대신 시험 분할을했는데 실제로 훨씬 더 단순 해졌습니다. 첫 번째 선형 비트를 보자.
>:^]
이미 두 가지 명령을 보았습니다. 또한 :
현재 스택의 상위 2 개 값을 ^
바꾸고 두 번째 값을 최상위 값으로 XOR합니다. 이렇게하면 :^
빈 스택에서 값을 복제하는 공통 패턴 이 만들어 집니다 (값 위에 0을 가져온 다음 0을로 바꿉니다 0 XOR x = x
). 이 후 테이프 섹션은 다음과 같습니다.
4
... 1 4 -1 ...
0 0 0
^
내가 구현 한 시행 분할 알고리즘은 input 1
에 대해 작동하지 않으므로이 경우 코드를 건너 뛰어야합니다. 를 사용 1
하여 쉽게 0
모든 것을 양수 값에 매핑 할 수 *
있으므로 다음 과 같이하십시오.
*(*...)
그것은 우리가 켭니다 1
으로 0
, 우리가 실제로 얻을 경우 코드의 큰 부분을 건너 뛸 수 0
있지만, 우리는 즉시 취소 내부에 *
우리가 우리의 입력 값을 얻을 수 있도록. 괄호 끝에서 양수 값으로 끝나서 반복을 시작하지 않도록 다시 확인해야합니다. 조건부 안에서 우리는 하나의 스택을 오른쪽으로 움직 인 >
다음 주요 시험 분할 루프를 시작합니다.
{<-!<:^>[:((-<)<(<!-)>>-_)_<<]>:]<]]}
중괄호 (괄호와 반대)는 다른 종류의 루프를 정의합니다. 반복 루프이므로 항상 하나 이상의 반복에 대해 실행됩니다. 다른 차이점은 종료 조건입니다. 루프에 들어갈 때 Stack Cat은 현재 스택의 최상위 값 ( 0
이 경우)을 기억합니다 . 그런 다음 반복이 끝날 때 동일한 값이 다시 나타날 때까지 루프가 실행됩니다. 이것은 우리에게 편리합니다. 각 반복에서 우리는 단순히 다음 잠재적 제수의 나머지를 계산 하고이 스택으로 이동하여 루프를 시작합니다. 제수를 찾으면 나머지가 0
있고 루프가 멈 춥니 다. 우리는 제수를 시작 n-1
해서로 줄이려고합니다 1
. 그것은 a) 우리가 도달하면 이것이 끝날 것이라는 것을 알고 있습니다.1
마지막으로 b) 그런 다음 시도한 마지막 제수를 검사하여 숫자가 소수인지 여부를 결정할 수 있습니다 (이 경우 1
소수, 그렇지 않은 경우).
가자 처음에는 짧은 선형 섹션이 있습니다.
<-!<:^>[:
당신은 지금 그 대부분의 일을 알고 있습니다. 새로운 명령은 -
및 !
입니다. 스택 고양이에는 증가 또는 감소 연산자가 없습니다. 그러나 -
(음수, 즉 곱하기 -1
) 및 !
(비트 NOT, 즉 곱하기 -1
및 감소)이 있습니다. 이것들은 증가 !-
, 또는 감소 로 결합 될 수 있습니다 -!
. 따라서의 n
상단 에서 사본을 줄인 -1
다음 n
스택에 다른 사본을 왼쪽으로 만든 다음 새 평가판을 가져 와서 그 아래에 놓습니다 n
. 따라서 첫 번째 반복에서 다음을 얻습니다.
4
3
... 1 4 -1 ...
0 0 0
^
추가 반복에서는 3
다음 테스트 제수 등으로 대체됩니다 ( n
이 시점에서 두 사본 은 항상 동일한 값입니다).
((-<)<(<!-)>>-_)
이것이 모듈로 계산입니다. 루프는 양수 값에서 종료되므로, 양수 값 을 얻을 때까지 -n
시행 제수 d
를 반복해서 추가합니다 . 일단 우리가 결과를 빼면 d
나머지가 나옵니다. 여기서 까다로운 점 -n
은 스택 맨 위에 올려 놓을 수 없으며 추가하는 루프를 시작할 수 없다는 것 입니다. 스택 d
맨 위가 음수이면 루프가 입력되지 않습니다. 이는 가역 프로그래밍 언어의 한계입니다.
따라서이 문제를 피하기 위해 n
스택 맨 위에서 시작 하지만 첫 번째 반복에서만 무시하십시오. 다시 말하지만, 그것은 그보다 더 간단하게 들립니다 ...
(-<)
스택의 상단이 양수이면 (즉, 첫 번째 반복에서만)이를로 무효화합니다 -
. 그러나 우리는 단지 두 번 적용될 때까지 루프를 떠나지(-)
않기 때문에 할 수 없습니다 . 따라서 양수 값이 있다는 것을 알기 때문에 하나의 셀을 왼쪽으로 옮깁니다 . 자, 이제 우리는 첫 번째 반복에 대해 확실하게 부정 했습니다. 그러나 새로운 문제가 생겼습니다. 테이프 헤드는 이제 첫 번째 반복에서 다른 모든 반복 위치와 다른 위치에 있습니다. 계속 진행하기 전에이를 통합해야합니다. 다음 은 테이프 헤드를 왼쪽으로 이동합니다. 첫 번째 반복 상황 :-
<
1
n
<
-4
3
... 1 4 -1 ...
0 0 0 0
^
그리고 두 번째 반복에서 ( 지금 우리가 d
한 번 추가했음을 기억하십시오 -n
) :
-1
3
... 1 4 -1 ...
0 0 0
^
다음 조건은 이러한 경로를 다시 병합합니다.
(<!-)
첫 번째 반복에서는 테이프 헤드가 0을 가리 키므로 완전히 건너 뜁니다. 추가 반복에서 테이프 헤드는 하나를 가리 키므로이를 실행하고 왼쪽으로 이동하여 셀을 증가시킵니다. 셀이 0에서 시작한다는 것을 알기 때문에 이제는 항상 양수이므로 루프를 떠날 수 있습니다. 이렇게하면 항상 메인 스택의 왼쪽에 2 개의 스택이 생겨 이제로 되돌아 갈 수 있습니다 >>
. 그런 다음 모듈로 루프의 끝에서을 수행 -_
합니다. 당신은 이미 알고 -
있습니다. XOR의 _
내용을 빼는 것 ^
입니다. 스택의 맨 위에 a
있고 그 아래의 값이 b
로 바뀐 a
경우 b-a
. 우리가 처음 부정 때문에 a
하지만, -_
대체 a
로 b+a
하여 추가,d
우리의 누적 합계로.
루프가 종료되면 (양수에 도달) 테이프는 다음과 같습니다.
2
3
... 1 1 4 -1 ...
0 0 0 0
^
가장 왼쪽의 값은 양수일 수 있습니다. 실제로, 그것은 반복 횟수에서 1을 뺀 것입니다. 또 다른 짧은 선형 비트가 있습니다.
_<<]>:]<]]
앞서 말했듯 d
이 실제 나머지 ( 3-2 = 1 = 4 % 3
) 를 얻으려면 결과를 빼야 하므로 _
한 번 더하십시오. 다음으로 왼쪽에서 증가하고있는 스택을 정리해야합니다. 다음 나누기를 시도 할 때 첫 번째 반복이 작동하려면 다시 0이어야합니다. 따라서 우리는 그곳으로 이동하여 그 양수 값을 다른 도우미 스택으로 푸시 <<]
한 다음 다른 스택과 함께 운영 스택으로 다시 이동 >
합니다. 우리는 끌어 d
와 :
와에 다시 밀어 -1
로 ]
하고 우리는 우리의 조건 스택에 나머지를 이동합니다 <]]
. 그것은 시험 분할 루프의 끝입니다. 이것은 우리가 0의 나머지를 얻을 때까지 계속됩니다.이 경우 왼쪽의 스택에는n
의 최대 제수 (이외 n
)
루프가 끝나면 *<
경로가 입력과 1
다시 결합되기 직전에 있습니다. 는 *
단순히으로 제로집니다 1
우리가 좀 필요한 것, 그리고 우리와 제수로 이동 <
(우리는 입력과 동일한 스택에있어 수 있도록 1
).
이 시점에서 세 가지 다른 종류의 입력을 비교하는 데 도움이됩니다. 먼저, n = 1
우리가 그 시범 부문을 수행하지 않은 특별한 경우 :
0
... 1 1 -1 ...
0 0 0
^
그런 다음 이전 예제 n = 4
에서 복합 번호 :
2
1 2 1
... 1 4 -1 1 ...
0 0 0 0
^
마지막으로 n = 3
소수입니다.
3
1 1 1
... 1 3 -1 1 ...
0 0 0 0
^
따라서 소수의 1
경우이 스택에 있고 복합 숫자의 0
경우보다 큰 양수를 갖 습니다 2
. 우리는이 상황을 다음과 같은 최종 코드 로 바꾸 0
거나 1
필요로합니다.
]*(:)*=<*
]
이 값을 오른쪽으로 밀면됩니다. 그리고 *
최하위 비트를 전환하여, 우리는 설정 : 크게 조건부 상황을 단순화하는 데 사용됩니다 1
에 (프라임) 0
, 0
양의 값으로 (복합)를 1
, 다른 모든 양의 값은 여전히 긍정적 유지됩니다. 이제 우리는 구별 0
하고 긍정적이어야합니다. 우리가 다른 곳을 사용하는 곳 (:)
입니다. 스택의 상단이 0
(그리고 입력이 소수 인 경우), 이것은 단순히 건너 뜁니다. 그러나 스택의 상단 (및 입력이 복합 수 있었다)이이 그것을 스왑 양수이면 1
우리가 지금 가질 수 있도록, 0
복합과1
소수의 경우-두 개의 고유 한 값만 있습니다. 물론 그것들은 우리가 출력하고 싶은 것과 반대이지만, 다른 것으로 쉽게 고쳐집니다 *
.
이제 남은 모든 우리 주변의 프레임 워크에 의해 예상 스택의 패턴을 복원하는 것입니다 : 오른쪽 스택의 상단에, 양의 값에 테이프 헤드를 초래하고, 하나 -1
의 스택 오른쪽에 그 . 이것은 무엇 =<*
을위한 것입니다. =
인접한 두 스택의 상단을 교환하여 -1
결과의 오른쪽으로 이동합니다 ( 예 4
: 다시 입력) .
2 0
1 3
... 1 4 1 -1 ...
0 0 0 0 0
^
그런 다음 왼쪽으로 이동하여 <
0을 1로 바꿉니다 *
. 그게 다야.
프로그램 작동 방식에 대해 더 자세히 알고 싶다면 디버그 옵션을 사용할 수 있습니다. -d
플래그를 추가하고 "
현재 메모리 상태를보고 싶은 위치 ( 예 : 이와 같이 )를 삽입하거나 -D
플래그 를 사용 하여 전체 프로그램의 완전한 추적을 얻습니다 . 또는 단계별 디버거와 함께 스택 고양이 인터프리터가 포함 된 Timwi의 EsotericIDE 를 사용할 수 있습니다 .