Golfscript - 12 karakter
{,1\{)*}/}:f
Memulai dengan Golfscript - Faktorial selangkah demi selangkah
Ini ada sesuatu untuk orang-orang yang mencoba belajar skrip golf. Prasyarat adalah pemahaman dasar tentang skrip golf, dan kemampuan untuk membaca dokumentasi skrip golf.
Jadi kami ingin mencoba skrip golf alat baru kami . Itu selalu baik untuk memulai dengan sesuatu yang sederhana, jadi kita mulai dengan faktorial. Berikut ini adalah upaya awal, berdasarkan kodesemu imperatif sederhana:
# pseudocode: f(n){c=1;while(n>1){c*=n;n--};return c}
{:n;1:c;{n 1>}{n c*:c;n 1-:n;}while c}:f
Spasi sangat jarang digunakan dalam skrip golf. Trik termudah untuk menyingkirkan spasi putih adalah dengan menggunakan nama variabel yang berbeda. Setiap token dapat digunakan sebagai variabel (lihat halaman sintaks ). Token berguna untuk digunakan sebagai variabel karakter khusus seperti |
, &
, ?
- umumnya apa pun tidak digunakan di tempat lain dalam kode. Ini selalu diuraikan sebagai token karakter tunggal. Sebaliknya, variabel seperti n
akan membutuhkan ruang untuk mendorong nomor ke tumpukan setelahnya. Angka pada dasarnya adalah variabel yang diinisialisasi.
Seperti biasa, akan ada pernyataan yang bisa kita ubah, tanpa mempengaruhi hasil akhirnya. Dalam golfscript, semuanya mengevaluasi untuk benar kecuali 0
, []
, ""
, dan {}
(lihat ini ). Di sini, kita dapat mengubah kondisi keluar loop menjadi sederhana {n}
(kita loop waktu tambahan, dan berakhir ketika n = 0).
Seperti halnya bermain golf bahasa apa pun, ada baiknya mengetahui fungsi yang tersedia. Untungnya daftar ini sangat pendek untuk skrip golf. Kita dapat mengubah 1-
untuk (
menyelamatkan karakter lain. Saat ini kodenya terlihat seperti ini: (kita bisa menggunakan 1
alih-alih di |
sini jika kita mau, yang akan menghilangkan inisialisasi.)
{:n;1:|;{n}{n|*:|;n(:n;}while|}:f
Penting untuk menggunakan tumpukan dengan baik untuk mendapatkan solusi terpendek (latihan praktik praktik). Secara umum, jika nilai hanya digunakan dalam segmen kode yang kecil, mungkin tidak perlu menyimpannya ke dalam variabel. Dengan menghapus variabel produk yang sedang berjalan dan hanya menggunakan tumpukan, kita dapat menyimpan cukup banyak karakter.
{:n;1{n}{n*n(:n;}while}:f
Ada hal lain untuk dipikirkan. Kami menghapus variabel n
dari tumpukan di akhir badan loop, tetapi kemudian mendorongnya segera setelah itu. Bahkan, sebelum loop dimulai kami juga menghapusnya dari stack. Kita seharusnya membiarkannya di stack, dan kita bisa menjaga kondisi loop kosong.
{1\:n{}{n*n(:n}while}:f
Mungkin kita bahkan bisa menghilangkan variabel sepenuhnya. Untuk melakukan ini, kita harus menyimpan variabel di stack setiap saat. Ini berarti bahwa kita memerlukan dua salinan variabel pada tumpukan di akhir pemeriksaan kondisi sehingga kita tidak kehilangannya setelah pemeriksaan. Yang berarti bahwa kita akan memiliki redundan 0
pada stack setelah loop berakhir, tetapi itu mudah diperbaiki.
Ini membawa kita ke while
solusi loop optimal kami !
{1\{.}{.@*\(}while;}:f
Sekarang kami masih ingin membuat ini lebih pendek. Target yang jelas harus menjadi kata while
. Melihat dokumentasi, ada dua alternatif yang layak - buka dan lakukan . Ketika Anda memiliki pilihan rute yang berbeda untuk diambil, coba dan timbang manfaat keduanya. Buka lipatan adalah 'cukup banyak loop sementara', sehingga sebagai perkiraan kami akan memotong 5 karakter while
dengan 4 menjadi /
. Adapun do
, kami memotong while
3 karakter, dan bisa menggabungkan dua blok, yang mungkin menyimpan satu atau dua karakter lain.
Sebenarnya ada kelemahan besar untuk menggunakan do
loop. Karena pemeriksaan kondisi dilakukan setelah tubuh dieksekusi sekali, nilai 0
akan salah, jadi kita mungkin perlu pernyataan if. Saya akan memberi tahu Anda sekarang bahwa lipatan lebih singkat (beberapa solusi dengan do
disediakan di akhir). Silakan dan coba, kode yang sudah kita miliki membutuhkan perubahan minimal.
{1\{}{.@*\(}/;}:f
Bagus! Solusi kami sekarang sangat singkat dan kami selesai di sini, kan? Nggak. Ini adalah 17 karakter, dan J memiliki 12 karakter. Jangan pernah mengakui kekalahan!
Sekarang Anda berpikir dengan ... rekursi
Menggunakan rekursi berarti kita harus menggunakan struktur percabangan. Sayangnya, tetapi karena faktorial dapat diekspresikan dengan ringkas secara rekursif, ini sepertinya merupakan alternatif yang layak untuk iterasi.
# pseudocode: f(n){return n==0?n*f(n-1):1}
{:n{n.(f*}1if}:f # taking advantage of the tokeniser
Yah itu mudah - seandainya kita mencoba rekursi sebelumnya kita mungkin bahkan tidak pernah melihat menggunakan while
loop Namun, kami hanya memiliki 16 karakter.
Array
Array umumnya dibuat dalam dua cara - menggunakan karakter [
dan ]
, atau dengan ,
fungsi. Jika dieksekusi dengan integer di bagian atas tumpukan, ,
mengembalikan array dengan panjang itu dengan arr [i] = i.
Untuk iterating over array, kami memiliki tiga opsi:
{block}/
: push, block, push, block, ...
{block}%
: [push, block, push, block, ...] (ini memiliki beberapa nuansa, misalnya nilai menengah dihapus dari tumpukan sebelum setiap dorongan)
{block}*
: push, push, block, push, block, ...
Dokumentasi skrip golf memiliki contoh penggunaan {+}*
untuk menjumlahkan isi dari suatu array. Ini menyarankan kita dapat menggunakan {*}*
untuk mendapatkan produk dari array.
{,{*}*}:f
Sayangnya, itu tidak sesederhana itu. Semua elemen dimatikan oleh satu ( [0 1 2]
bukan [1 2 3]
). Kita dapat menggunakan {)}%
untuk memperbaiki masalah ini.
{,{)}%{*}*}:f
Ya tidak cukup. Ini tidak menangani nol dengan benar. Kita dapat menghitung (n +1)! / (N +1) untuk memperbaiki ini, meskipun ini biayanya terlalu banyak.
{).,{)}%{*}*\/}:f
Kami juga dapat mencoba menangani n = 0 dalam keranjang yang sama dengan n = 1. Ini sebenarnya sangat singkat untuk dilakukan, coba dan kerjakan sesingkat mungkin.
Tidak begitu baik adalah menyortir, pada 7 karakter: [1\]$1=
. Perhatikan bahwa teknik penyortiran ini memang memiliki tujuan yang bermanfaat, seperti memaksakan batasan pada angka (mis. `[0 \ 100] $ 1 =)
Inilah pemenangnya, dengan hanya 3 karakter:. +
Jika kita ingin memiliki kenaikan dan perkalian di blok yang sama, kita harus mengulangi setiap elemen dalam array. Karena kita tidak membangun array, ini berarti kita harus menggunakan {)*}/
, yang membawa kita ke implementasi skrip golf terpendek dari faktorial! Panjangnya 12 karakter, ini terkait dengan J!
{,1\{)*}/}:f
Solusi bonus
Dimulai dengan if
solusi langsung untuk satu do
loop:
{.{1\{.@*\(.}do;}{)}if}:f
Kita bisa memeras beberapa tambahan dari ini. Agak rumit, jadi Anda harus meyakinkan diri sendiri bahwa ini berhasil. Pastikan Anda memahami semua ini.
{1\.!!{{.@*\(.}do}*+}:f
{.!{1\{.@*\(.}do}or+}:f
{.{1\{.@*\(.}do}1if+}:f
Alternatif yang lebih baik adalah menghitung (n +1)! / (N +1), yang menghilangkan kebutuhan akan suatu if
struktur.
{).1\{.@*\(.}do;\/}:f
Tetapi do
solusi terpendek di sini membutuhkan beberapa karakter untuk memetakan 0 ke 1, dan yang lainnya untuk dirinya sendiri - jadi kita tidak perlu bercabang. Optimalisasi semacam ini sangat mudah untuk dilewatkan.
{.!+1\{.@*\(.}do;}:f
Bagi siapa pun yang tertarik, beberapa solusi rekursif alternatif dengan panjang yang sama seperti di atas disediakan di sini:
{.!{.)f*0}or+}:f
{.{.)f*0}1if+}:f
{.{.(f*}{)}if}:f
* catatan: Saya belum benar-benar menguji banyak bagian kode dalam posting ini, jadi jangan ragu untuk memberi tahu jika ada kesalahan.