ऐलिस , 38 36 बाइट्स
2 बाइट बचाने के लिए लियो को धन्यवाद।
/ow;B1dt&w;31J
\i@/01dt,t&w.2,+k;d&+
इसे ऑनलाइन आज़माएं!
लगभग निश्चित रूप से इष्टतम नहीं है। नियंत्रण प्रवाह काफी विस्तृत है और जब मैं पिछले संस्करणों में सहेजे गए कितने बाइट्स के साथ काफी खुश हूं, तो मुझे लगता है कि मैं उन चीजों को ओवरक्लॉक कर रहा हूं जो एक सरल और छोटा समाधान हो सकता है ।
व्याख्या
सबसे पहले, मुझे ऐलिस के रिटर्न एड्रेस स्टैक (आरएएस) पर थोड़ा विस्तार करने की आवश्यकता है। कई अन्य fungeoids की तरह, ऐलिस के पास कोड में चारों ओर कूदने की आज्ञा है। हालाँकि, इसमें यह भी आज्ञा है कि आप जहाँ से आए हैं, वहाँ लौटने में मदद करता है, जिससे आप सबरूटीन्स को बहुत आसानी से लागू कर सकते हैं। बेशक, यह एक 2 डी भाषा होने के नाते, सबरूटीन वास्तव में केवल सम्मेलन द्वारा मौजूद हैं। रिटर्न कमांड (या सबरूटीन में किसी भी बिंदु पर) के माध्यम से अन्य साधनों के माध्यम से एक सबरूटीन में प्रवेश करने या छोड़ने से आपको कोई रोक नहीं सकता है, और आप आरएएस का उपयोग कैसे करते हैं इसके आधार पर, वैसे भी एक साफ कूद / वापसी पदानुक्रम नहीं हो सकता है।
सामान्य तौर पर, यह कूदने j
से पहले आरएएस के लिए वर्तमान आईपी पते को जम्प कमांड के द्वारा कार्यान्वित किया जाता है । वापसी आदेश k
तब RAS का एक पता पॉप करता है और वहां कूदता है। यदि RAS खाली है, k
तो कुछ भी नहीं करता है।
आरएएस में हेरफेर करने के अन्य तरीके भी हैं। इस कार्यक्रम के लिए इनमें से दो प्रासंगिक हैं:
w
आरएएस में कहीं भी कूदने के बिना वर्तमान आईपी पते को धक्का देता है । यदि आप इस आदेश को दोहराते हैं, तो आप सरल छोरों को बहुत आसानी से लिख सकते हैं &w...k
, जो मैंने पिछले उत्तरों में पहले ही किया है।
J
की तरह है, j
लेकिन RAS पर वर्तमान IP पता याद नहीं है।
यह भी ध्यान रखना महत्वपूर्ण है कि आरएएस आईपी की दिशा के बारे में कोई जानकारी नहीं रखता है । इसलिए एक पते पर लौटने से k
हमेशा वर्तमान आईपी दिशा (और इसलिए भी कि क्या हम कार्डिनल या साधारण मोड में हैं) की रक्षा करेंगे, भले ही हम पहले से कैसे गुजरे j
या w
उस आईपी पते को धक्का दिया हो।
उस रास्ते के साथ, चलो उपर्युक्त कार्यक्रम में सबरूटीन को देखकर शुरू करें:
01dt,t&w.2,+k
यह सबरूटीन स्टैक के निचले तत्व को खींचता है, n , शीर्ष पर और फिर फाइबोनैचि संख्या F (n) और F (n + 1) की गणना करता है (उन्हें स्टैक के शीर्ष पर छोड़ता है)। हमें कभी भी एफ (एन + 1) की आवश्यकता नहीं होती है , लेकिन &w...k
आरएएस के साथ बातचीत कैसे करते हैं (जो इन छोरों को एक सबरूटीन के अंत में होने की आवश्यकता है) के कारण यह सबरूटीन के बाहर छोड़ दिया जाएगा । हम शीर्ष के बजाय नीचे से तत्व ले रहे हैं इसका कारण यह है कि इससे हम स्टैक को एक कतार की तरह अधिक व्यवहार कर सकते हैं, जिसका अर्थ है कि हम सभी फाइबोनैचि संख्याओं की गणना एक ही बार में कर सकते हैं, उन्हें कहीं और स्टोर किए बिना।
यहां बताया गया है कि यह सबरूटीन कैसे काम करता है:
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.
लूप का अंत थोड़ा मुश्किल है। जब तक स्टैक पर 'डब्ल्यू' पते की एक प्रति है, तब तक यह अगला चलना शुरू करता है। एक बार जब वे समाप्त हो जाते हैं, तो परिणाम इस बात पर निर्भर करता है कि सबरूटीन कैसे लागू किया गया था। यदि सबरूटिन को 'जे' के साथ बुलाया जाता है, तो अंतिम 'के' वहां लौटता है, इसलिए लूप एंड सबरूटीन की वापसी के रूप में दोगुना हो जाता है। यदि सबरूटीन को 'जे' के साथ बुलाया गया था, और स्टैक पर पहले से एक पता अभी भी है, तो हम वहां कूदते हैं। इसका मतलब है कि अगर सबरूटीन को बाहरी लूप में बुलाया जाता है, तो यह 'k' उस बाहरी लूप की शुरुआत में लौटता है । यदि सबरूटिन को 'J' के साथ बुलाया गया था, लेकिन RAS अभी खाली है, तो यह 'k' कुछ नहीं करता है और IP केवल लूप के बाद आगे बढ़ता रहता है। हम प्रोग्राम में इन तीनों मामलों का उपयोग करेंगे।
अंत में, कार्यक्रम पर ही।
/o....
\i@...
दशमलव पूर्णांकों को पढ़ने और मुद्रित करने के लिए ये साधारण रूप से साधारण यात्रा में केवल दो त्वरित हैं।
इसके बाद i
, w
दूसरा होने के कारण, जो सबरूटीन में जाने से पहले वर्तमान स्थिति को याद करता है /
। यह सबरूटीन गणना F(n)
और F(n+1)
इनपुट पर पहला आह्वान है n
। बाद में हम यहां वापस आ गए, लेकिन हम अब पूर्व की ओर बढ़ रहे हैं, इसलिए कार्डिनल मोड में कार्यक्रम के शेष ऑपरेटर। मुख्य कार्यक्रम इस तरह दिखता है:
;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.