GNU Prolog, 98 bayt
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).
Bu cevap Prolog'un en basit I / O formatları ile bile nasıl mücadele edebileceğinin harika bir örneğidir. Sorunu çözecek algoritmadan ziyade problemi tanımlayarak gerçek Prolog tarzında çalışır: yasal bir kabarcık düzenlemesi olarak neyin sayılacağını belirler, Prolog'a tüm bu kabarcık düzenlemelerini oluşturmasını ister ve sonra bunları sayar. Nesil 55 karakter alır (programın ilk iki satırı). Sayma ve G / Ç diğer 43'ü alır (üçüncü satır ve iki parçayı ayıran yeni satır). Bu, OP'nin dillerin G / Ç ile mücadele etmesine neden olmasını beklediği bir sorun değil! (Not: Stack Exchange'in sözdizimi vurgulaması, okumayı zorlaştırır, kolaylaştırmaz, bu yüzden kapattım).
açıklama
Aslında işe yaramayan benzer bir programın sözde kod sürümü ile başlayalım:
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).
Nasıl b
çalıştığı oldukça açık olmalıdır : kabarcıkları sıralı listelerle temsil ediyoruz (eşit eşitlikli çoklu eşlemelerin eşit karşılaştırılmasına neden olan basit bir çoklu uygulama uygulamasıdır) ve tek bir kabarcığın []
sayısı olan daha büyük bir kabarcığın sayısı 1'dir. İçindeki toplam kabarcık sayısı artı 1'e eşittir. 4 sayısı için bu program (işe yaradıysa) aşağıdaki listeleri oluşturur:
[[],[],[],[]]
[[],[],[[]]]
[[],[[],[]]]
[[],[[[]]]]
[[[]],[[]]]
[[[],[],[]]]
[[[],[[]]]]
[[[[],[]]]]
[[[[[]]]]]
Bu program birkaç nedenden dolayı cevap olarak uygun değildir, ancak en acil olan Prolog'un aslında bir map
öngörüsünün olmamasıdır (ve yazması çok fazla bayt alacaktır). Bunun yerine programı daha çok şöyle yazarız:
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).
Buradaki diğer büyük problem, Prolog'un değerlendirme emrinin işe yaraması nedeniyle, çalıştırıldığında sonsuz bir döngüye girmesi. Bununla birlikte, sonsuz döngüyü programı biraz yeniden düzenleyerek çözebiliriz:
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).
Bu oldukça garip görünebilir - ne olduklarını bilmeden önce sayıları bir araya getiriyoruz - ama GNU Prolog #=
bu tür nedensel olmayan aritmetiği kullanabiliyor ve çünkü her şeyden önce b
gelen HeadCount
ve TailCount
her ikisi de daha küçük olmalı Count
(ki bu bilinir), özyinelemeli terimin kaç kez eşleşebileceğini doğal olarak sınırlandırma yöntemi olarak hizmet eder ve bu nedenle programın daima sonlandırılmasına neden olur.
Bir sonraki adım, bu biraz aşağı golf oynamak. Gibi kısaltmalar kullanarak, tek karakterlik değişken adları kullanarak, boşluk Çıkarma :-
için if
ve ,
için and
kullanılarak setof
yerine listof
(bir kısa adı vardır ve bu durumda aynı sonuçları üretir) ve kullanma sort0(X,X)
yerine is_sorted(X)
, çünkü ( is_sorted
aslında bir işlev değil, Ben yaptım):
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).
Bu oldukça kısa, ama daha iyisini yapmak mümkün. Anahtar kavrayış, [H|T]
liste sözdizimleri devam ettikçe gerçekten de ayrıntılı. Lisp programcılarının bileceği gibi, bir liste temelde sadece temelde tuple olan cons hücrelerinden oluşur ve bu programın neredeyse hiçbir kısmı liste yerleşiklerini kullanmaz. Prolog birkaç çok kısa tuple sözdizimine sahiptir (favorim A-B
, ancak ikinci favorim, A/B
burada kullanıyorum çünkü bu durumda okunması kolay hata ayıklama çıktısı üretiyor); ve ayrıca nil
, iki karakterle sıkışmak yerine, listenin sonunda kendi tek karakterimizi seçebiliriz []
(seçtim x
, fakat temelde her şey işe yarıyor). Öyleyse yerine [H|T]
kullanabiliriz T/H
ve çıktı alabiliriz.b
bu şöyle gözüküyor (tupl'lerdeki sıralama düzeninin listelerdekilerden biraz farklı olduğunu unutmayın, bu yüzden yukarıdakiler aynı değildir):
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)))
Bu, yukarıdaki iç içe geçmiş listelerden okumaktan daha zordur, ancak mümkündür; zihinsel olarak x
s'yi atlayın ve /()
bir kabarcık olarak yorumlayın (ya da sadece /
içeriği yoksa, içeriği olmayan, dejenere bir balon gibi düz ()
) ve öğelerin yukarıda listelenen sürüm sürümüyle bire 1 (bozuksa) yazışması var .
Elbette, bu liste gösterimi, daha kısa olmasına rağmen, büyük bir dezavantaja sahiptir; dilin içine yerleştirilmemiş sort0
olduğundan, listemizin sıralanıp sıralanmadığını kontrol etmek için kullanamayız . sort0
Yine de, oldukça ayrıntılı bir şekilde anlatılmış olsa da, elle yapmak çok büyük bir kayıp değildir (aslında, [H|T]
listeyi el ile yapmak da tam olarak aynı bayt sayısına eşittir). Burada anahtar fikir listesi sıralanır eğer kuyruğu sıralanır eğer yazılı çekler gibi program, görmek olmasıdır onun kuyruğu sıralanmış ve benzeri edilir; çok fazla gereksiz kontrol var ve bundan faydalanabiliriz. Bunun yerine, ilk iki öğenin sıralı olduğundan emin olmak için kontrol edeceğiz (listenin kendisi ve tüm sonekleri kontrol edildikten sonra listenin sıralanmasını sağlar).
İlk eleman kolayca erişilebilir; bu sadece listenin başı H
. İkinci unsur, erişmek için daha zordur, ancak mevcut olmayabilir. Neyse ki, x
(Prolog'un genelleştirilmiş karşılaştırma operatörü aracılığıyla) düşündüğümüz tüm dosyalardan daha az @>=
, bu yüzden bir singleton listesinin “ikinci öğesini” düşünebiliriz x
ve program iyi çalışır. Aslında ikinci elemana erişime gelince, en ters yöntem, temel durumda ve özyinelemeli durumda b
dönen bir üçüncü argüman (bir çıkış argümanı) eklemektir ; bu, ikinci özyinelemeli çağrıdan bir çıktısı olarak kuyruğun başını tutabileceğimiz anlamına gelir ve tabii ki kuyruğun başı listenin ikinci elemanıdır. Şimdi şuna benziyor: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.
Temel durum yeterince basit (boş liste, 0 değerini döndür, boş listenin "ilk elemanı" dır x
). Özyinelemeli dava öncekiyle aynı şekilde başlar (sadece T/H
gösterimde değil [H|T]
, H
ek bir argüman olarak); ekstra argümanı kafanın özyinelemeli çağrısından almaz, ancak bunu J
özyinelemede çağrıda saklı tutarız. O zaman tek yapmamız gereken , listenin sona erdiğinden emin olmak H
için J
(yani "listenin en az iki öğeye sahip olması durumunda, ikincisi büyük veya eşittir) olmalıdır.
Ne yazık ki, bu yeni tanımı ile birlikte setof
önceki tanımını kullanmaya çalışırsak , bir uyumsuzluk yaratır, çünkü kullanılmayan parametreleri, istediğimiz gibi olmayan bir SQL gibi aşağı yukarı aynı şekilde ele alır . İstediğimizi yapmak için yeniden yapılandırmak mümkündür, ancak bu yeniden yapılandırma karakterleri tutar. Bunun yerine, daha uygun bir varsayılan davranışa sahip olan ve yalnızca iki karakterden oluşan, bize şu tanımları veren kullanıyoruz :c
b
GROUP BY
findall
c
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).
Ve bu tam bir program; tersine kabarcık paternleri oluşturun, sonra onları saymak için tam bir bayt yükü harcayın ( findall
jeneratörü bir listeye dönüştürmek için uzun zamana ihtiyacımız var, daha sonra length
bu listenin uzunluğunu kontrol etmek için ne yazık ki sözlü olarak adlandırılmış , ayrıca bir fonksiyon bildirimi için kazan plakasını kullanacağız ).