Alice , 38 36 byte
Terima kasih kepada Leo karena telah menghemat 2 byte.
/ow;B1dt&w;31J
\i@/01dt,t&w.2,+k;d&+
Cobalah online!
Hampir dipastikan tidak optimal. Alur kontrolnya cukup rumit dan walaupun saya cukup senang dengan berapa byte yang disimpan di versi sebelumnya, saya merasa bahwa saya terlalu rumit, mungkin ada yang lebih sederhana dan lebih sederhana. lebih pendek.
Penjelasan
Pertama, saya perlu menguraikan sedikit tentang tumpukan alamat pengirim Alice (RAS). Seperti banyak fungeoids lainnya, Alice memiliki perintah untuk melompat-lompat dalam kode. Namun, ia juga memiliki perintah untuk kembali ke tempat asal Anda, yang memungkinkan Anda menerapkan subrutin dengan cukup mudah. Tentu saja, ini menjadi bahasa 2D, subrutin benar-benar hanya ada dengan konvensi. Tidak ada yang menghentikan Anda memasuki atau meninggalkan subrutin melalui cara lain selain perintah kembali (atau pada titik mana pun dalam subrutin), dan tergantung pada bagaimana Anda menggunakan RAS, bagaimanapun, mungkin tidak ada hierarki lompat / kembali yang bersih.
Secara umum, ini diimplementasikan dengan memiliki perintah lompat j
mendorong alamat IP saat ini ke RAS sebelum melompat. Perintah kembali k
kemudian muncul alamat RAS dan melompat ke sana. Jika RAS kosong, k
tidak melakukan apa-apa.
Ada juga cara lain untuk memanipulasi RAS. Dua di antaranya relevan untuk program ini:
w
mendorong alamat IP saat ini ke RAS tanpa melompat ke mana pun. Jika Anda mengulangi perintah ini, Anda dapat menulis loop sederhana dengan nyaman&w...k
, yang sudah saya lakukan di jawaban sebelumnya.
J
seperti j
tetapi tidak ingat alamat IP saat ini di RAS.
Penting juga untuk dicatat bahwa RAS tidak menyimpan informasi tentang arah IP. Jadi kembali ke alamat dengan k
akan selalu menjaga arah IP saat ini (dan karena itu juga apakah kita dalam mode Kardinal atau Ordinal) terlepas dari bagaimana kita melewati j
atauw
yang mendorong alamat IP di tempat pertama.
Dengan itu, mari kita mulai dengan melihat subrutin dalam program di atas:
01dt,t&w.2,+k
Subrutin ini menarik elemen bawah tumpukan, n , ke atas dan kemudian menghitung angka Fibonacci F (n) dan F (n +1) (meninggalkannya di atas tumpukan). Kita tidak pernah membutuhkan F (n +1) , tetapi akan dibuang di luar subrutin, karena bagaimana &w...k
loop berinteraksi dengan RAS (yang memerlukan loop ini pada akhir subrutin). Alasan kami mengambil elemen dari bawah alih-alih dari atas adalah bahwa ini memungkinkan kami memperlakukan tumpukan lebih seperti antrian, yang berarti kami dapat menghitung semua angka Fibonacci dalam sekali jalan tanpa harus menyimpannya di tempat lain.
Inilah cara kerja subrutin ini:
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.
Akhir dari loop agak rumit. Selama ada salinan alamat 'w' di stack, ini akan memulai iterasi berikutnya. Setelah habis, hasilnya tergantung pada bagaimana subrutin itu dipanggil. Jika subrutin dipanggil dengan 'j', 'k' terakhir kembali ke sana, sehingga ujung loop berlipat ganda sebagai kembalinya subrutin. Jika subrutin dipanggil dengan 'J', dan masih ada alamat dari sebelumnya di stack, kita lompat ke sana. Ini berarti jika subrutin dipanggil dalam loop luar itu sendiri, 'k' ini kembali ke awal loop luar itu . Jika subrutin dipanggil dengan 'J' tetapi RAS kosong sekarang, maka 'k' ini tidak melakukan apa-apa dan IP hanya terus bergerak setelah loop. Kami akan menggunakan ketiga kasus ini dalam program.
Akhirnya, ke program itu sendiri.
/o....
\i@...
Ini hanya dua kunjungan singkat ke mode Ordinal untuk membaca dan mencetak bilangan bulat desimal.
Setelah itu i
, ada w
yang mengingat posisi saat ini sebelum melewati subrutin, karena yang kedua /
. Doa pertama dari subrutin ini menghitung F(n)
dan memberi F(n+1)
masukan n
. Setelah itu kami melompat kembali ke sini, tapi kami bergerak ke timur sekarang, jadi sisa dari operator program dalam mode Kardinal. Program utama terlihat seperti ini:
;B1dt&w;31J;d&+
^^^
Di sini, 31J
ada panggilan lain ke subrutin dan karenanya menghitung angka Fibonacci.
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.