Tôi phân tích mã của bạn trong phần Phân tích mã của bạn . Trước đó tôi trình bày một vài phần thú vị của tài liệu tiền thưởng.
Một lót Một chữ cái 1
say e; # 2.718281828459045
Nhấp vào liên kết ở trên để xem bài viết đặc biệt của Damian Conway về điện toán e
trong Raku.
Bài viết rất thú vị (sau tất cả, đó là Damian). Đó là một cuộc thảo luận rất dễ hiểu về điện toán e
. Và đó là sự tôn kính đối với sự tái sinh bicarbonate của Raku về triết lý TIMTOWTDI được tán thành bởi Larry Wall. 3
Là một món khai vị, đây là một trích dẫn từ khoảng nửa bài báo:
Cho rằng tất cả các phương thức hiệu quả này đều hoạt động theo cùng một cách bằng cách tóm tắt (một tập hợp con ban đầu) một chuỗi thuật ngữ vô hạn mà có lẽ sẽ tốt hơn nếu chúng ta có một chức năng để làm điều đó cho chúng ta. Và chắc chắn sẽ tốt hơn nếu hàm có thể tự giải quyết chính xác bao nhiêu tập con ban đầu của chuỗi mà nó thực sự cần bao gồm để tạo ra một câu trả lời chính xác ... thay vì yêu cầu chúng tôi tự kết hợp thông qua kết quả của nhiều thử nghiệm để khám phá điều đó.
Và, như thường thấy ở Raku, thật dễ dàng để xây dựng đúng thứ chúng ta cần:
sub Σ (Unary $block --> Numeric) {
(0..∞).map($block).produce(&[+]).&converge
}
Phân tích mã của bạn
Đây là dòng đầu tiên, tạo ra chuỗi:
my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
Việc đóng ( { code goes here }
) tính một thuật ngữ. Một bao đóng có một chữ ký, ngầm hoặc rõ ràng, xác định có bao nhiêu đối số sẽ chấp nhận. Trong trường hợp này không có chữ ký rõ ràng. Việc sử dụng $_
( biến "chủ đề" ) dẫn đến một chữ ký ngầm yêu cầu một đối số ràng buộc $_
.
Toán tử chuỗi ( ...
) liên tục gọi hàm đóng bên trái của nó, chuyển thuật ngữ trước đó làm đối số của bao đóng, để lười biếng xây dựng một loạt các thuật ngữ cho đến khi điểm cuối bên phải của nó, trong trường hợp này là*
viết tắt cho Inf
vô cực.
Chủ đề trong cuộc gọi đầu tiên đến đóng cửa là 1
. Vì vậy, việc đóng cửa tính toán và trả về1 / (1 * 1)
hai thuật ngữ đầu tiên trong chuỗi như 1, 1/1
.
Chủ đề trong cuộc gọi thứ hai là giá trị của cuộc gọi trước 1/1
, tức là 1
một lần nữa. Vì vậy, việc đóng cửa tính toán và trả về 1 / (1 * 2)
, mở rộng chuỗi tới 1, 1/1, 1/2
. Tất cả có vẻ tốt.
Các đóng cửa tiếp theo tính toán 1 / (1/2 * 3)
đó là 0.666667
. Thuật ngữ đó nên được 1 / (1 * 2 * 3)
. Giáo sư.
Làm cho mã của bạn khớp với công thức
Mã của bạn được cho là phù hợp với công thức:
Trong công thức này, mỗi thuật ngữ được tính dựa trên vị trí của nó trong chuỗi. Các k hạn thứ trong loạt (trong đó k = 0 cho là người đầu tiên 1
) chỉ là thừa k 's đối ứng.
(Vì vậy, không có gì để làm với giá trị của thuật ngữ trước. Do đó $_
, nhận giá trị của thuật ngữ trước, không nên được sử dụng trong bao đóng.)
Hãy tạo một toán tử hậu tố giai thừa:
sub postfix:<!> (\k) { [×] 1 .. k }
( ×
là một toán tử nhân infix, một bí danh Unicode trông đẹp hơn của infix ASCII thông thường *
.)
Đó là viết tắt của:
sub postfix:<!> (\k) { 1 × 2 × 3 × .... × k }
(Tôi đã sử dụng ký hiệu siêu thực giả trong niềng răng để biểu thị ý tưởng thêm hoặc bớt bao nhiêu thuật ngữ theo yêu cầu.
Tổng quát hơn, việc đặt một toán tử infix op
trong dấu ngoặc vuông ở đầu biểu thức tạo thành một toán tử tiền tố tổng hợp tương đương với reduce with => &[op],
. Xem Giảm metaoperoper để biết thêm.
Bây giờ chúng ta có thể viết lại bao đóng để sử dụng toán tử postfix giai thừa mới:
my @e = 1, { state $a=1; 1 / $a++! } ... *;
Chơi lô tô. Điều này tạo ra loạt đúng.
... cho đến khi nó không, vì một lý do khác. Vấn đề tiếp theo là độ chính xác số. Nhưng hãy giải quyết điều đó trong phần tiếp theo.
Một lớp lót có nguồn gốc từ mã của bạn
Có thể nén ba dòng xuống một:
say [+] .[^10] given 1, { 1 / [×] 1 .. ++$ } ... Inf
.[^10]
áp dụng cho chủ đề, được thiết lập bởi given
. ( ^10
là viết tắt của 0..9
, vì vậy đoạn mã trên sẽ tính tổng của mười thuật ngữ đầu tiên trong chuỗi.)
Tôi đã loại bỏ $a
khỏi máy tính đóng cửa nhiệm kỳ tiếp theo. Một đơn độc $
giống như (state $)
, một vô hướng trạng thái vô định. Tôi đã làm cho nó một pre-increment thay vì hậu increment để đạt được tác dụng tương tự như bạn đã làm bằng cách khởi tạo $a
để1
.
Bây giờ chúng ta còn lại với vấn đề cuối cùng (lớn!), Được bạn chỉ ra trong một bình luận dưới đây.
Với điều kiện không phải toán hạng của nó là Num
(một số float và do đó gần đúng), /
toán tử thường trả về độ chính xác 100% Rat
(một tỷ lệ chính xác có giới hạn). Nhưng nếu mẫu số của kết quả vượt quá 64 bit thì kết quả đó được chuyển đổi thành một Num
- giao dịch hiệu suất cho chính xác, một sự đánh đổi mà chúng ta không muốn thực hiện. Chúng ta cần phải tính đến điều đó.
Để chỉ định độ chính xác không giới hạn cũng như độ chính xác 100%, chỉ cần ép buộc thao tác sử dụng FatRat
s. Để làm điều này một cách chính xác, chỉ cần làm cho (ít nhất) một trong các toán hạng là một FatRat
(và không ai khác là a Num
):
say [+] .[^500] given 1, { 1.FatRat / [×] 1 .. ++$ } ... Inf
Tôi đã xác minh điều này đến 500 chữ số thập phân. Tôi hy vọng nó vẫn chính xác cho đến khi chương trình gặp sự cố do vượt quá giới hạn của ngôn ngữ Raku hoặc trình biên dịch Rakudo. (Xem câu trả lời của tôi về Không thể bỏ hộp lớn 65536 bit thành số nguyên gốc để thảo luận về điều đó.)
Chú thích
1 Raku có một vài hằng số toán học quan trọng được xây dựng trong, bao gồm e
, i
và pi
(và bí danh của nó π
). Do đó, người ta có thể viết Danh tính của Euler trong Raku giống như trong sách toán học. Với khoản tín dụng cho mục nhập Raku của RosettaCode cho Danh tính của 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 bài viết của Damian là phải đọc. Nhưng đó chỉ là một trong một số phương pháp điều trị đáng ngưỡng mộ nằm trong số hơn 100 trận đấu cho một google cho 'raku "số euler"' .
3 Xem TIMTOWTDI vs TSBO-APOO-OWTDI để biết một trong những quan điểm cân bằng hơn về TIMTOWTDI được viết bởi một người hâm mộ của trăn. Nhưng có những nhược điểm khi dùng TIMTOWTDI quá xa. Để phản ánh "mối nguy hiểm" sau này, cộng đồng Perl đã tạo ra TIMTOWTDIBSCINABTE dài một cách hài hước - Có nhiều cách để làm điều đó nhưng đôi khi sự nhất quán không phải là điều xấu, hay được phát âm là "Tim Toady bicarbonate". Thật kỳ lạ , Larry đã áp dụng bicarbonate vào thiết kế của Raku và Damian áp dụng nó vào điện toán e
trong Raku.