Ben bölümünde kodunuzu analiz kodunuzu analiz . Bundan önce bonus malzemelerin birkaç eğlenceli bölümünü sunuyorum.
Bir astar Bir harf 1
say e; # 2.718281828459045
Damian Conway'in e
Raku'da bilgi işlem hakkındaki olağanüstü makalesini görmek için yukarıdaki bağlantıyı tıklayın .
Makale çok eğlenceli (sonuçta Damian). Bu, hesaplamanın çok anlaşılır bir tartışması e
. Raku'nun Larry Wall tarafından benimsenen TIMTOWTDI felsefesinin bikarbonat reenkarnasyonu için bir saygı. 3
Meze olarak, makalenin yaklaşık yarısından bir alıntı:
Bu etkili yöntemlerin hepsinin aynı şekilde çalıştığı göz önüne alındığında - sonsuz bir terim dizisini (ilk alt kümesini) toplayarak - belki bunu bizim için yapacak bir fonksiyonumuz olsaydı daha iyi olurdu. Ve işlevin, sonuçların manuel olarak taranmasını istemek yerine, doğru bir cevap üretmek için aslında serinin ilk alt kümesinin tam olarak ne kadarını içermesi gerektiği daha iyi olurdu. bunu keşfetmek için birden fazla deneme.
Raku'da olduğu gibi, tam da ihtiyacımız olanı inşa etmek şaşırtıcı derecede kolaydır:
sub Σ (Unary $block --> Numeric) {
(0..∞).map($block).produce(&[+]).&converge
}
Kodunuzu analiz etme
İşte diziyi oluşturan ilk satır:
my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
Closure ( { code goes here }
) bir terimi hesaplar. Bir kapanışın, kaç argümanı kabul edeceğini belirleyen kapalı veya açık bir imzası vardır. Bu durumda açık bir imza yoktur. $_
( "Topic" değişkeni ) kullanımı, bağlı bir argüman gerektiren örtük bir imza ile sonuçlanır $_
.
Sekans operatörü ( ...
) , sağdaki uç noktaya kadar tembel bir şekilde bir dizi terim oluşturmak için önceki terimi kapanışın argümanı olarak geçirerek soldaki kapağı tekrar tekrar çağırır; bu durumda aka sonsuzluk *
için Inf
kısaydı.
İlk kapanış çağrısında yer alan konu 1
. Böylece kapanış 1 / (1 * 1)
hesaplanır ve dizideki ilk iki terimi verir 1, 1/1
.
İkinci çağrıdaki konu, bir öncekinin değeridir 1/1
, yani 1
tekrar. Böylece kapanış hesaplar ve geri döner 1 / (1 * 2)
, seriyi genişletir 1, 1/1, 1/2
. Her şey iyi görünüyor.
Bir sonraki kapanış 1 / (1/2 * 3)
olanı hesaplar 0.666667
. Bu terim olmalı 1 / (1 * 2 * 3)
. Hata.
Kodunuzu formülle eşleştirme
Kodunuzun aşağıdaki formüle uyması gerekiyor:
Bu formülde, her terim, serideki konumuna göre hesaplanır . K (seri terimi inci k için ilk = 0 1
) sadece faktörlüdür k 'in karşılıklı.
(Dolayısıyla , önceki dönemin değeri ile ilgisi yoktur . Bu nedenle $_
, önceki dönemin değerini alan kapanışta kullanılmamalıdır.)
Faktöriyel bir postfix operatörü oluşturalım:
sub postfix:<!> (\k) { [×] 1 .. k }
( ×
bir infix çarpma operatörüdür, normal ASCII infix'in daha hoş görünen Unicode diğer adıdır*
.)
Bu kısayol için:
sub postfix:<!> (\k) { 1 × 2 × 3 × .... × k }
(Gerektiği kadar terim ekleme veya çıkarma fikrini belirtmek için parantez içindeki sahte metasyntaktik notasyonu kullandım.
Daha genel olarak, op
bir ifadenin başlangıcında bir infix operatörünün köşeli parantez içine alınması, eşdeğeri olan bileşik bir önek operatörü oluşturur reduce with => &[op],
. Daha fazla bilgi için İndirgeme metaoperatörü bölümüne bakın .
Şimdi yeni faktöriyel postfix operatörünü kullanmak için kapatmayı yeniden yazabiliriz:
my @e = 1, { state $a=1; 1 / $a++! } ... *;
Bingo. Bu doğru seriyi üretir.
... farklı bir sebepten ötürü gelene kadar. Bir sonraki sorun sayısal doğruluktur. Ama bir sonraki bölümde bununla başa çıkalım.
Kodunuzdan türetilen tek bir astar
Belki üç çizgiyi bire sıkıştırın:
say [+] .[^10] given 1, { 1 / [×] 1 .. ++$ } ... Inf
.[^10]
tarafından belirlenen konuya uygulanır given
. ( ^10
kısayol 0..9
, bu nedenle yukarıdaki kod serideki ilk on terimin toplamını hesaplar.)
$a
Gelecek dönem bilgisayar kapanıştan elimine ettik . Yalnız $
, (state $)
anonim bir durum skaleriyle aynıdır . Sana başlatarak yaptığı gibi aynı etkiyi elde etmek için bu artırma öncesi yerine artırma sonrası yapılan $a
için 1
.
Şimdi, aşağıdaki yorumda belirttiğiniz son (büyük!) Sorundan ayrıldık.
Operandlarının hiçbirinin bir Num
(şamandıra ve dolayısıyla yaklaşık) olmaması şartıyla , /
operatör normal olarak% 100 doğruluk Rat
(sınırlı bir hassas rasyonel) döndürür . Ancak sonucun paydası 64 bit'i aşarsa, bu sonuç bir Num
performansa dönüştürülür - ki bu performans için doğruluk için işlemek, yapmak istemediğimiz bir ödünleşmedir. Bunu hesaba katmalıyız.
Sınırsız hassasiyet ve% 100 doğruluk belirtmek için , FatRat
s'yi kullanmak için işlemi zorlayın . Bunu doğru bir şekilde yapmak için, (en azından) işlenenlerden birini a FatRat
(ve hiçbiri a değil Num
) yapın:
say [+] .[^500] given 1, { 1.FatRat / [×] 1 .. ++$ } ... Inf
Bunu 500 ondalık basamağa kadar doğruladım. Raku dili veya Rakudo derleyicisinin bazı sınırlarını aşması nedeniyle program çökene kadar doğru kalmasını bekliyorum. (Bkz cevabım değil Unbox 65536 bit genişliğinde yerli tamsayı içine bigint Can'ın ondan biraz tartışma için.)
Dipnotlar
1 Raku dahil yerleşik birkaç önemli matematiksel sabitleri vardır e
, i
ve pi
(ve takma π
). Böylece Raku'da Euler'in Kimliğini matematik kitaplarında göründüğü gibi yazabiliriz. Kredi ile Euler Kimlik için RosettaCode en Raku girişi :
# 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 Damian'ın makalesi mutlaka okunmalı. Ancak , 'raku "euler sayısı' ' için bir google için 100'den fazla maç arasında yer alan takdire şayan tedavilerden sadece bir tanesi .
3 Bkz TSBO-apoo-OWTDI vs TIMTOWTDI piton bir fan tarafından yazılan TIMTOWTDI daha dengeli görünümlerden biri için. Ama olan çok uzakta TIMTOWTDI alarak kötü yanları. Bu son "tehlikeyi" yansıtmak için, Perl topluluğu mizahi bir şekilde uzun, okunamayan ve sade olan TIMTOWTDIBSCINABTE'ı icat etti - Bunu yapmak için birden fazla yol var, ancak bazen Tutarlılık Kötü Bir Şey Değil, "Tim Toady Bikarbonat" olarak telaffuz edildi. Garip bir şekilde Larry Raku'nun tasarımına bikarbonat uyguladı ve Damian e
da Raku'daki bilgisayarlara uyguladı .