Brainfuck에서의 골프 팁


23

당신은 brainfuck에서 골프에 대한 일반적인 팁이 있습니까? 나는 일반적으로 brainfuck에 다소 특정한 코드 골프 문제에 적용될 수있는 아이디어를 찾고 있습니다 (예 : "댓글 제거"는 답이 아닙니다). 답변 당 하나의 팁을 게시하십시오.

답변:


25

답변 당 하나의 팁을 넣으면 너무 많은 답변이 될 것입니다.

  • Brainfuck에서 생각하는 법을 배우십시오. 그것은 다른 것과는 매우 다릅니다. 많은 brainfuck 프로그램을 읽고, 쓰고, 쓰고, 다시 쓰고 있습니다. 이 언어는 많은 작업을 제공하지 않으므로 유연하고 효율적으로 제공하는 것을 사용하는 것이 중요합니다. 당신과 언어 사이에 추상화가 들어 가지 않도록하십시오.

  • 비파괴 흐름 제어로 매우 편안합니다. 시작 셀을 다른 곳에 복사하여 루프를 떠난 후 다시 복사하여 의사 결정 루프를 벗어나려면 포인터를 근처의 기존 0으로 이동하는 것이 좋습니다. 예, 이것은 루프를 통과했는지 여부에 따라 포인터가 다른 위치에 있음을 의미하지만 해당 위치는 근처에 0과 0이 아닌 다른 배열이있을 수 있으므로 다른 루프를 사용하여 포인터 위치를 다시 동기화하는 데 사용할 수 있습니다. 이 기술은 좋은 Brainfuck 프로그래밍의 기본이며 다양한 형태가 지속적으로 유용 할 것입니다.

  • 그 모든 사실 >이나 <비용이 메모리 레이아웃의 세부 사항이 중요하다는 것을 의미한다. 인내심이있는 한 많은 레이아웃 변형을 시도하십시오. 또한 메모리 레이아웃이 데이터를 위치에 엄격하게 매핑 할 필요는 없습니다. 실행 과정에서 변경 될 수 있습니다.

  • 더 큰 규모로 다양한 알고리즘을 구현하고 고려해보십시오. 처음에는 어떤 알고리즘이 가장 적합한 지 명확하지 않습니다. 어떤 기본적인 접근 방식이 가장 좋을지 분명하지 않을 수도 있으며, 아마도 일반 언어에서 가장 좋은 방법과 다를 수도 있습니다.

  • 크거나 가변적 인 크기의 데이터를 처리하는 경우 데이터의 크기 나 숫자 위치를 추적하지 않고도 로컬에서 처리 할 수있는 방법이 있는지 확인하십시오.

  • 동일한 데이터는 서로 다른 두 가지가 될 수 있습니다. 대부분의 경우 숫자 또는 문자와 0이 아닌 위치 마커입니다. 그러나 random.b를 참조하십시오. 여기서 비트 카운터는 셀룰러 오토 마톤의 한 셀 값으로 두 배가됩니다.

  • 동일한 코드는 서로 다른 두 가지 작업을 수행 할 수 있으며 코드가 일반적인 언어로 수행하는 것이 훨씬 쉽습니다 <+<. 그러한 가능성에주의하십시오. 실제로, 잘 작성된 프로그램 인 것처럼 보일지라도, 완전히 삭제 될 수있는 작은 부분이 있으며, 추가 된 것이 없으며, 상황에 따라 여전히 완벽하게 실행되는 것을 알 수 있습니다.

  • 대부분의 언어에서 컴파일러 또는 인터프리터를 자주 사용하여 프로그램의 동작을 확인합니다. Brainfuck 언어는 더 큰 개념적 제어를 요구합니다. 프로그램이 무엇을하는지 알려주는 컴파일러가 필요하다면, 프로그램을 충분히 파악하지 못했을 것입니다. 아마도 적어도 분명한 이미지를 원한다면 프로그램을 좀 더 쳐다 봐야 할 것입니다. 골프에 능숙한 유사한 프로그램의 개념적 후광. 실제로는 하나의 프로그램을 실행하기 전에 12 가지 버전의 프로그램을 생성하게되며,이 시점에서 가장 짧은 프로그램이 올바르게 실행될 것입니다.

  • 행운을 빕니다! Brainfuck을 간결하게 쓰려고하는 사람은 거의 없지만, 언어가 놀랍도록 모호한 예술 형태 인 언어로 지속적인 관심을 정당화 할 수있는 유일한 방법이라고 생각합니다.


3
이것을 읽으면 지금 Brainfuck에서 프로그래밍을 시도하고 싶습니다 ..
Claudiu

젠장, 내가 당신의 이름을 알고 있다고 생각했습니다. 당신의 brainfuck 프로그램, 특히 당신의 짧은 자기 해석기의 큰 팬!
조 왕

"때때로 ... 완전히 삭제 될 수있는 작은 부분이 있고, 추가 된 것이 없으며, 상황에 따라 여전히 완벽하게 실행될 것입니다." 이것은 특히 초보자에게 해당됩니다. 나는 기억할 수있는 한 BF에 단 하나의 답변을 썼지 만, 개정 내역 에는 프로그램을 가지고 놀고 있고 무작위로 갔다. "이 비트를 제거해도 프로그램은 여전히 ​​작동합니다! "
ETHproductions

"인내심이 많은 레이아웃을 다양하게 시도해보십시오"또는 무차별 대입 할 수 있습니다 . : P
Esolanging Fruit

9

여기 몇 가지 팁이 있습니다.

상수 :

Esolangs '상수 페이지는 특정 값을 만들 수있는 짧은 가지의 매우 유용한 목록이 있습니다. 나는 프로그램 당 적어도 두 번이 페이지를 참조하는 것을 발견했다.

모든 것의 시작 :

+++[[<+>>++<-]>]

테이프를 3 * n ^ 2 형식으로 설정합니다.

3 6 12 24 48 96192128 0 '

이것이 왜 중요한가?

리스트를 내려 가자 :

  • 3과 6은 지루하다
  • 12 : 10 (줄 바꿈) 또는 13 (캐리지 리턴)에 가깝습니다. 0-9 카운터에도 사용할 수 있습니다
  • 24 : 알파벳의 문자 수인 26에 가까움
  • 48 : ASCII 0
  • 96 : 97에 가까운, ASCII a
  • 196 및 128 : 196-128 = 64, 65에 가까운 ASCII A.

이 하나의 알고리즘에서 ASCII 범위의 거의 모든 시퀀스가 ​​시작되며 각각의 카운터와 개행 문자를 쉽게 찾을 수 있습니다.

실용적인 예 :

모든 대문자와 소문자 및 숫자를 인쇄합니다.

알고리즘으로 :

+++[[<+>>++<-]>]<<[-<->]<<<<++[->>+.>+.<<<]<--[>>.+<<-]

없이:

+++++++++++++[->+++++++>++>+++++>++++>+<<<<<]>+++++>[-<+.>>.+<]>>---->---[-<.+>]

우리는 대부분의 바이트를 두 번째 예에서 테이프를 초기화 하는 데 사용합니다. 이 중 일부는 첫 번째 예에서 추가 이동으로 상쇄되지만이 방법은 분명히 이점이 있습니다.

같은 맥락에서 몇 가지 다른 흥미로운 알고리즘 :

3 * 2 ^ n + 1 :

+++[[<+>>++<-]+>]
Tape: 4 7 13 25 49 65 197 129 1 0'

이는 값을 1 씩 상쇄하여 몇 가지 사항을 수행합니다. 12는 캐리지 리턴, 64는 대문자 알파벳의 실제 시작, 24는 26에 더 가깝게 만듭니다.

2 ^ n :

+[[<+>>++<-]>]
Tape: 1 2 4 8 16 32 64 128

64는 대문자에 적합하기 때문에 32는 공백에 대한 ASCII이고 128은 26에 대한 카운터로 사용될 수 있습니다 (130/5 = 26). 이것은 숫자와 소문자가 필요하지 않은 특정 상황에서 바이트를 절약 할 수 있습니다.

질문에 맞는 구현을 선택하십시오.

  • 음수 셀은 거의 항상 유용하며 바이트 수를 변경하지 않는 한 피할 이유가 없습니다.
  • 많은 상수가 줄 바꿈을 사용하기 때문에 셀을 줄 바꿈하는 것과 거의 동일합니다.
  • 임의의 셀 크기는 피보나치 시퀀스를 무한히 계산 ( +[[-<+>>+>+<<]>])하거나 더 큰 / 음수를 처리 하는 등의 무한 수학 시퀀스에 유용 합니다. 단점은 숫자가 음수 인 경우 [-]와 같은 일반적인 방법 과 [->+<]의존 할 수 없다는 것입니다.
  • EOF는 0, -1이거나 변경되지 않습니다. 추가 검사없이 전체 입력을 반복 할 수 있으므로 일반적으로 0이 바람직합니다. -1은 배열 구조를 반복 할 때 유용합니다. 아직 변경 사항이없는 용도를 찾지 못했습니다 :(.

프릭이 진행되는 상황을 추적하십시오.

항상 포인터가 그 주변의 데이터와 관련하여 어디에 있어야하는지에 대한 의견이 있어야하며 각 셀의 가능한 값 범위를 알고 있어야합니다. 이것은 루프 전에 포인터를 분할 할 때 특히 중요합니다. 나중에 두 가지 가능성을 다시 결합해야하기 때문입니다.

언제든지 내 코드에는 다음과 같은 다른 모든 줄에 주석이 흩어져 있습니다.

*0 *dat a_1 ?  0' !0 0*
 or
*0 *dat 0' ap1 0  !0 0*

추가적인 조언은 심볼에 특별한 의미를 부여하는 것입니다. 위의 예 '에서 포인터가있는 위치는 *해당 방향으로의 반복을 ?의미하고 값을 알 수없는 셀을 !0의미하고 0이 아닌 셀을 의미하며 _에 대한 대체 -p대체입니다 +. or테이프가 표현 중 하나처럼 보일 수 있으며 처리해야 함을 의미합니다.

당신의 상징 체계는 반드시 내 것과 동일 할 필요는 없으며 (몇 가지 결함이 있음) 단지 일관성이 있어야합니다. 디버깅 할 때 당신은 그 시점에 그것을 실행하고 당신이 무엇을 실제 테이프를 비교할 수 있기 때문에, 또한 매우 유용 합니다 코드의 잠재적 결함을 지적 할 수있는 있습니다.


5

내 주요 조언은 하지 않을 것입니다.

좋아, 그보다 더 유용한 것을 원해. BF는 이미 매우 간결한 언어이지만 실제로 당신을 죽이는 것은 산술적이며 효과적으로 단 항적으로 수행해야합니다. Esolang 의 상수 페이지를 읽으면 효율적으로 많은 수를 쓰는 방법을 정확하게 선택하고 가능한 한 줄 바꿈을 활용하는 것이 좋습니다.

메모리 액세스도 매우 비쌉니다. 테이프를 읽고 있기 때문에 주어진 시간에 머리가 움직이는 위치를 염두에 두어야합니다. 그냥 쓰기 수있는 다른 언어와는 달리 a, b, c, BF에 당신은 당신이 무엇을 저장할 위치를 염두해야하므로 명시 적, 수 바이트를 왼쪽 또는 오른쪽으로 머리를 이동해야합니다. 최적의 방식으로 메모리를 구성하는 것이 NP-hard이므로 확실합니다.


5

이 답변에서는 테이프의 특정 셀을 여러 번 참조하겠습니다. 어떤 셀인지는 중요하지 않지만 전체 답변에서 동일한 셀입니다. 이 게시물의 목적을 위해 해당 셀을 "토드"라고합니다.

셀을 일정한 값으로 설정하려고 할 때 때로는 즉시 완료하지 않기 위해 지불합니다. 예를 들어, Todd에 30을 포함 시키려고한다고 가정하십시오. 나중에 코드에서 Todd의 값을 수정할 수 있지만 절대 읽지 않을 수 있습니다. Todd로 돌아옵니다. Todd의 값이 0이면 프로그램이 종료됩니다. 그렇지 않으면 Todd의 값이 영원히 인쇄됩니다.

brainfuck 상수esolangs.org 페이지에 따르면 (아마도 팁의 주제 일 수 있습니다!) 30을 얻는 가장 짧은 방법은 >+[--[<]>>+<-]>+입니다. 그 선두 >는 포인터 왼쪽의 아무것도 수정되지 않도록하기 위해 존재하지만이 경우 우리는 그것에 신경 쓰지 않고 떨어 뜨린다 고 가정합니다. 해당 코드를 사용하면 코드는 다음과 같습니다.

+[--[<]>>+<-]>+(MISC. CODE)(GO TO TODD)[.]

다음과 같이 첫 번째 코드 덩어리를 생각할 수 있습니다.

(SET TODD TO 30)(MISC. CODE)(GO TO TODD)[.]

그러나 해당 청크의 마지막 두 문자를 기억하십시오 >+. 이런 식으로 생각하는 것만 큼 유효합니다.

(SET TODD TO 29)(GO TO TODD)(ADD 1 TO TODD)(MISC. CODE)(GO TO TODD)[.]

당신은 (GO TO TODD)두 번 유의하십시오 ! 대신 다음과 같이 코드를 작성할 수 있습니다.

(SET TODD TO 29)(MISC. CODE)(GO TO TODD)(ADD 1 TO TODD)[.]
+[--[<]>>+<-](MISC. CODE)(GO TO TODD)+[.]

그것이 필요한 바이트의 수가 (GO TO TODD)이전과 같다고 가정하면 , 하나의 적은 이동 == 하나의 적은 바이트! 때때로 당신의 시작 위치가 바뀌 었다고해서 그 이점이 사라지는 것은 아니지만 항상 그런 것은 아닙니다.


0

입력하지 않고 도전에 대한 작은 팁. 대부분의 인터프리터 (TIO.run 하나 포함)가 셀 내용을 EOF 표시로 0으로 설정하므로 셀을 빠르게 지워야하는 경우 ,대신 대신 사용할 수 있습니다 [-]. 이것은 프로그램을 조금 이식 할 수 없게 만들지 만, 어쨌든 코드 골프에서 누가 관심이 있습니까?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.