Brain-Flak에서의 골프 팁


24

Brain-flak 는 스택 기반 Turing-tarpit 언어이며, 나와 DJMcMayhem1000000000 사이에서 공동으로 작성되었습니다 .

일부 사용자는 Brain-Flak의 신비한 방법에 매우 경험이 있습니다. 그래서 저는이 질문을 우리뿐만 아니라 다른 사람들도 우리의 지식을 커뮤니티와 공유하고 "사용하기 힘들도록 고안된"이 언어의 진입 기준을 낮추는 방법으로 설정하는 것이 좋습니다. 그리고 아마도 더 많은 경험을 가진 사람들에게 새로운 속임수를 가르쳐 줄 수도 있습니다.

뇌파에서 골프를하기위한 팁이 있습니까? 저는 일반적으로 뇌-플래 크에 다소 특정한 코드 골프 문제에 적용될 수있는 아이디어를 찾고 있습니다 (예 : "댓글 제거"는 답이 아닙니다).

답변 당 하나의 팁을 게시하십시오.

답변:


22

세 번째 스택 사용

제목을 읽은 경우 약간 혼란 스러울 수 있습니다. Brain-Flak에는 반드시 두 개의 스택 만 있습니까? 그러나 나는 그것이 존재하고 그것이 Brain-Flak을 작성하고 골프를 타는 데 가장 강력한 도구가 아니라면 가장 강력하다는 것을 확신합니다.


"Third Stack"은 무엇입니까?

모든 Brain-Flak 프로그램은 어떤 방식 으로든 세 번째 스택을 사용하지만 대부분의 사용은 뒤에서 진행되며 종종 존재한다는 사실을 단순히 무시하는 것이 유용합니다. 프로그램의 각 괄호는 스택에서 하나의 항목을 추가하거나 제거합니다. 열린 괄호 중 3 개는 ([<모두 스택에 항목을 추가하고 3 개의 켤레는 )]>모두 스택에서 항목을 제거합니다. 스택의 아이템 값은 프로그램의 현재 범위 값이며 nilads를 사용하면 특정 방식으로이 값을 수정합니다. 닫기 괄호 )는 요소를 세 번째 스택에서 현재 스택으로 이동시키는 고유 한 기능을 가지고 있습니다. 푸시.

잘만되면 이것은 당신에게 분명 해지고있다. 세 번째 스택은 이미 실행 된 코드의 반환 값을 기억하는 일종의 스택입니다. 두 개의 일반 스택과 세 번째 스택을 추적하는 간단한 프로그램의 예를 살펴 보겠습니다.

다음과 같은 프로그램을 안내합니다. 이 프로그램은 -3, 1, -2스택으로 푸시 됩니다.

(([()()()])(()))

우리는 세 개의 열린 괄호로 시작합니다.

이제 스택은 다음과 같습니다. 세 번째 스택은 오른쪽에 있고 활성 스택은 그 ^아래에 있습니다.

        0
        0
  0  0  0
  ^

(([()()()])(()))
   ^

이제 우리는 세 가지 ()nilads가 있습니다. 이것들은 일반적인 두 스택에는 아무런 영향을 미치지 않지만 각각 세 번째 스택의 상단에 하나씩 추가하여 스택을 다음과 같이 만듭니다.

        3
        0
  0  0  0
  ^

(([()()()])(()))
         ^

이제 ]닫는 중괄호가 세 번째 스택에서 항목을 제거하기 전에 언급 한대로 표시되지만 스택 ]의 상단에서 제거하는 요소를 빼는 기능이 있습니다. 따라서 새로운 스택은 다음과 같습니다.

       -3
  0  0  0
  ^

(([()()()])(()))
          ^

이것은 말이된다; [...]부정 ]은 아래로 빼야합니다.

이제 우리는를 실행해야합니다 ). )프로그램에서 물건이 스택으로 푸시되는 위치를 기억할 것이므로 세 번째 스택의 상단을 현재 스택으로 이동하고 또한 -3세 번째 스택의 다음 요소 에을 추가합니다 .

 -3  0 -3
  ^

(([()()()])(()))
           ^

다시 한번 우리는 세 개의 열린 중괄호 중 하나를 만나서 세 번째 스택에 다른 요소를 추가합니다.

        0
 -3  0 -3
  ^

(([()()()])(()))
            ^

앞서 말했듯 ()이 세 번째 스택의 맨 위를 하나씩 증가시킵니다.

        1
 -3  0 -3
  ^

(([()()()])(()))
              ^

그리고 )세 번째 스택의 상단을 활성 스택으로 옮기고 아래쪽으로 추가합니다

  1
 -3  0 -2
  ^

(([()()()])(()))
               ^

마지막 )은 세 번째 스택을 활성 스택 으로 이동하고 추가 할 요소가 세 번째 스택에 남아 있지 않으므로 다른 작업은 수행하지 않습니다.

 -2
  1
 -3  0
  ^

(([()()()])(()))
                ^

프로그램이 종료되었으므로 종료하고 출력합니다.


이 예제는 세 번째 스택이 무엇이며 어떤 역할을하는지에 대한 느낌을주기위한 것입니다. 모든 작업이 포함되어 있지는 않지만 각 작업이 자체적으로 수행하는 작업을 파악할 수 있기를 바랍니다. 여전히 어려움을 겪고 있다면이 답변 맨 아래에 "치트 시트"를 포함시켜 도움을 드리겠습니다.

좋아, 그럼?

자, 이제 세 번째 스택을 이해하지만 "그래서 무엇"입니까? "Third Stack"이라고하지 않더라도 이미 사용하고 있었는데, Third Stack이라는 관점에서 생각하는 것이 골프에 어떤 도움이됩니까?

문제를 보자. 당신 은 숫자삼각형을 원합니다 . 이것은 n보다 작은 모든 숫자의 합입니다.

한 가지 방법은 오프 스택에서 누산기를 만들고 카운트 다운 할 때 추가하는 것입니다. 다음과 같은 코드가 생성됩니다.

(<>)<>{(({}[()])()<>{})<>}{}<>({}<>)

온라인으로 사용해보십시오!

이 코드는 매우 간결하며 더 작아 질 수 없다고 생각할 수 있습니다. 그러나 우리가 세 번째 스택 관점에서 접근하면 이것이 비효율적이라는 것이 분명해집니다. 누산기를 오프 스택에 두는 대신 a를 사용하여 세 번째 스택에 배치하고 (사용하는 마지막에 검색 할 수 있습니다 ). 우리는 다시 한 번 모든 숫자를 반복하지만 이번에는 세 번째 스택을 늘리기 위해 많은 일을 할 필요가 없습니다. 이것은 다음과 같습니다

({()({}[()])}{})

온라인으로 사용해보십시오

이 코드는 우리가 이전에 만든 골프 버전의 절반보다 작습니다. 실제로 컴퓨터 검색은이 프로그램이이 작업을 수행 할 수있는 가장 짧은 프로그램이라는 것을 증명했습니다. 이 프로그램은 "모든 실행의 합"접근법을 사용하여 설명 할 수 있지만, 제 3 스택 접근법을 사용하여 설명하면 훨씬 직관적이고 명확하다고 생각합니다.

세 번째 스택은 언제 사용합니까?

이상적으로 Brain-Flak에서 새로운 문제에 대한 작업을 시작할 때마다 세 번째 스택을 염두에두고 어떻게해야하는지 스스로 생각해야합니다. 그러나 어떤 유형의 누산기를 추적하거나 누적 합계를 유지해야 할 때마다 일반적으로 두 개의 실제 스택 대신 세 번째 스택에 배치하는 것이 좋습니다.

세 번째 스택 사용을 고려하는 것이 좋은 또 다른 때는 다른 두 스택에 값을 저장할 공간이 없을 때입니다. 이것은 두 개의 기존 스택에서 조작을 수행 할 때 유용하며 나중에 위치를 추적하지 않고 나중에 사용할 수 있도록 값을 저장하려고합니다.

세번째 스택의 한계

세 번째 스택은 여러 가지면에서 매우 강력하지만 자체 한계와 단점이 있습니다.

첫째, 주어진 시점에서 세 번째 스택의 최대 스택 높이는 컴파일 타임에 결정됩니다. 즉, 스택에서 일정량의 공간을 사용하려면 프로그램을 작성할 때 해당 공간을 할당해야합니다.

둘째, 세 번째 스택은 랜덤 액세스가 아닙니다. 이는 가장 높은 값을 제외한 모든 값에서 작업을 수행 할 수 없음을 의미합니다. 또한 스택에서 값을 이동할 수 없습니다 (예 : 처음 두 요소 교환).

결론

세 번째 스택은 강력한 도구이므로 모든 Brain-Flak 사용자에게 필수적이라고 생각합니다. Brain-Flak 프로그래밍에 익숙해지기 위해서는 약간의 적응이 필요하지만, 올바르게 사용하면 골프와 관련하여 괜찮은 것과 놀라운 것의 모든 차이를 만듭니다.

컨닝 지

다음은 작업 목록과 해당 작업이 세 번째 스택에 미치는 영향입니다

 Operation  |                 Action
====================================================
   (,[,<    | Put a zero on top of the Third Stack
----------------------------------------------------
     )      | Add the top of the Third Stack to the
            | second element and move it to the 
            | active stack
----------------------------------------------------
     ]      | Subtract the top of the Third Stack
            | from the second element and pop it
----------------------------------------------------
     >      | Pop the top of the Third Stack
----------------------------------------------------
     ()     | Add one to the top of the Third Stack
----------------------------------------------------
     {}     | Pop the top of the active stack and
            | add it to the top of the Third Stack
----------------------------------------------------
     []     | Add the stack height to the Third
            | Stack
----------------------------------------------------
   <>,{,}   | Nothing

1
와, 환상적인 팁! 나는 이것을 보았을 때 실제로 비슷한 대답을하기 위해 여기에 왔습니다. +1! 나는 그것을 Accumulator 로 생각 하고 싶지만 The Third Stack 은유는 정말 흥미 롭습니다.
DJMcMayhem

나는 항상 "워크 스페이스"또는 "워크 스택"이라고 불렀습니다.
sagiksp

Brainflak의 TIO 버전 {...}에서 모든 반복의 합을 반환합니다.
CalculatorFeline

@CalculatorFeline 예, 이것은 초기 버전을 제외한 거의 모든 버전의 Brain-Flak에서 적용됩니다. 그러나 왜 당신이 왜이 게시물에 대해 언급했는지 잘 모르겠습니다.
위트 마법사

<>,{,} | Nothing
CalculatorFeline

21

계수 / 나머지 찾기

n 모듈로 m을 찾는 것은 많은 문제에 중요한 기본 산술 연산 중 하나입니다. 예를 들어 > 0 내지 MN> = 0 , 다음의 46 바이트의 단편을 사용할 수있다. 그것은 그 가정 n은 과 활성 스택의 상단에 m 다음 하나의 아래로, 그리고로 대체 N 모드 m . 나머지 스택은 그대로 둡니다.

({}(<>))<>{(({})){({}[()])<>}{}}{}<>([{}()]{})

이 주석이 달린 버전은 프로그램의 일부 지점에서 스택 내용을 보여줍니다. ;두 스택을 분리하고 .활성 스택을 표시합니다.

. n m;
({}(<>))<>
{   . m; r 0   (r = n - km)
    (({}))
    . m m; r 0
    {({}[()])<>}
    {}
}
m-n%m-1 m; . 0
{}<>([{}()]{})
. n%m;

주석이없는 부분이 무엇을했는지 이해하는 데 시간이 걸렸습니다 ( {({}[()])<>}). 일단 이해하면 ... Genius :-)
ETHproductions

11

푸시 팝 리던던시

이것은 큰 것입니다. 그것은 또한 미묘한 차이가 있습니다.

아이디어는 무언가를 밀고 아무 것도하지 않고 터뜨리면 전혀 밀지 않아야한다는 것입니다.

예를 들어 오프 스택으로 무언가를 옮긴 다음 추가하려는 경우 다음 코드를 작성할 수 있습니다.

({}<>)({}())

이것은 다음과 같이 간단 할 수 있습니다.

({}<>())

첫 번째 프로그램은 항목을 집어 들고 다시 집어 들고 하나를 추가하는 반면 두 번째 프로그램은 한 번에 두 항목을 모두 처리합니다.

이 예제는 간단하지만 훨씬 더 복잡해질 수 있습니다. 예를 들어 보자.

({}<({}<>)><>)(<((()()()){}[((){}{})])>)

감소는 명확하지 않지만 프로그램의 4 번째 팝은 두 번째 푸시로 감소시킬 수 있습니다.

(<((()()()){}[((){}<({}<>)><>{})])>)

이것은 내 골프 레퍼토리에서 가장 강력한 도구이지만 잘 활용하려면 연습이 필요합니다. 이 작업을 잠시 수행하면 거의 즉시 이러한 작업을 수행 할 수 있습니다.


후자의 예에서 첫 번째 괄호 쌍의 일부가 ({}<{}>)?
feersum

@feersum 아닙니다. 스택의 두 번째 항목 사본을 오프 스택으로 이동 ({}<{}>)하고 항목을 완전히 파괴합니다. 그러나 이러한 프로그램은 실제로 여기서 작동하는 원리를 강조하기 위해 최적의 것이 아닙니다.
밀 마법사

6

정수 최적화

Brain-Flak에서는 정수가 지루합니다. 다행히도 Brain-Flak Integer 골프에 도움이되는 질문이 있습니다. (질문은 정수를 스택으로 푸시하도록 설계되었으므로 푸시 팝 중복은 더 현실적인 프로그램에 적용될 수 있습니다.)


우리는 또한 brain-flak.github.io/integer/ 를 가지고 있는데 , 이는 편의상 이러한 알고리즘 중 하나를 온라인으로 실행합니다.
DJMcMayhem

@DrMcMoylex Brain-Flak 자체에서 정수 metagolfer를 구현할 때 필요한 모든 것!

1
우리는 그것을 가지고 있습니다. codegolf.stackexchange.com/a/98054/31716 : D
DJMcMayhem

5

추가 루프 카운터 푸시

자주, 당신은 같은 것을하고 싶을 것입니다

스택의 모든 요소에 대해 X 작업 수행

또는

스택의 모든 인접 요소 쌍에 대해 X 작업 수행

입력에 '0'이 포함되거나 X 연산의 결과에 0이 표시되면 실제로 불편한 것입니다. 당신이해야하기 때문에 :

([])
{

  {}

  ({}...<>)
  ([])

}

각 요소에 X 를 수행 한 다음 나중에

<>
([])
{
  {}
  ({}<>)
  <>
  ([])
}
<>

배열을 다시 반전시킵니다.

더 나쁜 것은 인접한 요소 쌍에서 작업 ([][()])하는 경우 대신 대신 해야합니다 ([]). 정말 불편합니다. 요령은 다음과 같습니다. 각 요소에 X 를 수행하는 동안 1 바로 위의 대체 스택 으로 1 을 푸시 합니다 X(element). 그런 다음 반전시키면서 간단하게 수행 할 수 있습니다.

<>
{
  {}
  ({}<>)
  <>
}
<>

이것은 8 바이트 더 짧으므로 1을 푸시하기 위해 추가 코드를 고려하면 결국 4-6 바이트를 절약하게됩니다. 보다 구체적인 예를 보려면 배열의 델타를 얻는 작업을 비교하십시오. 이 트릭이 없으면 다음이 필요합니다.

([][()]){

    {}

    ([{}]({})<>)<>

    ([][()])

}{}{}<>

([])
{{}({}<>)<>([])}<>

이 트릭을 사용하면

([][()]){

    {}

    ([{}]({})<>)(())<>

    ([][()])

}{}{}<>

{{}({}<>)<>}<>

올바른 방법으로 사용하는 경우 (예를 들어, 먼저 후진 ([][()])하고 나중에 두 개 를 제거하는 경우 ),이 경우 특정 시나리오에서 훨씬 더 많은 비용을 절약 할 수 있습니다.


3

'스택-높이'nilad 활용

특히 문제 또는 입력의 크기를 항상 알고있는 문제에서는 '스택-높이'nilad []를 활용하여 정수를 만들 수 있습니다.

가상의 도전 과제로이를 해결해 봅시다 : output CAT. 골피가 아닌 방법은 온라인 정수 골퍼 를 사용하여 67, 65 및 84를 누르는 것입니다.

(((((()()()()){}){}){}()){}())

(((((()()()()){}){}){}){}())

((((((()()()){}()){}){})){}{})

(명확성을위한 줄 바꿈). 이것은 88 바이트이며 그렇게 크지 않습니다. 대신 값 사이의 연속적인 차이를 푸시하면 많은 비용을 절약 할 수 있습니다. 따라서 첫 번째 번호를 푸시 콜로 감싸고 2를 뺍니다.

(   (((((()()()()){}){}){}()){}())  [()()] )

그런 다음이 코드를 가져 와서 푸시 콜로 감싸서 끝에 19를 추가합니다.

(  ((((((()()()()){}){}){}()){}())[()()])   ((((()()()){})){}{}()) )

이것은 무려 26 바이트의 골프를위한 62 바이트입니다!

이제 여기가 우리가 스택 높이 nilad을 활용할 수있는 곳입니다. 우리가 19을 누르면 시작 시간, 우리는 너무 스택에이 개 항목이 이미 있다는 것을 알고 []로 평가한다가 2. 이것을 사용하여 더 적은 바이트로 19를 만들 수 있습니다. 확실한 방법은 내부 ()()()를 로 변경하는 것 ()[]입니다. 그래도 2 바이트 만 절약합니다. 좀 더 땜질하면 19로 밀 수 있습니다.

((([][]){})[]{})

이렇게하면 6 바이트가 절약됩니다. 이제 우리는 56 세로 떨어졌습니다.

다음 팁에서이 팁이 매우 효과적으로 사용되는 것을 볼 수 있습니다.


귀하의 CAT프로그램은 실제로 다음을 추진합니다 TAC: P
Wheat Wizard


2
나는 이것이 팁이라는 것을 알고 있지만 50 바이트 나 자신을 도울 수 없다 .
밀 마법사

1
남용을 돕는 또 다른 이상하지만 때로는 효과적인 팁 []: 코드 앞에 0s를 붙 (<>)입니다. 어쨌든 코드를 거꾸로 푸시하려는 경우에만 실제로 작동하지만 올바른 숫자를 발견하면 코드를 상당히 줄일 수 있습니다. 오히려 극단적 인 예를 들어 내가 6 개 추가 0내게 많은처럼 사용할 수 있습니다들, []내가 사용으로의()
조 왕

2

위키 사용

우리는 위키가 있습니다 ! 약간의 문제가 있지만 도움이되는 자료입니다. 그것은 당신이 당신의 코드에 붙여 넣을 수있는, 골프, 유용한 골프, 스택 클린 프로그램의 목록이 있습니다. 당신이 뭔가를하고 싶다면 당신은 위키에있을 가능성이 높기 전에 누군가가했을 수도 있다고 생각합니다.


2

더 나은 반복

쉬운 방법은 다음과 같습니다.

상당히 일반적인 구조는 다음과 같습니다.

(({})<{({}[()]<...>)}{}>)

n 번 반복하지만 여전히 n을 유지하려는 위치. 그러나 이것은 다음과 같이 쓸 수 있습니다.

({<({}[()]<...>)>()}{})

2 바이트를 절약 할 수 있습니다.

또 다른 일반적인 패러다임은

([])({<{}>...<([])>}{})

전체 스택을 반복하고 누적합니다. 멋진 수학으로 인해 다음과 같습니다.

(([]){[{}]...([])}{})

1

네거티브 확인

때로는 [...]모나드로 무엇을 무효화할지 전략적으로 선택하여 몇 바이트를 골프로 칠 수 있습니다.

간단한 예는 중첩 된 것 [...]입니다. 예를 들어 [()()()[()()]]단지가 될 수[()()()]()()

값이 시작 괄호인지 확인하려고한다고 가정하십시오 (<{[. 첫 번째 시도는 각 문자의 차이를 밀어 내고 빼는 것보다 반복하는 것입니다.

({}<(((((       #Push the differences between start bracket characters
(((()()()()){}){}){})   #Push 32
[()])                   #Push 31
[((()()())()){}{}])     #Push 20
){})><>)                #Push 40
<>({<(([{}]<>{}))>(){[()](<{}>)}<>})

그러나 네거티브 버전의 차이점을 대신 밀어서 4 바이트를 절약 할 수 있습니다!

({}<(((((       #Push the differences between start bracket characters
((([()()()()]){}){}){}) #Push -32
())                     #Push -31
((()()())()){}{})       #Push -20
){})><>)                #Push -40
<>({<(({}<>{}))>(){[()](<{}>)}<>})

일반적으로 이렇게하면 비용을 크게 절약 할 수 없지만 [...]주변 환경 을 바꾸려는 노력도 거의 들지 않습니다 . 양수 대신 카운터 음수를 눌러 나중에 감소하는 대신 여러 번 증분하는 것을 절약 할 수있는 상황에주의하십시오. 또는 스와핑 (a[b])과 함께하는 ([a]b)두 숫자의 부정적인 대신에 긍정적 인 차이를 만들 수 있습니다.


1
0- <a<b>c>> <abc><a[b]c>->으로 비슷한 작업을 수행 할 수 있습니다 <abc>.
밀 마법사
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.