GNU 프롤로그, 98 바이트
b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).
이 답변은 Prolog가 가장 간단한 I / O 형식으로 어려움을 겪을 수있는 좋은 예입니다. 문제를 해결하는 알고리즘이 아니라 문제를 설명함으로써 진정한 프롤로그 스타일로 작동합니다. 적법한 버블 배열로 계산할 대상을 지정하고 모든 버블 배열을 생성하도록 Prolog에 요청한 다음 계산합니다. 세대는 55 자 (프로그램의 처음 두 줄)를 사용합니다. 카운팅 및 I / O는 나머지 43 개 (세 번째 줄과 두 부분을 구분하는 줄 바꿈)를 사용합니다. OP가 언어로 인해 I / O로 어려움을 겪을 것으로 예상되는 문제는 아닙니다. (참고 : Stack Exchange의 구문 강조 표시를 사용하면 읽기가 더 어렵고 쉽지 않기 때문에 해제했습니다.)
설명
실제로 작동하지 않는 유사 프로그램의 유사 코드 버전으로 시작해 보겠습니다.
b(Bubbles,Count) if map(b,Bubbles,BubbleCounts)
and sum(BubbleCounts,InteriorCount)
and Count is InteriorCount + 1
and is_sorted(Bubbles).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
b
작동 방식 이 매우 명확해야 합니다. 우리는 정렬 된 목록을 통해 거품을 표현하고 있습니다 (동일한 다중 집합을 동일하게 비교하는 다중 집합의 간단한 구현). 단일 거품 []
의 개수는 1이고 큰 거품은 개수를 갖습니다. 내부에있는 거품의 총 수에 1을 더한 값 1과 같습니다. 4의 경우이 프로그램은 (작동 한 경우) 다음 목록을 생성합니다.
[[],[],[],[]]
[[],[],[[]]]
[[],[[],[]]]
[[],[[[]]]]
[[[]],[[]]]
[[[],[],[]]]
[[[],[[]]]]
[[[[],[]]]]
[[[[[]]]]]
이 프로그램은 여러 가지 이유로 대답하기에 부적합하지만 가장 중요한 것은 Prolog에 실제로 map
술어 가 없다는 것입니다 (작성하는 데 너무 많은 바이트가 소요됨). 대신 우리는 다음과 같이 프로그램을 작성합니다.
b([], 0).
b([Head|Tail],Count) if b(Head,HeadCount)
and b(Tail,TailCount)
and Count is HeadCount + TailCount + 1
and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
다른 주요 문제는 Prolog의 평가 순서가 작동하는 방식으로 인해 실행될 때 무한 루프로 진행된다는 것입니다. 그러나 프로그램을 약간 재정렬하여 무한 루프를 해결할 수 있습니다.
b([], 0).
b([Head|Tail],Count) if Count #= HeadCount + TailCount + 1
and b(Head,HeadCount)
and b(Tail,TailCount)
and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
and length(List,NPossibilities).
우리는 그들이 무엇인지 알기 전에 우리가 함께 카운트를 추가하는 - - 이것은 매우 이상한 보일 수 있지만, GNU 프롤로그의이 #=
비인 연산의 종류를 처리 할 수 있고, 그것은의 첫 번째 라인이기 때문에 b
, 그리고 HeadCount
그리고 TailCount
반드시 모두 미만이 될 Count
(알려진) 재귀 용어와 일치하는 횟수를 자연스럽게 제한하는 방법으로 사용되므로 프로그램이 항상 종료됩니다.
다음 단계는 골프를 조금만하는 것입니다. 같은 약어를 사용하여 단일 문자 변수 이름을 사용하여, 여백 제거 :-
용 if
및 ,
대를 and
이용 setof
하기보다는 listof
(이것은 짧은 이름을 가지고 있으며,이 경우에도 동일한 결과를 생성하는)을 사용 sort0(X,X)
하기보다는 is_sorted(X)
때문에 ( is_sorted
실제로는 실제 함수 아니다 나는 그것을 만들었다) :
b([],0).
b([H|T],N):-N#=A+B+1,b(H,A),b(T,B),sort0([H|T],[H|T]).
c(X,Y):-setof(A,b(A,X),L),length(L,Y).
이것은 상당히 짧지 만 더 잘 할 수 있습니다. 주요 통찰력은 [H|T]
목록 구문이 진행됨에 따라 실제로 장황 하다는 것입니다. Lisp 프로그래머가 알고 있듯이,리스트는 기본적으로 단지 튜플 인 cons 셀로 구성되며이 프로그램의 어떤 부분도리스트 내장을 사용하지 않습니다. 프롤로그에는 몇 가지 매우 짧은 튜플 구문이 있습니다 (내가 가장 좋아하는 것은 A-B
이지만 두 번째로 가장 좋아하는 것은 A/B
입니다.이 경우 읽기 쉬운 디버그 출력을 생성하므로 여기에서 사용하고 있습니다). 그리고 우리는 또한 nil
두 개의 문자에 집착하지 않고 목록의 끝에 자신의 단일 문자 를 선택할 수 있습니다 []
(나는 선택 x
했지만 기본적으로 모든 것이 작동합니다). 대신에을 [H|T]
사용 T/H
하여b
이것은 다음과 같습니다 (튜플의 정렬 순서는 목록의 정렬 순서와 약간 다르므로 위와 동일한 순서는 아닙니다).
x/x/x/x/x
x/x/x/(x/x)
x/(x/x)/(x/x)
x/x/(x/x/x)
x/(x/x/x/x)
x/x/(x/(x/x))
x/(x/x/(x/x))
x/(x/(x/x/x))
x/(x/(x/(x/x)))
위의 중첩 목록보다 읽기가 다소 어렵지만 가능합니다. 정신적으로 x
s를 건너 뛰고 /()
거품으로 해석하십시오 (또는 /
내용이 없으면 퇴행성 거품으로 일반화 ()
됩니다). 요소는 위에 표시된 목록 버전과 일대일 (무질서한 경우) 대응 관계가 있습니다 .
물론이 목록 표현은 훨씬 짧아 지더라도 큰 단점이 있습니다. 언어에 내장되어 있지 않으므로 sort0
목록이 정렬되어 있는지 확인할 수 없습니다 . sort0
어쨌든 상당히 장황하므로 손으로하는 것은 큰 손실이 아닙니다 (사실, [H|T]
목록 표현에서 손으로하는 것은 정확히 동일한 바이트 수에 해당합니다). 여기서 중요한 통찰력 목록이 정렬되어있는 경우 꼬리가 정렬되어있는 경우 경우 서면 검사와 같은 프로그램이, 볼 수 있다는 것입니다 그것의 꼬리 정렬 등입니다; 중복 검사가 많이 있으며이를 활용할 수 있습니다. 대신, 처음 두 요소가 순서대로 정렬되어 있는지 확인합니다 (목록 자체와 모든 접미사를 확인하면 목록이 정렬되도록합니다).
첫 번째 요소는 쉽게 접근 할 수 있습니다. 그것은 단지 목록의 머리 일뿐 H
입니다. 그러나 두 번째 요소는 액세스하기가 다소 어렵고 존재하지 않을 수 있습니다. 운 좋게도 x
(Prolog의 일반 비교 연산자를 통해) 우리가 고려하고있는 모든 튜플보다 적기 @>=
때문에 싱글 톤 목록의 "두 번째 요소"를 고려할 수 x
있으며 프로그램은 정상적으로 작동합니다. 실제로 번째 요소 액세스 같이 tersest 방법으로 세 번째 인자 (아웃 인수)를 추가하는 것 b
, 즉 복귀 x
베이스 경우와 H
재귀 경우; 이것은에 대한 두 번째 재귀 호출의 출력으로 꼬리의 머리를 잡을 수 B
있으며 물론 꼬리의 머리는 목록의 두 번째 요소입니다. 이제 b
다음과 같이 보입니다.
b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
기본 사례는 충분히 간단합니다 (빈 목록, 카운트 0을 리턴하고 빈 목록의 "첫 번째 요소"는 x
). 재귀 경우는 이전과 같은 방법으로 밖으로 시작 (단지로 T/H
표기보다는를 [H|T]
하고, H
인수 밖으로 추가로); 우리는 머리에 대한 재귀 호출의 추가 인수를 무시하지만 J
꼬리의 재귀 호출에 저장합니다 . 그럼 우리가 할 일은이 보장되는 H
보다 큰거나 같은 J
목록 종료까지 정렬되도록하기 위해 (즉, "목록이 두 개 이상의 요소가있는 경우, 먼저 두 번째 같거나보다 큰 경우).
불행히도, 이 새로운 정의와 함께 setof
이전 정의를 사용하려고 시도하면 사용 하지 않는 매개 변수가 SQL과 거의 같은 방식으로 처리되므로 적합하지 않습니다. 원하는 것을 수행하도록 재구성 할 수는 있지만 재구성에는 문자가 필요합니다. 대신, 우리 는보다 편리한 기본 동작을 가지고 있으며 두 글자 만 더 길어 다음과 같은 정의를 제공합니다 .c
b
GROUP BY
findall
c
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).
그리고 그것은 완전한 프로그램입니다. 버블 패턴을 생성 한 다음, 바이트를 계산하는 데 전체 바이트를 소비합니다 ( findall
생성기를리스트로 변환 하는 데 다소 시간이 걸리고 , 불행히도 length
리스트의 길이와 함수 선언을위한 상용구를 확인하기 위해 이름 이 불명확합니다 ).