Saya menganalisis kode Anda di bagian Menganalisis kode Anda . Sebelum itu saya menyajikan beberapa bagian menyenangkan dari materi bonus.
Satu liner Satu huruf 1
say e; # 2.718281828459045
Klik tautan di atas untuk melihat artikel luar biasa Damian Conway tentang komputasi e
di Raku.
Artikel ini sangat menyenangkan (lagipula Damian). Ini adalah diskusi komputasi yang sangat dimengerti e
. Dan itu adalah penghormatan terhadap reinkarnasi bikarbonat Raku terhadap filosofi TIMTOWTDI yang didukung oleh Larry Wall. 3
Sebagai hidangan pembuka, inilah kutipan dari sekitar setengah artikel:
Mengingat bahwa metode yang efisien ini semuanya bekerja dengan cara yang sama — dengan menjumlahkan (bagian awal dari) serangkaian istilah yang tak terbatas — mungkin akan lebih baik jika kita memiliki fungsi untuk melakukan itu untuk kita. Dan tentu saja akan lebih baik jika fungsi tersebut dapat bekerja dengan sendirinya persis berapa banyak dari subset awal dari seri yang sebenarnya perlu dimasukkan untuk menghasilkan jawaban yang akurat ... daripada mengharuskan kita untuk secara manual menyisir hasil dari beberapa percobaan untuk menemukan itu.
Dan, seperti yang sering terjadi di Raku, sangat mudah untuk membangun apa yang kita butuhkan:
sub Σ (Unary $block --> Numeric) {
(0..∞).map($block).produce(&[+]).&converge
}
Menganalisis kode Anda
Inilah baris pertama, menghasilkan seri:
my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
Penutupan ( { code goes here }
) menghitung suatu istilah. Penutupan memiliki tanda tangan, baik implisit atau eksplisit, yang menentukan berapa banyak argumen yang akan diterima. Dalam hal ini tidak ada tanda tangan eksplisit. Penggunaan $_
( variabel "topik" ) menghasilkan tanda tangan implisit yang membutuhkan satu argumen yang terikat $_
.
Operator urutan ( ...
) berulang kali memanggil penutupan di sebelah kirinya, melewati istilah sebelumnya sebagai argumen penutupan, untuk dengan malas membangun serangkaian istilah sampai titik akhir di sebelah kanannya, yang dalam hal ini adalah *
, singkatan untuk Inf
alias tak terhingga.
Topik dalam panggilan pertama ke penutupan adalah 1
. Jadi penutupan menghitung dan mengembalikan 1 / (1 * 1)
menghasilkan dua istilah pertama dalam seri sebagai 1, 1/1
.
Topik dalam panggilan kedua adalah nilai dari panggilan sebelumnya 1/1
, yaitu 1
sekali lagi. Jadi penutupan menghitung dan mengembalikan 1 / (1 * 2)
, memperluas seri ke 1, 1/1, 1/2
. Semuanya terlihat bagus.
Penutupan selanjutnya menghitung 1 / (1/2 * 3)
yaitu 0.666667
. Istilah itu seharusnya 1 / (1 * 2 * 3)
. Ups.
Membuat kode Anda sesuai dengan formula
Kode Anda seharusnya cocok dengan rumus:
Dalam rumus ini, setiap istilah dihitung berdasarkan posisinya dalam rangkaian. Istilah k th dalam seri (di mana k = 0 untuk yang pertama 1
) hanyalah faktorial k timbal balik.
(Jadi itu tidak ada hubungannya dengan nilai dari istilah sebelumnya. Dengan demikian $_
, yang menerima nilai dari istilah sebelumnya, tidak boleh digunakan dalam penutupan.)
Mari kita buat operator postfix faktorial:
sub postfix:<!> (\k) { [×] 1 .. k }
( ×
Adalah operator multiplikasi infiks, alias Unicode yang tampak lebih bagus dari infiks ASCII biasa *
.)
Itu singkatan untuk:
sub postfix:<!> (\k) { 1 × 2 × 3 × .... × k }
(Saya telah menggunakan notasi metasyntactic pseudo di dalam kurung kurawal untuk menyatakan gagasan menambah atau mengurangi sebanyak istilah yang diperlukan.
Lebih umum, menempatkan operator infiks op
dalam tanda kurung siku pada awal ekspresi membentuk operator awalan komposit yang setara dengan reduce with => &[op],
. Lihat Pengurangan metaoperator untuk info lebih lanjut.
Sekarang kita dapat menulis ulang penutupan untuk menggunakan operator postfix faktorial baru:
my @e = 1, { state $a=1; 1 / $a++! } ... *;
Bingo. Ini menghasilkan seri yang tepat.
... sampai tidak, untuk alasan yang berbeda. Masalah selanjutnya adalah akurasi numerik. Tapi mari kita hadapi itu di bagian selanjutnya.
Satu liner berasal dari kode Anda
Mungkin kompres ketiga garis menjadi satu:
say [+] .[^10] given 1, { 1 / [×] 1 .. ++$ } ... Inf
.[^10]
berlaku untuk topik, yang diatur oleh given
. ( ^10
adalah singkatan 0..9
, jadi kode di atas menghitung jumlah dari sepuluh istilah pertama dalam seri.)
Saya telah menghilangkan $a
dari penutupan komputasi istilah berikutnya. Satu-satunya $
adalah sama dengan (state $)
, skalar keadaan anonynous. Saya membuat pra-increment bukan pasca-kenaikan untuk mencapai efek yang sama seperti yang Anda lakukan dengan menginisialisasi $a
untuk 1
.
Kami sekarang memiliki masalah (besar!) Final, yang ditunjukkan oleh Anda dalam komentar di bawah.
Asalkan operandnya tidak merupakan Num
(float, dan dengan demikian perkiraan), /
operator biasanya mengembalikan 100% akurat Rat
(rasional presisi terbatas). Tetapi jika penyebut dari hasil melebihi 64 bit maka hasil itu dikonversi ke a Num
- yang memperdagangkan kinerja untuk akurasi, tradeoff yang tidak ingin kita buat. Kita perlu memperhitungkannya.
Untuk menentukan presisi tak terbatas serta akurasi 100%, cukup paksakan operasi untuk menggunakan FatRat
s. Untuk melakukan ini dengan benar, buat saja (setidaknya) salah satu operan menjadi FatRat
(dan tidak ada yang lain menjadi Num
):
say [+] .[^500] given 1, { 1.FatRat / [×] 1 .. ++$ } ... Inf
Saya telah memverifikasi ini hingga 500 digit desimal. Saya berharap itu tetap akurat sampai program crash karena melebihi batas bahasa Raku atau kompilator Rakudo. (Lihat jawaban saya untuk Tidak dapat membuka kotak bigint 65536 bit ke dalam integer asli untuk beberapa diskusi tentang itu.)
Catatan kaki
1 Raku memiliki beberapa konstanta matematika penting dibangun, termasuk e
, i
dan pi
(dan alias-nya π
). Dengan demikian orang dapat menulis Identitas Euler dalam Raku seperti yang terlihat di buku matematika. Dengan kredit ke entri Raku RosettaCode untuk Identitas Euler :
# There's an invisible character between <> and iπ character pairs!
sub infix:<> (\left, \right) is tighter(&infix:<**>) { left * right };
# Raku doesn't have built in symbolic math so use approximate equal
say e**iπ + 1 ≅ 0; # True
2 Artikel Damian harus dibaca. Tapi itu hanya salah satu dari beberapa perawatan mengagumkan yang ada di antara 100+ kecocokan untuk google untuk 'raku "nomor euler"' .
3 Lihat TIMTOWTDI vs TSBO-APOO-OWTDI untuk salah satu pandangan TIMTOWTDI yang lebih seimbang yang ditulis oleh penggemar python. Tapi ada yang downsides untuk mengambil TIMTOWTDI terlalu jauh. Untuk merefleksikan "bahaya" yang terakhir ini, komunitas Perl menciptakan TIMTOWTDIBSCINABTE yang panjang, penuh humor, dan tidak dimengerti - Ada Lebih Dari Satu Cara Untuk Melakukannya Tetapi Terkadang Konsistensi Bukanlah Hal yang Buruk, diucapkan "Tim Toady Bicarbonate". Anehnya , Larry menerapkan bikarbonat pada desain Raku dan Damian menerapkannya pada komputasi e
di Raku.