*(>:^]*(*>{<-!<:^>[:((-<)<(<!-)>>-_)_<<]>:]<]]}*<)]*(:)*=<*)>]
จำเป็นต้องรันด้วย-ln
แฟล็กบรรทัดคำสั่ง (ด้วยเหตุนี้จึงมีความยาว +4 ไบต์) พิมพ์0
สำหรับหมายเลขคอมโพสิตและ1
สำหรับช่วงเวลา
ลองออนไลน์!
ฉันคิดว่านี่เป็นโปรแกรม Stack Cats ที่ไม่ใช่เรื่องแรก
คำอธิบาย
การแนะนำ Stack Stack ด่วน:
- Stack Cats ทำงานบนเทปที่ไม่มีที่สิ้นสุดของสแต็กโดยมีหัวเทปที่ชี้ไปที่สแต็กปัจจุบัน สแต็กทุกครั้งจะถูกเติมด้วยจำนวนศูนย์ที่ไม่ จำกัด ฉันมักจะไม่สนใจค่าศูนย์เหล่านี้ในถ้อยคำของฉันดังนั้นเมื่อฉันพูดว่า "ด้านล่างของสแต็ค" ฉันหมายถึงค่าต่ำสุดที่ไม่เป็นศูนย์และถ้าฉันบอกว่า "สแต็กว่างเปล่า" ฉันหมายความว่ามีศูนย์อยู่
- ก่อนที่โปรแกรมจะเริ่มทำงาน a
-1
จะถูกพุชไปยังสแต็กเริ่มต้นจากนั้นอินพุตทั้งหมดจะถูกผลักไปที่ด้านบนของรายการนั้น ในกรณีนี้เนื่องจาก-n
แฟล็กอินพุตจะถูกอ่านเป็นจำนวนเต็มฐานสิบ
- ในตอนท้ายของโปรแกรมสแต็กปัจจุบันจะใช้สำหรับการส่งออก หากมี
-1
ด้านล่างก็จะถูกละเว้น อีกครั้งเนื่องจากการ-n
ตั้งค่าสถานะค่าจากสแต็กจะพิมพ์เป็นจำนวนเต็มทศนิยมที่แยกบรรทัดด้วยตัวป้อนบรรทัด
- Stack Cats เป็นภาษาโปรแกรมที่สามารถย้อนกลับได้: โค้ดทุกชิ้นสามารถยกเลิกได้ (โดยไม่มี Stack Cats คอยติดตามประวัติที่ชัดเจน) โดยเฉพาะอย่างยิ่งที่จะย้อนกลับชิ้นส่วนของรหัสใด ๆ คุณก็สะท้อนมันเช่นกลายเป็น
<<(\-_)
(_-/)>>
เป้าหมายการออกแบบนี้วางข้อ จำกัด ที่ค่อนข้างรุนแรงเกี่ยวกับชนิดของตัวดำเนินการและโครงสร้างการไหลของการควบคุมที่มีอยู่ในภาษา
ยิ่งไปกว่านั้นทุกโปรแกรม Stack Cats จะต้องมีความสมมาตรด้วยตนเอง คุณอาจสังเกตเห็นว่านี่ไม่ใช่กรณีของซอร์สโค้ดด้านบน นี่คือสิ่งที่-l
ธงมีไว้สำหรับ: โดยปริยายสะท้อนรหัสไปทางซ้ายโดยใช้อักขระตัวแรกสำหรับกึ่งกลาง ดังนั้นโปรแกรมจริงคือ:
[<(*>=*(:)*[(>*{[[>[:<[>>_(_-<<(-!>)>(>-)):]<^:>!->}<*)*[^:<)*(>:^]*(*>{<-!<:^>[:((-<)<(<!-)>>-_)_<<]>:]<]]}*<)]*(:)*=<*)>]
การเขียนโปรแกรมอย่างมีประสิทธิภาพด้วยรหัสทั้งหมดนั้นไม่สำคัญและไม่ได้ใช้งานง่ายและยังไม่สามารถเข้าใจได้ว่ามนุษย์สามารถทำมันได้อย่างไร เรากำลังบังคับให้โปรแกรมดังกล่าวทำงานได้ง่ายขึ้น แต่จะไม่สามารถเข้าถึงได้ด้วยมือ โชคดีที่เราพบรูปแบบพื้นฐานซึ่งช่วยให้คุณไม่ต้องสนใจโปรแกรมครึ่งหนึ่ง ขณะนี้เป็นสิ่งที่ไม่ดีอย่างแน่นอนปัจจุบันเป็นวิธีเดียวที่รู้จักในการเขียนโปรแกรมอย่างมีประสิทธิภาพใน Stack Cats
ดังนั้นในคำตอบนี้เทมเพลตของรูปแบบที่กล่าวมานี้คือ (มีความแปรปรวนบางอย่างในวิธีการใช้งาน):
[<(...)*(...)>]
เมื่อโปรแกรมเริ่มต้นเทปสแต็กจะมีลักษณะเช่นนี้ (สำหรับอินพุต4
พูด):
4
... -1 ...
0
^
[
ย้ายด้านบนของสแต็คไปทางซ้าย (และหัวเทปพร้อม) - เราเรียกสิ่งนี้ว่า "ผลักดัน" และ<
ย้ายหัวเทปเพียงอย่างเดียว ดังนั้นหลังจากคำสั่งสองคำแรกเราก็มีสถานการณ์เช่นนี้:
... 4 -1 ...
0 0 0
^
ตอนนี้(...)
คือลูปที่สามารถใช้งานได้ง่ายตามเงื่อนไข: ลูปจะถูกป้อนและปล่อยให้ต่อเมื่อด้านบนของสแต็กปัจจุบันเป็นบวก เนื่องจากปัจจุบันเป็นศูนย์เราจึงข้ามโปรแกรมครึ่งแรกไปทั้งหมด *
ตอนนี้คำสั่งศูนย์เป็น นี่คือง่ายๆXOR 1
คือมันสลับบิตที่สำคัญน้อยที่สุดของสแต็คและในกรณีนี้จะเปลี่ยน0
เป็น1
:
... 1 4 -1 ...
0 0 0
^
(...)
ตอนนี้เราพบภาพสะท้อนของ คราวนี้ด้านบนของสแต็คเป็นบวกและเราทำใส่รหัส ก่อนที่เราจะดูว่าเกิดอะไรขึ้นในวงเล็บให้ฉันอธิบายว่าเราจะสรุปได้อย่างไร: เราต้องการให้แน่ใจว่าในตอนท้ายของบล็อกนี้เรามีหัวเทปในค่าบวกอีกครั้ง (เพื่อ ห่วงยุติหลังจากย้ำเดียวและถูกนำมาใช้เพียงเป็นเชิงเส้นเงื่อนไข) ที่สแต็คไปทางขวาถือเอาท์พุทและสแต็คขวาของที่-1
ถือ หากเป็นเช่นนั้นเราจะปล่อยให้ลูป>
เคลื่อนที่ไปยังค่าเอาต์พุตและ]
ดันเข้าไปที่-1
ดังนั้นเราจึงมีสแต็กที่สะอาดสำหรับเอาต์พุต
นั่นคือที่ ตอนนี้ในวงเล็บเราสามารถทำทุกอย่างที่เราต้องการตรวจสอบไพรเมอร์ได้ตราบใดที่เรามั่นใจว่าเราตั้งสิ่งต่าง ๆ ตามที่อธิบายไว้ในย่อหน้าก่อนหน้าในตอนท้าย (ซึ่งสามารถทำได้อย่างง่ายดายด้วยการกดและเคลื่อนหัวเทป) ครั้งแรกที่ฉันพยายามแก้ปัญหาด้วยทฤษฎีบทของ Wilsonแต่จบลงด้วยดีกว่า 100 ไบต์เนื่องจากการคำนวณแฟคทอเรียลกำลังสองนั้นค่อนข้างแพงใน Stack Cats (อย่างน้อยฉันก็ไม่ได้หาทางลัด) ดังนั้นฉันจึงไปกับแผนกทดลองแทนและมันก็ง่ายกว่ามาก ลองดูที่บิตเชิงเส้นแรก:
>:^]
คุณเห็นคำสั่งเหล่านั้นสองคำแล้ว นอกจากนี้:
แลกเปลี่ยนค่าสองค่าสูงสุดของสแต็กปัจจุบันและ^
XORs ค่าที่สองเป็นค่าสูงสุด สิ่งนี้ทำให้:^
รูปแบบทั่วไปเพื่อทำซ้ำค่าในสแต็กเปล่า (เราดึงศูนย์ที่ด้านบนของค่าแล้วเปลี่ยนค่าศูนย์ให้เป็น0 XOR x = x
) ดังนั้นหลังจากนี้หมวดเทปของเราจะเป็นดังนี้:
4
... 1 4 -1 ...
0 0 0
^
อัลกอริทึมการแบ่งรุ่นทดลองใช้ที่ฉันนำมาใช้ไม่ได้ผลสำหรับอินพุต1
ดังนั้นเราควรข้ามโค้ดในกรณีนี้ เราสามารถ map 1
ไป0
และทุกอย่างอื่นไปเป็นค่าบวกด้วย*
ดังนั้นนี่คือวิธีที่เราทำว่า
*(*...)
นั่นคือเรากลาย1
เป็น0
ข้ามส่วนใหญ่ของรหัสถ้าเราได้รับแน่นอน0
แต่ภายในเราทันทีเลิกทำ*
เพื่อให้เราได้รับค่าอินพุตของเรากลับมา เราแค่ต้องทำให้แน่ใจอีกครั้งว่าเราจบด้วยค่าบวกที่จุดสิ้นสุดของวงเล็บเพื่อที่พวกเขาจะไม่เริ่มวนซ้ำ ภายในเงื่อนไขเราย้ายหนึ่งสแต็คไปทางขวาด้วย>
แล้วเริ่มวนรอบการแบ่งการทดลองหลัก:
{<-!<:^>[:((-<)<(<!-)>>-_)_<<]>:]<]]}
การจัดฟัน (ตรงข้ามกับวงเล็บ) กำหนดการวนซ้ำที่แตกต่าง: มันเป็นการวนรอบขณะทำซึ่งหมายความว่ามันจะวนซ้ำอย่างน้อยหนึ่งครั้ง ข้อแตกต่างอื่น ๆ คือเงื่อนไขการเลิกจ้าง: เมื่อเข้าสู่ Loop Stack Cat จะจดจำค่าสูงสุดของ stack ปัจจุบัน ( 0
ในกรณีของเรา) ลูปจะทำงานจนกว่าจะเห็นค่าเดียวกันนี้อีกครั้งเมื่อสิ้นสุดการวนซ้ำ สิ่งนี้สะดวกสำหรับเรา: ในการคำนวณซ้ำแต่ละครั้งเราเพียงคำนวณส่วนที่เหลือของตัวหารที่เป็นไปได้ถัดไปและย้ายไปยังสแต็กนี้เราจะเริ่มวนรอบ เมื่อเราพบตัวหารส่วนที่เหลือคือ0
และลูปหยุด เราจะพยายามเริ่มต้นที่ตัวหารแล้วพร่องพวกเขาลงไปn-1
1
นั่นหมายถึงก) เรารู้ว่าสิ่งนี้จะยุติลงเมื่อเราไปถึง1
ที่ล่าสุดและ b) จากนั้นเราจะสามารถระบุได้ว่าเป็นจำนวนที่สำคัญหรือไม่โดยการตรวจสอบตัวหารที่ผ่านมาเราพยายาม (ถ้ามัน1
ก็เป็นสำคัญมิฉะนั้นจะไม่ได้)
ไปกันเถอะ มีส่วนเส้นตรงสั้น ๆ ที่จุดเริ่มต้น:
<-!<:^>[:
คุณรู้แล้วว่าสิ่งเหล่านี้ส่วนใหญ่ทำอะไรในตอนนี้ คำสั่งใหม่และ-
!
Stack Cats ไม่มีตัวดำเนินการเพิ่มหรือลด อย่างไรก็ตามมันมี-
(การปฏิเสธคือการคูณด้วย-1
) และ!
(bitwise NOT คือการคูณด้วย-1
และการลดลง) เหล่านี้สามารถรวมกันเป็นทั้งการเพิ่มขึ้น, หรือพร่อง!-
-!
ดังนั้นเราจึงพร่องสำเนาของn
ด้านบนของ-1
แล้วทำสำเนาของผู้อื่นในกองไปทางซ้ายแล้วดึงตัวหารพิจารณาคดีใหม่และวางไว้ใต้n
n
ดังนั้นในการทำซ้ำครั้งแรกเราจะได้รับ:
4
3
... 1 4 -1 ...
0 0 0
^
ในการทำซ้ำต่อไป3
จะถูกแทนที่ด้วยตัวหารการทดสอบต่อไปและอื่น ๆ (ในขณะที่ทั้งสองสำเนาn
จะเป็นค่าเดียวกัน ณ จุดนี้)
((-<)<(<!-)>>-_)
นี่คือการคำนวณแบบโมดูโล เนื่องจากลูปสิ้นสุดด้วยค่าบวกแนวคิดจึงเริ่มต้นจาก-n
และเพิ่มตัวหารทดลองซ้ำไปเรื่อย ๆd
จนกว่าเราจะได้รับค่าบวก เมื่อเราทำแล้วเราจะลบผลลัพธ์ออกd
และสิ่งนี้จะให้ส่วนที่เหลือ บิตที่ยุ่งยากนี่คือเราไม่สามารถใส่-n
ด้านบนของสแต็กและเริ่มการวนซ้ำที่เพิ่มd
: ถ้าด้านบนของสแต็กเป็นลบการวนซ้ำจะไม่ถูกป้อน นี่เป็นข้อ จำกัด ของภาษาการเขียนโปรแกรมที่สามารถย้อนกลับได้
ดังนั้นเพื่อหลีกเลี่ยงปัญหานี้เราจะเริ่มต้นn
จากด้านบนของสแต็ก แต่คัดค้านในการทำซ้ำครั้งแรกเท่านั้น อีกครั้งที่ฟังดูง่ายกว่าที่คิดว่าเป็น ...
(-<)
เมื่อด้านบนของสแต็คเป็นบวก (เช่นเฉพาะบนซ้ำแรก) -
เราปฏิเสธมันด้วย อย่างไรก็ตามเราไม่สามารถทำได้เพียง(-)
เพราะเราจะไม่ออกจากลูปจนกว่าจะ-
มีการใช้สองครั้ง ดังนั้นเราจึงย้ายเซลล์หนึ่งไปทางซ้าย<
เพราะเรารู้ว่ามีค่าเป็นบวกตรงนั้น (ตัว1
) โอเคดังนั้นตอนนี้เราได้คัดค้านอย่างน่าเชื่อถือn
ในการทำซ้ำครั้งแรก แต่เรามีปัญหาใหม่: หัวเทปตอนนี้อยู่ในตำแหน่งที่แตกต่างกันในการทำซ้ำครั้งแรกกว่าในทุก ๆ เราจำเป็นต้องรวบรวมสิ่งนี้ก่อนที่จะดำเนินการต่อไป ครั้งต่อไป<
จะเลื่อนหัวเทปไปทางซ้าย สถานการณ์ในการทำซ้ำครั้งแรก:
-4
3
... 1 4 -1 ...
0 0 0 0
^
และในการทำซ้ำครั้งที่สอง (โปรดจำไว้ว่าเราได้เพิ่มd
ไว้ใน-n
ตอนนี้):
-1
3
... 1 4 -1 ...
0 0 0
^
เงื่อนไขถัดไปรวมเส้นทางเหล่านี้อีกครั้ง:
(<!-)
ในการทำซ้ำครั้งแรกหัวเทปชี้ไปที่ศูนย์ดังนั้นสิ่งนี้จะถูกข้ามทั้งหมด ในการทำซ้ำเพิ่มเติมหัวเทปชี้ไปที่หนึ่งดังนั้นเราจะดำเนินการนี้ย้ายไปทางซ้ายและเพิ่มเซลล์ที่นั่น เนื่องจากเรารู้ว่าเซลล์เริ่มจากศูนย์ตอนนี้มันจะเป็นค่าบวกเสมอเพื่อให้เราสามารถออกจากลูปได้ สิ่งนี้ทำให้เรามั่นใจได้ว่าจะมีสแต็กสองตัวที่เหลืออยู่ของสแต็กหลัก>>
เสมอ -_
จากนั้นในตอนท้ายของวงโมดูโลที่เราทำ -
คุณรู้อยู่แล้วว่า _
คือการลบสิ่ง^
คือการแฮคเกอร์: ถ้าด้านบนของสแต็คเป็นa
และความคุ้มค่าภายใต้คือb
แทนที่ด้วยa
b-a
ตั้งแต่ที่เราถูกทอดทิ้งครั้งแรกa
แม้ว่าจะ-_
แทนที่a
ด้วยb+a
จึงเพิ่มd
เป็นผลรวมการดำเนินงานของเรา
หลังจากลูปสิ้นสุดลง (เรามีค่าเป็นบวก) เทปจะมีลักษณะดังนี้:
2
3
... 1 1 4 -1 ...
0 0 0 0
^
ค่าซ้ายสุดอาจเป็นจำนวนบวกใด ๆ ที่จริงแล้วมันเป็นจำนวนการทำซ้ำลบหนึ่ง มีอีกบิตเชิงเส้นสั้น ๆ ในขณะนี้:
_<<]>:]<]]
อย่างที่ฉันบอกไปก่อนหน้านี้เราจำเป็นต้องลบผลลัพธ์d
เพื่อให้ได้ส่วนที่เหลือจริง ( 3-2 = 1 = 4 % 3
) ดังนั้นเราจึงทำ_
อีกครั้ง ต่อไปเราต้องล้างสแต็กที่เราเพิ่มขึ้นทางซ้าย: เมื่อเราลองตัวหารถัดไปมันต้องเป็นศูนย์อีกครั้งเพื่อให้การวนซ้ำครั้งแรกทำงานได้ ดังนั้นเราจึงย้ายไปที่นั่นและผลักดันค่าบวกนั้นไปยังกองช่วยเหลืออื่นด้วย<<]
แล้วย้ายกลับไปยังกองปฏิบัติการของเราอีก>
อัน เราดึงขึ้นd
ด้วย:
และผลักดันมันกลับลงบน-1
ด้วยแล้วเราย้ายที่เหลือลงบนสแต็คเงื่อนไขของเราด้วย]
<]]
นั่นคือจุดสิ้นสุดของลูปการหารทดลอง: สิ่งนี้จะดำเนินต่อไปจนกว่าเราจะได้ส่วนที่เหลือเป็นศูนย์ซึ่งในกรณีนี้สแต็กทางด้านซ้ายจะมีn
ตัวหารที่ยิ่งใหญ่ที่สุด (นอกเหนือจากn
)
หลังจากลูปสิ้นสุดลง*<
ก่อนที่เราจะเข้าร่วมพา ธ ด้วยอินพุต1
อีกครั้ง *
ก็จะเปลี่ยนเป็นศูนย์1
ซึ่งเราจะต้องในบิตและจากนั้นเราย้ายไปหารด้วย<
(เพื่อว่าเราอยู่ในกองเดียวกับสำหรับการป้อนข้อมูล1
)
ณ จุดนี้มันช่วยในการเปรียบเทียบสามอินพุตที่แตกต่างกัน ครั้งแรกกรณีพิเศษn = 1
ที่เราไม่ได้ทำสิ่งใดในแผนกการทดลองนั้น:
0
... 1 1 -1 ...
0 0 0
^
จากนั้นตัวอย่างก่อนหน้าของเราn = 4
หมายเลขคอมโพสิต:
2
1 2 1
... 1 4 -1 1 ...
0 0 0 0
^
และในที่สุดก็n = 3
เป็นจำนวนเฉพาะ:
3
1 1 1
... 1 3 -1 1 ...
0 0 0 0
^
ดังนั้นสำหรับตัวเลขที่สำคัญเรามี1
ในกองนี้และสำหรับตัวเลขคอมโพสิตเราทั้งมีหรือเป็นจำนวนบวกมากกว่า0
2
เราเปลี่ยนสถานการณ์นี้ให้เป็น0
หรือ1
เราต้องการด้วยรหัสชิ้นสุดท้ายดังต่อไปนี้:
]*(:)*=<*
]
เพียงแค่กดค่านี้ไปทางขวา จากนั้น*
จะใช้ในการลดความซับซ้อนของสถานการณ์ที่มีเงื่อนไขอย่างมาก: ด้วยการสลับบิตที่สำคัญน้อยที่สุดเราหัน1
(นายก) ลง0
, 0
(คอมโพสิต) ลงในค่าบวก1
และค่าบวกอื่น ๆ ทั้งหมดจะยังคงอยู่ในเชิงบวก ตอนนี้เราแค่ต้องแยกแยะระหว่าง0
และบวก (:)
นั่นคือสิ่งที่เราจะใช้อีก หากด้านบนสุดของสแต็กคือ0
(และอินพุตเป็นไพร์ม) สิ่งนี้จะถูกข้ามไป แต่ถ้าด้านบนสุดของสแต็คเป็นบวก (และอินพุตเป็นจำนวนประกอบ) นี่จะสลับกับ1
เพื่อตอนนี้เรามี0
คอมโพสิตและ1
สำหรับช่วงเวลา - เพียงสองค่าที่แตกต่าง แน่นอนพวกเขาอยู่ตรงข้ามของสิ่งที่เราต้องการที่จะส่งออก *
แต่ที่ได้รับการแก้ไขได้อย่างง่ายดายอีกด้วย
ตอนนี้ทั้งหมดที่เหลือคือการเรียกคืนรูปแบบของกองโดยคาดว่ากรอบโดยรอบของเรา: หัวเทปเป็นค่าบวกส่งผลให้ด้านบนของสแต็คไปทางขวาและเป็นหนึ่งเดียว-1
บนสแต็คขวาของที่ นี่คือสิ่งที่=<*
มีไว้เพื่อ =
สลับยอดของทั้งสองกองที่อยู่ติดกันดังนั้นเลื่อน-1
ไปทางขวาของผลลัพธ์เช่นเพื่อป้อน4
อีกครั้ง:
2 0
1 3
... 1 4 1 -1 ...
0 0 0 0 0
^
จากนั้นเราก็เลื่อนไปทางซ้ายด้วยและเปิดที่ศูนย์เป็นหนึ่งด้วย<
*
และนั่นคือสิ่งที่
หากคุณต้องการเจาะลึกลงไปถึงวิธีการทำงานของโปรแกรมคุณสามารถใช้ประโยชน์จากตัวเลือกการดีบัก ทั้งเพิ่ม-d
ธงและใส่"
ทุกที่ที่คุณต้องการที่จะเห็นสถานะหน่วยความจำในปัจจุบันเช่นเช่นนี้หรือใช้-D
ธงที่จะได้รับการติดตามที่สมบูรณ์ของโปรแกรมทั้งหมด หรือคุณสามารถใช้EsotericIDE ของ Timwiซึ่งรวมล่าม Stack Cats พร้อมดีบักเกอร์แบบเป็นขั้นตอน