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ใช้(มีชื่อที่สั้นกว่าและให้ผลลัพธ์ที่เหมือนกันในกรณีนี้) และใช้แทน(เพราะไม่ใช่ฟังก์ชันจริง ฉันทำมันขึ้นมา):setoflistofsort0(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)))
นี่เป็นการยากที่จะอ่านมากกว่ารายการที่ซ้อนอยู่ด้านบน ใจข้ามxs และตีความ/()เป็นฟอง (หรือเพียงแค่ธรรมดา/เป็นฟองเสื่อมสภาพที่ไม่มีเนื้อหาถ้าไม่มี()หลังจากนั้น) และองค์ประกอบมีการติดต่อ 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มาก