Prolog ของ 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 จริงผ่านการอธิบายปัญหาแทนที่จะใช้อัลกอริทึมในการแก้ปัญหา: มันระบุสิ่งที่นับว่าเป็นการจัดเรียงตามกฎหมายฟองถาม Prolog เพื่อสร้างการจัดเรียงฟองเหล่านั้นทั้งหมดแล้วนับพวกเขา การสร้างใช้เวลา 55 ตัวอักษร (สองบรรทัดแรกของโปรแกรม) การนับและ I / O ใช้อีก 43 (บรรทัดที่สามและบรรทัดใหม่ที่แยกสองส่วน) ฉันเดิมพันว่านี่ไม่ใช่ปัญหาที่ OP คาดว่าจะทำให้ภาษาต้องต่อสู้กับ I / O! (หมายเหตุ: การเน้นไวยากรณ์ของ Stack Exchange ทำให้การอ่านยากขึ้นไม่ใช่เรื่องง่ายดังนั้นฉันจึงปิด)
คำอธิบาย
เริ่มด้วยโปรแกรม pseudocode รุ่นที่คล้ายกันซึ่งใช้งานไม่ได้:
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
ทำงาน: เราแสดงฟองผ่านรายการที่เรียงลำดับ (ซึ่งเป็นการนำ multisets ไปใช้งานง่ายซึ่งทำให้ multisets เท่ากันเพื่อเปรียบเทียบเท่ากัน) และฟองเดียว[]
มีจำนวน 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 อารัมภบทของ#=
ความสามารถในการจัดการการจัดเรียงของเลขคณิต noncausal นั้นและเพราะมันเป็นบรรทัดแรกมากb
และHeadCount
และTailCount
ทั้งสองจะต้องน้อยกว่าCount
(ซึ่งเป็นที่รู้จักกัน) มันทำหน้าที่เป็นวิธีการ จำกัด โดยธรรมชาติกี่ครั้งที่คำ recursive สามารถจับคู่ได้และทำให้โปรแกรมหยุดทำงานเสมอ
ขั้นต่อไปคือการตีกอล์ฟลงเล็กน้อย การลบช่องว่างออกโดยใช้ชื่อตัวแปรอักขระเดียวโดยใช้ตัวย่อเช่น:-
for if
และ,
for แทนที่จะ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]
จริง verbose เป็นรายการไวยากรณ์ไป ดังที่โปรแกรมเมอร์ Lisp รู้แล้วรายชื่อนั้นทำจากเซลล์ข้อเสียซึ่งโดยทั่วไปเป็นเพียงสิ่งอันดับและแทบจะทุกส่วนของโปรแกรมนี้ใช้ลิสต์ในตัว Prolog มีซินแท็กซ์ tuple สั้น ๆ หลายตัว (สิ่งที่ฉันชอบคือA-B
, แต่สิ่งที่ฉันชอบอันดับสองคือA/B
, ซึ่งฉันใช้ที่นี่เพราะมันให้ผลลัพธ์การดีบักที่อ่านง่ายขึ้นในกรณีนี้) และเรายังสามารถเลือกอักขระเดี่ยวของเราเองnil
สำหรับตอนท้ายของรายการแทนที่จะติดกับอักขระสองตัว[]
(ฉันเลือกx
แต่โดยพื้นฐานแล้วอะไรก็ได้ที่ใช้ได้) ดังนั้นแทนที่จะ[H|T]
เราสามารถใช้T/H
และรับผลลัพธ์จากb
ที่มีลักษณะเช่นนี้ (โปรดทราบว่าลำดับการเรียงใน tuples นั้นแตกต่างจากรายการในรายการเล็กน้อยดังนั้นสิ่งเหล่านี้จะไม่อยู่ในลำดับเดียวกันกับด้านบน):
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 และตีความ/()
เป็นฟอง (หรือเพียงแค่ธรรมดา/
เป็นฟองเสื่อมสภาพที่ไม่มีเนื้อหาถ้าไม่มี()
หลังจากนั้น) และองค์ประกอบมีการติดต่อ 1 ต่อ 1 (ถ้ายุ่งเหยิง) กับรายการรุ่นที่แสดงข้างต้น .
แน่นอนว่าการเป็นตัวแทนรายการนี้แม้จะสั้นกว่ามาก แต่ก็มีข้อเสียเปรียบครั้งใหญ่ มันไม่ได้สร้างขึ้นในภาษาดังนั้นเราจึงไม่สามารถใช้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
เราไม่ได้ใช้ถ้าเราพยายามที่จะใช้คำจำกัดความก่อนหน้านี้c
พร้อมกับคำจำกัดความใหม่ของb
มันเพราะมันปฏิบัติต่อพารามิเตอร์ที่ไม่ได้ใช้ในแบบเดียวกับ SQL GROUP BY
ซึ่งมากกว่าหรือน้อยกว่าที่เราต้องการ เป็นไปได้ที่จะกำหนดค่าใหม่เพื่อทำสิ่งที่เราต้องการ แต่ตัวละครนั้นต้องเสียค่าใช้จ่าย แต่เราใช้findall
ซึ่งมีพฤติกรรมเริ่มต้นที่สะดวกกว่าและมีความยาวเพียงสองตัวอักษรเท่านั้นทำให้เราได้นิยามc
:
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).
และนั่นคือโปรแกรมที่สมบูรณ์ สร้างรูปแบบบับเบิ้ลให้แน่นขึ้นจากนั้นใช้จำนวนไบต์ทั้งหมดในการนับ (เราต้องใช้เวลาค่อนข้างนานfindall
ในการแปลงเครื่องกำเนิดไฟฟ้าไปยังรายการจากนั้นมีชื่อที่โชคร้ายlength
มาก