Я аналізую ваш код у розділі Аналіз вашого коду . Перед цим я представляю пару веселих розділів бонусного матеріалу.
Один вкладиш Один лист 1
say e; # 2.718281828459045
Клацніть вище посилання, щоб побачити надзвичайну статтю Даміана Конвей про обчислення eв Раку.
Стаття дуже весела (зрештою, це Даміан). Це дуже зрозуміле обговорення обчислень e. І це повагу до перевтілення бікарбонату Раку у філософію TIMTOWTDI, яку підтримує Ларрі Уолл. 3
Як закуска, ось цитата приблизно з півдороги статті:
Враховуючи, що всі ці ефективні методи працюють однаково - шляхом підсумовування (початкового підмножини) нескінченного ряду термінів - можливо, було б краще, якби у нас була функція зробити це для нас. І, звичайно, було б краще, якби функція могла сама розробити, скільки саме цього початкового підмножини серії вона насправді потребує, щоб отримати точну відповідь ... а не вимагати від нас вручну розчісувати результати численні випробування, щоб виявити це.
І, як це часто буває в Раку, напрочуд легко побудувати саме те, що нам потрібно:
sub Σ (Unary $block --> Numeric) {
(0..∞).map($block).produce(&[+]).&converge
}
Аналіз вашого коду
Ось перший рядок із створення серії:
my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
Закриття ( { code goes here }) обчислює термін. Закриття має підпис, неявний або явний, який визначає, скільки аргументів він прийме. У цьому випадку явного підпису немає. Використання $_( в «темі» змінних ) призводить до неявній підпису , який вимагає один аргумент , який прив'язаний до $_.
Оператор послідовності ( ...) неодноразово викликає завершення зліва, передаючи попередній термін як аргумент закриття, щоб ліниво побудувати ряд термінів до кінцевої точки праворуч, що в цьому випадку є *скороченням для Infака-нескінченності.
Тема в першому заклику до закриття є 1. Таким чином, обчислюється і повертається 1 / (1 * 1)завершення, даючи перші два терміни в ряду як 1, 1/1.
Тема другого дзвінка - це значення попереднього 1/1, тобто 1знову. Таким чином, завершення обчислює і повертається 1 / (1 * 2), продовжуючи серію до 1, 1/1, 1/2. Це все добре виглядає.
Наступне обчислювальне завершення, 1 / (1/2 * 3)яке є 0.666667. Цей термін повинен бути 1 / (1 * 2 * 3). На жаль
Здійснення відповідності коду формулі
Ваш код повинен відповідати формулі:
У цій формулі кожен термін обчислюється виходячи з його положення в ряді. До - го члена ряду (де до = 0 для першого 1) просто факторіала до «з взаємним.
(Отже, це не має нічого спільного зі значенням попереднього терміна. Таким чином $_, який отримує значення попереднього терміна, не слід використовувати при закритті.)
Створимо факторний поштовий оператор:
sub postfix:<!> (\k) { [×] 1 .. k }
( ×це оператор множення інфіксації, приємніше виглядає псевдонім Unicode звичайної інфіксації ASCII *.)
Це скорочення для:
sub postfix:<!> (\k) { 1 × 2 × 3 × .... × k }
(Я використовував псевдометасинтаксичні позначення всередині дужок, щоб позначити ідею додавання чи віднімання стільки термінів, скільки потрібно.
Більш загально, розміщення оператора інфіксації opу квадратних дужках на початку виразу утворює складений оператор префікса, який є еквівалентом reduce with => &[op],. Для отримання додаткової інформації див. Метаоператор скорочення .
Тепер ми можемо переписати закриття, щоб скористатися новим факторіальним оператором Postfix:
my @e = 1, { state $a=1; 1 / $a++! } ... *;
Бінго. Це дає правильний ряд.
... поки цього не стане, з іншої причини. Наступна проблема - числова точність. Але давайте розберемося з цим у наступному розділі.
Один лайнер, отриманий від вашого коду
Можливо, стисніть три лінії до однієї:
say [+] .[^10] given 1, { 1 / [×] 1 .. ++$ } ... Inf
.[^10]стосується теми, яку встановлює given. ( ^10це скорочення для 0..9, тому вищевказаний код обчислює суму перших десяти доданків у серії.)
Я усунув $aіз закінчення обчислювань наступний термін. Самотня $- це те саме (state $), що і анонімний скаляр стану. Я зробив це попереднім збільшенням замість післякріплення, щоб досягти такого ж ефекту, як і ви, ініціалізуючи $aдо 1.
Зараз ми залишилися з остаточною (великою!) Проблемою, яку ви вказали у коментарі нижче.
За умови, що жоден з його операндів не є Num(поплавком і, таким чином, наближеним), /оператор, як правило, повертає на 100% точність Rat(з обмеженою раціональністю точності). Але якщо знаменник результату перевищує 64 біт, то цей результат перетворюється на Num- який торгує продуктивністю для точності, компромісу ми не хочемо робити. Нам потрібно це врахувати.
Щоб вказати необмежену точність , а також 100% точність, просто примушуйте операцію використовувати FatRats. Щоб зробити це правильно, просто зробіть (принаймні) один з операндів FatRat(а жоден інший не буде а Num):
say [+] .[^500] given 1, { 1.FatRat / [×] 1 .. ++$ } ... Inf
Я підтвердив це до 500 десяткових цифр. Я очікую, що вона буде точною, поки програма не завершиться через перевищення деякого ліміту мови Raku або компілятора Rakudo. (Дивіться мою відповідь на Неможливо розв’язати 65536 біт шириною bigint в нативне ціле число для обговорення цього.)
Виноски
1 Раку має кілька важливих математичних констант , побудованих в тому числі e, iі pi(і його псевдонім π). Таким чином, можна написати особистість Ейлера в Раку дещо так, як це виглядає в математичних книгах. Залучивши запис Raku RosettaCode для особи Юлера :
# 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 Стаття Даміана обов'язково прочитана. Але це лише одна з кількох захоплюючих процедур, які входять у 100+ матчів для google за номером "raku" euler's number " .
3 Дивіться TIMTOWTDI проти TSBO-APOO-OWTDI для одного з більш збалансованих поглядів TIMTOWTDI, написаного шанувальником пітона. Але є й недоліки прийняти TIMTOWTDI занадто далеко. Щоб відобразити цю останню "небезпеку", спільнота Perl створила жартівливо довгий, нечитабельний і занижений TIMTOWTDIBSCINABTE - Існує більше одного способу зробити це, але іноді послідовність не є поганою річчю, вимовляється "Тім Тоді Бікарбонат". Як не дивно , Ларрі застосував бікарбонат до дизайну Раку, а Даміан застосовує його до обчислень eу Раку.