อลิซ , 38 36 ไบต์
ขอขอบคุณลีโอสำหรับการบันทึก 2 ไบต์
/ow;B1dt&w;31J
\i@/01dt,t&w.2,+k;d&+
ลองออนไลน์!
เกือบจะไม่เหมาะสมที่สุด ขั้นตอนการควบคุมนั้นค่อนข้างละเอียดและในขณะที่ฉันมีความสุขกับจำนวนไบต์ที่บันทึกไว้ในเวอร์ชันก่อนหน้านี้ฉันมีความรู้สึกว่าฉันเข้าใจสิ่งต่าง ๆ มากเกินไปซึ่งอาจเป็นวิธีที่ง่ายและสั้นกว่า
คำอธิบาย
ก่อนอื่นฉันต้องอธิบายอย่างละเอียดเกี่ยวกับที่อยู่ผู้ส่งคืนของอลิซ (RAS) เช่นเดียวกับ fungeoids อื่น ๆ อลิซมีคำสั่งให้กระโดดไปมาในโค้ด อย่างไรก็ตามมันยังมีคำสั่งให้กลับไปยังที่ที่คุณมาซึ่งช่วยให้คุณสามารถใช้รูทีนย่อยได้สะดวก แน่นอนว่านี่เป็นภาษา 2D รูทีนย่อยมีอยู่จริงโดยการประชุมเท่านั้น ไม่มีอะไรที่จะหยุดคุณไม่ให้เข้าหรือออกจากรูทีนย่อยผ่านวิธีการอื่นนอกเหนือจากคำสั่ง return (หรือ ณ จุดใด ๆ ในรูทีนย่อย) และขึ้นอยู่กับวิธีที่คุณใช้ RAS อาจไม่มีลำดับขั้น
โดยทั่วไปสิ่งนี้ถูกนำไปใช้โดยการให้คำสั่งข้ามj
ส่งที่อยู่ IP ปัจจุบันไปที่ RAS ก่อนที่จะกระโดด คำสั่ง return k
จะปรากฏที่อยู่ของ RAS และข้ามไปที่นั่น ถ้า RAS ว่างเปล่าk
ไม่ทำอะไรเลย
นอกจากนี้ยังมีวิธีอื่นในการปรับแต่ง RAS สองสิ่งนี้เกี่ยวข้องกับโปรแกรมนี้:
w
ผลักที่อยู่ IP ปัจจุบันไปที่ RAS โดยไม่ต้องกระโดดไปทุกที่ ถ้าคุณทำซ้ำคำสั่งนี้คุณสามารถเขียนลูปง่าย ๆ ได้อย่างสะดวก&w...k
ซึ่งฉันได้ทำไปแล้วในคำตอบที่ผ่านมา
J
เป็นเหมือนj
แต่จำไม่ได้ว่าที่อยู่ IP ปัจจุบันบน RAS
สิ่งสำคัญคือต้องทราบว่า RAS เก็บข้อมูลเกี่ยวกับทิศทางของ IP ดังนั้นการกลับไปยังที่อยู่ด้วยk
จะเป็นการรักษาทิศทาง IP ปัจจุบันเสมอ(และดังนั้นไม่ว่าเราจะอยู่ในโหมด Cardinal หรือ Ordinal) โดยไม่คำนึงถึงวิธีที่เราส่งผ่านj
หรือw
ผลัก IP แอดเดรสในครั้งแรก
ด้วยวิธีการดังกล่าวเริ่มต้นด้วยการดูในรูทีนย่อยในโปรแกรมด้านบน:
01dt,t&w.2,+k
รูทีนย่อยนี้ดึงองค์ประกอบด้านล่างของสแต็กnไปยังด้านบนแล้วคำนวณตัวเลขฟีโบนักชีF (n)และF (n + 1) (ทิ้งไว้ที่ด้านบนของสแต็ก) เราไม่จำเป็นต้องใช้F (n + 1)แต่มันจะถูกทิ้งนอกรูทีนย่อยเนื่องจาก&w...k
ลูปโต้ตอบกับ RAS อย่างไร (ซึ่งลูปเหล่านี้ต้องการลูปเหล่านี้ในตอนท้ายของรูทีนย่อย) เหตุผลที่เรารับองค์ประกอบจากด้านล่างแทนด้านบนคือสิ่งนี้ช่วยให้เราปฏิบัติกับสแต็คได้มากกว่าซึ่งหมายความว่าเราสามารถคำนวณหมายเลขฟีโบนักชีทั้งหมดได้ในครั้งเดียวโดยไม่ต้องเก็บไว้ที่อื่น
นี่คือการทำงานของรูทีนย่อยนี้:
Stack
01 Push 0 and 1, to initialise Fibonacci sequence. [n ... 0 1]
dt, Pull bottom element n to top. [... 0 1 n]
t&w Run this loop n times... [... F(i-2) F(i-1)]
. Duplicate F(i-1). [... F(i-2) F(i-1) F(i-1)]
2, Pull up F(i-2). [... F(i-1) F(i-1) F(i-2)]
+ Add them together to get F(i). [... F(i-1) F(i)]
k End of loop.
จุดสิ้นสุดของลูปนั้นค่อนข้างยุ่งยาก ตราบใดที่มีสำเนาของที่อยู่ 'w' ในสแต็กนี่จะเริ่มต้นการทำซ้ำครั้งถัดไป เมื่อสิ่งเหล่านั้นหมดลงผลลัพธ์ขึ้นอยู่กับวิธีการเรียกรูทีนย่อย หากเรียกรูทีนย่อยด้วย 'j' 'k' สุดท้ายจะส่งคืนที่นั่นดังนั้นลูปสิ้นสุดจะเพิ่มขึ้นเป็นสองเท่าเมื่อรูทีนย่อยกลับมา หากรูทีนย่อยถูกเรียกด้วย 'J' และยังคงมีที่อยู่จากสแต็กก่อนหน้าเราจะกระโดดไปที่นั่น นี่หมายความว่าหากเรียกรูทีนย่อยในลูปภายนอกตัวเอง 'k' นี้จะกลับไปที่จุดเริ่มต้นของลูปภายนอก หากรูทีนย่อยนั้นถูกเรียกด้วย 'J' แต่ RAS ว่างเปล่าในขณะนี้ 'k' นี้จะไม่ทำอะไรเลยและ IP ก็จะเคลื่อนที่ไปเรื่อย ๆ หลังจากวนรอบ เราจะใช้ทั้งสามกรณีนี้ในโปรแกรม
ในที่สุดเมื่อไปถึงโปรแกรมของตัวเอง
/o....
\i@...
สิ่งเหล่านี้เป็นเพียงการแนะนำสั้น ๆ สองอย่างในโหมด Ordinal เพื่ออ่านและพิมพ์เลขจำนวนเต็มฐานสิบ
หลังจากที่i
มีการw
ซึ่งจำตำแหน่งปัจจุบันก่อนที่จะผ่านเข้าไปใน subroutine /
เนื่องจากสอง นี้ภาวนาแรกของ subroutine คำนวณF(n)
และในการป้อนข้อมูลF(n+1)
n
หลังจากนั้นเราก็กระโดดกลับมาที่นี่ แต่ตอนนี้เรากำลังเคลื่อนไปทางตะวันออกดังนั้นส่วนที่เหลือของผู้ให้บริการโปรแกรมในโหมด Cardinal โปรแกรมหลักมีลักษณะดังนี้:
;B1dt&w;31J;d&+
^^^
นี่31J
คือการเรียกรูทีนย่อยอื่นและดังนั้นจึงคำนวณจำนวนฟีโบนักชี
Stack
[F(n) F(n+1)]
; Discard F(n+1). [F(n)]
B Push all divisors of F(n). [d_1 d_2 ... d_p]
1 Push 1. This value is arbitrary. [d_1 d_2 ... d_p 1]
The reason we need it is due to
the fact that we don't want to run
any code after our nested loops, so
the upcoming outer loop over all
divisors will *start* with ';' to
discard F(d+1). But on the first
iteration we haven't called the
subroutine yet, so we need some
dummy value we can discard.
dt&w Run this loop once for each element [d_1 d_2 ... d_p 1]
in the stack. Note that this is once OR
more than we have divisors. But since [d_i d_(i+1) ... F(d_(i-1)) F(d_(i-1)+1)]
we're treating the stack as a queue,
the last iteration will process the
first divisor for a second time.
Luckily, the first divisor is always
1 and F(1) = 1, so it doesn't matter
how often we process this one.
; Discard the dummy value on the [d_1 d_2 ... d_p]
first iteration and F(d+1) of OR
the previous divisor on subsequent [d_i d_(i+1) ... F(d_(i-1))]
iterations.
31J Call the subroutine without pushing [d_(i+1) ... F(d_i) F(d_i+1)]
the current address on the RAS.
Thereby, this doubles as our outer
loop end. As long as there's an
address left from the 'w', the end
of the subroutine will jump there
and start another iteration for the
next divisor. Once that's done, the
'k' at the end of the subroutine will
simply do nothing and we'll continue
after it.
; Discard the final F(d_i+1).
d&+ Get the stack depth D and add the top [final result]
D+2 values. Of course that's two more
than we have divisors, but the stack is
implicitly padded with zeros, so that
doesn't matter.