Я пытаюсь вычислить постоянную е (число Эйлера Эйка ), вычисляя формулу
Чтобы вычислить факториал и деление за один снимок, я написал это:
my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
say reduce * + * , @e[^10];
Но это не сработало. Как это сделать правильно?
code-generation
lazy-evaluation
raku
eulers-number
Ларс Мальмстин
источник
источник
$_
переменную- значение , пытаясь построить факториал. Это было явно излишним. В правильном решении ниже,$_
был сброшен, и это сработало отлично.Ответы:
Я анализирую ваш код в разделе Анализ вашего кода . Перед этим я представляю пару забавных разделов бонусного материала.
Один вкладышОдна буква 1«Трактат о нескольких способах» 2
Нажмите на ссылку выше, чтобы увидеть необычную статью Дамиана Конвея о компьютерах
e
в Раку.Статья очень веселая (в конце концов, это Дамиан). Это очень понятное обсуждение вычислительной техники
e
. И это дань уважения бикарбонатному перевоплощению Раку философии TIMTOWTDI, поддерживаемой Ларри Уоллом. 3В качестве закуски вот цитата из середины статьи:
Анализируя ваш код
Вот первая строка, генерирующая серию:
Закрытие (
{ code goes here }
) вычисляет термин. Замыкание имеет подпись, неявную или явную, которая определяет, сколько аргументов оно примет. В этом случае нет явной подписи. Использование$_
( переменная "topic" ) приводит к неявной подписи, которая требует одного аргумента, с которым связан$_
.Оператор sequence (
...
) неоднократно вызывает замыкание слева от него, передавая предыдущий член в качестве аргумента замыкания, чтобы лениво построить ряд терминов до конечной точки справа, что в данном случае является*
сокращением для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
) просто факториала к «с взаимным.(Таким образом, он не имеет ничего общего со значением предыдущего термина. Таким образом, тот
$_
, который получает значение предыдущего срока, не должен использоваться в закрытии.)Давайте создадим факториальный постфиксный оператор:
(
×
это оператор умножения инфиксов, более привлекательный псевдоним Юникода для обычного инфикса ASCII*
.)Это сокращение для:
(Я использовал псевдо метасинтаксические обозначения внутри фигурных скобок, чтобы обозначить идею добавления или вычитания столько терминов, сколько требуется.
В более общем смысле, помещая инфиксный оператор
op
в квадратные скобки в начале выражения, формируется составной префиксный оператор, эквивалентныйreduce with => &[op],
. См. Сокращение метаоператора для получения дополнительной информации.Теперь мы можем переписать замыкание для использования нового факториального постфиксного оператора:
Бинго. Это производит правильную серию.
... пока этого не произойдет, по другой причине. Следующая проблема - численная точность. Но давайте разберемся с этим в следующем разделе.
Одна строка, полученная из вашего кода
Может быть, сжать три строки до одной:
.[^10]
относится к теме, которая установленаgiven
. (^10
это условное0..9
обозначение, поэтому вышеприведенный код вычисляет сумму первых десяти членов в ряду.)Я исключил
$a
из вычисления замыкания следующий термин. Лоун$
такая же , как(state $)
, в anonynous состояния скаляр. Я сделал предварительное увеличение вместо постинкрементного, чтобы добиться того же эффекта, что и вы, выполнив инициализацию$a
для1
.Теперь у нас осталась последняя (большая!) Проблема, указанная вами в комментарии ниже.
При условии, что ни один из его операндов не является
Num
(с плавающей точкой, и, следовательно, приблизительным),/
оператор обычно возвращает 100% точностьRat
(рациональная ограниченная точность). Но если знаменатель результата превышает 64 бита, то этот результат преобразуется вNum
- что обменивает производительность на точность, компромисс, который мы не хотим делать. Мы должны принять это во внимание.Чтобы указать неограниченную точность, а также точность 100%, просто принудительно используйте операцию
FatRat
s. Чтобы сделать это правильно, просто сделайте (по крайней мере) один из операндов бытьFatRat
(и ни один другой не будетNum
):Я подтвердил это до 500 десятичных цифр. Я ожидаю, что он останется точным до тех пор, пока не произойдет сбой программы из-за превышения некоторого ограничения языка Raku или компилятора Rakudo. (См. Мой ответ на Невозможно распаковать bigint 65536 бит в родное целое для некоторого обсуждения этого.)
Сноски
1 Раку имеет несколько важных математических констант , построенных в том числе
e
,i
иpi
(и его псевдонимπ
). Таким образом, «Идентичность Эйлера» можно написать в «Раку» так, как это выглядит в книгах по математике. С благодарностью за запись Raku RosettaCode для Идентификации Эйлера :2 Статья Дамиана обязательна к прочтению. Но это только одна из нескольких замечательных процедур, которые входят в число 100+ совпадений для Google по поводу "raku" числа Эйлера " .
3 См. TIMTOWTDI против TSBO-APOO-OWTDI для одного из более сбалансированных представлений TIMTOWTDI, написанных поклонником python. Но есть минусы принимать TIMTOWTDI слишком далеко. Чтобы отразить эту последнюю «опасность», сообщество Perl придумало юмористически длинный, нечитаемый и недооцененный TIMTOWTDIBSCINABTE - существует больше, чем один способ сделать это, но иногда последовательность не является плохой вещью, произносится «Тим Бобикарбонат Тим». Как ни странно , Ларри применил бикарбонат к дизайну Раку, а Дамиан применил его к вычислениям
e
в Раку.источник
$
была сокращениемstate $
, это довольно удобно.e
для 3-го решения (под названием « Мой путь основан на вашем способе» )? Я пытался добавить FatRat (500) рядом с 1 в:... given 1.FatRat(500), ...
для точности 500 цифр, но это не сработало.FatRat
вопрос в последнем разделе. Я также отточил весь ответ, хотя единственное существенное изменение - этоFatRat
материал. (Между прочим, я понимаю, что большая часть моего ответа действительно имеет отношение к вашему первоначальному вопросу; я надеюсь, вы не возражали против того, чтобы я писал весь этот дополнительный пух, чтобы развлечь себя и, возможно, быть интересным для более поздних читателей.).FatRat
расширение должно быть помещено в генератор кода. Теперь я попробовал это сFatRat
добавленным способом, и он вычислил e с точностью до 1000+ цифр. Добавленный лишний пух стоит. Например, я не знал, чтоsay
он усекает длинные массивы / последовательности. Такие биты информации полезно знать..FatRat
расширение должно быть помещено в генератор кода." Да. В более общем смысле, если выражение, включающее деление, уже было оценено, будет слишком поздно отменить ущерб, вызванный превышениемRat
точности. Если это так, он оценивается какNum
(float), и это, в свою очередь, портит любые дальнейшие вычисления, связанные с ним, делая их такжеNum
. Единственный способ гарантировать, что вещи остаются,FatRat
состоит в том, чтобы начать ихFatRat
и избежать любыхNum
s.Int
s иRat
s в порядке, при условии, что есть хотя бы один,FatRat
чтобы Раку знал, что он придерживаетсяFatRat
s.Есть дроби
$_
. Таким образом, вам нужно,1 / (1/$_ * $a++)
а точнее$_ /$a++
.С помощью Raku вы можете сделать этот шаг за шагом
источник
andthen
.