J'essaie de calculer la constante e ( AKA Euler's Number ) en calculant la formule
Afin de calculer la factorielle et la division en un seul coup, j'ai écrit ceci:
my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
say reduce * + * , @e[^10];
Mais ça n'a pas marché. Comment le faire correctement?
code-generation
lazy-evaluation
raku
eulers-number
Lars Malmsteen
la source
la source
$_
, pour tenter de construire la factorielle. C'était évidemment redondant. Dans la bonne solution ci-dessous, a$_
été supprimé et cela a parfaitement fonctionné.Réponses:
J'analyse votre code dans la section Analyser votre code . Avant cela, je présente quelques sections amusantes de matériel bonus.
Une doublureUne lettre 1"Un traité aux multiples voies" 2
Cliquez sur le lien ci-dessus pour voir l'article extraordinaire de Damian Conway sur l'informatique
e
à Raku.L'article est très amusant (après tout, c'est Damian). C'est une discussion très compréhensible de l'informatique
e
. Et c'est un hommage à la réincarnation par Raku du bicarbonate de la philosophie TIMTOWTDI adoptée par Larry Wall. 3En entrée, voici une citation d'environ la moitié de l'article:
Analyser votre code
Voici la première ligne, générant la série:
La fermeture (
{ code goes here }
) calcule un terme. Une fermeture a une signature, implicite ou explicite, qui détermine le nombre d'arguments qu'elle acceptera. Dans ce cas, il n'y a pas de signature explicite. L'utilisation de$_
( la variable "topic" ) entraîne une signature implicite qui nécessite un argument lié à$_
.L'opérateur de séquence (
...
) appelle à plusieurs reprises la fermeture sur sa gauche, en passant le terme précédent comme argument de fermeture, pour construire paresseusement une série de termes jusqu'à l'extrémité à sa droite, qui dans ce cas est un*
raccourci pourInf
aka infini.Le sujet du premier appel à la clôture est
1
. La fermeture calcule et renvoie donc1 / (1 * 1)
les deux premiers termes de la série as1, 1/1
.Le sujet du deuxième appel est la valeur du précédent
1/1
, c'est-1
à- dire à nouveau. Ainsi, la fermeture calcule et retourne1 / (1 * 2)
, étendant la série à1, 1/1, 1/2
. Tout a l'air bien.La prochaine fermeture calcule
1 / (1/2 * 3)
ce qui est0.666667
. Ce terme devrait être1 / (1 * 2 * 3)
. Oops.Faire correspondre votre code à la formule
Votre code est censé correspondre à la formule:
Dans cette formule, chaque terme est calculé en fonction de sa position dans la série. Le k ème terme de la série (où k = 0 pour le premier
1
) est juste réciproque factoriel k .(Donc, cela n'a rien à voir avec la valeur du terme précédent. Ainsi
$_
, qui reçoit la valeur du terme précédent, ne doit pas être utilisé dans la fermeture.)Créons un opérateur de suffixe factoriel:
(
×
est un opérateur de multiplication d'infixes, un alias Unicode plus joli de l'infixe ASCII habituel*
.)C'est un raccourci pour:
(J'ai utilisé une notation pseudo métasyntaxique à l'intérieur des accolades pour indiquer l'idée d'ajouter ou de soustraire autant de termes que nécessaire.
Plus généralement, mettre un opérateur infixe
op
entre crochets au début d'une expression forme un opérateur préfixe composite qui est l'équivalent dereduce with => &[op],
. Voir Méta-opérateur de réduction pour plus d'informations.Nous pouvons maintenant réécrire la fermeture pour utiliser le nouvel opérateur factoriel postfix:
Bingo. Cela produit la bonne série.
... jusqu'à ce que ce ne soit pas le cas, pour une raison différente. Le problème suivant est la précision numérique. Mais examinons cela dans la section suivante.
Un liner dérivé de votre code
Peut-être compresser les trois lignes en une seule:
.[^10]
s'applique au sujet, qui est défini par legiven
. (^10
est un raccourci pour0..9
, donc le code ci-dessus calcule la somme des dix premiers termes de la série.)J'ai éliminé
$a
de la fermeture le calcul du prochain mandat. Un solitaire$
est identique à(state $)
un scalaire à état anonynique. J'ai fait un pré-incrément au lieu d'un post-incrément pour obtenir le même effet que vous avez fait en l'initialisant$a
à1
.Il nous reste maintenant le dernier (gros!) Problème, que vous avez signalé dans un commentaire ci-dessous.
À condition qu'aucun de ses opérandes ne soit un
Num
(un flottant, et donc approximatif), l'/
opérateur retourne normalement un 100% précisRat
(une précision limitée rationnelle). Mais si le dénominateur du résultat dépasse 64 bits, ce résultat est converti en unNum
- qui échange les performances contre la précision, un compromis que nous ne voulons pas faire. Nous devons en tenir compte.Pour spécifier une précision illimitée ainsi qu'une précision de 100%, il suffit de contraindre l'opération à utiliser l'
FatRat
art. Pour le faire correctement, il suffit de faire (au moins) l'un des opérandes être unFatRat
(et aucun autre être unNum
):J'ai vérifié cela à 500 chiffres décimaux. Je m'attends à ce qu'il reste précis jusqu'à ce que le programme plante en raison du dépassement d'une limite de la langue Raku ou du compilateur Rakudo. (Voir ma réponse à Cannot unbox 65536 bit large bigint into native integer for some discussion of that.)
Notes de bas de page
1 Raku a quelques constantes mathématiques importantes intégrées, y compris
e
,i
etpi
(et son aliasπ
). Ainsi, on peut écrire l'identité d'Euler dans Raku un peu comme on dirait dans les livres de mathématiques. Avec crédit à l'entrée Raku de RosettaCode pour l'identité d'Euler :2 L'article de Damian est à lire absolument. Mais ce n'est qu'un des nombreux traitements admirables qui font partie des 100+ correspondances pour un google pour le "raku" euler's number "' .
3 Voir TIMTOWTDI vs TSBO-APOO-OWTDI pour l'une des vues les plus équilibrées de TIMTOWTDI écrites par un fan de python. Mais il y a des inconvénients à aller trop loin avec TIMTOWTDI. Pour refléter ce dernier "danger", la communauté Perl a inventé TIMTOWTDIBSCINABTE , humoristique, long et illisible - Il y a plus d'une façon de le faire, mais parfois la cohérence n'est pas une mauvaise chose non plus, prononcé "Tim Toady Bicarbonate". Curieusement , Larry a appliqué du bicarbonate au design de Raku et Damian l'applique à l'informatique
e
dans Raku.la source
$
était un raccourcistate $
, c'est assez pratique.e
pour la 3e solution (intitulé Mon chemin en fonction de votre chemin )? J'ai essayé d'ajouter FatRat (500) à côté de 1 po:... given 1.FatRat(500), ...
pour que les chiffres soient précis à 500 chiffres, mais cela n'a pas fonctionné.FatRat
question très importante dans la dernière section. J'ai également affiné toute la réponse, bien que le seul changement majeur soit laFatRat
substance. (Btw, je me rends compte qu'une grande partie de ma réponse est vraiment tangentielle à votre question d'origine; j'espère que cela ne vous a pas dérangé d'écrire tous les trucs supplémentaires pour me divertir et peut-être être intéressant pour les lecteurs ultérieurs.).FatRat
extension doit donc être placée à l'intérieur du générateur de code. Maintenant, je l'ai essayé avec l'FatRat
ajout de cette façon et il a calculé le e à la précision de 1000+ chiffres. Les peluches supplémentaires ajoutées sont inutiles. Par exemple, je ne savais pas que lesay
tronçonnait les longs tableaux / séquences. Ces informations sont bonnes à savoir..FatRat
extension doit être placée à l'intérieur du générateur de code.". Oui. Plus généralement, si une expression impliquant une division a déjà été évaluée, il est trop tard pour réparer les dommages causés si elle dépassait laRat
précision. Si c'est le cas, il évaluera à unNum
(flottant) et cela à son tour entacher tous les calculs supplémentaires impliquant, en les faisant aussiNum
. La seule façon de garantir un séjour des chosesFatRat
est de commencer lesFatRat
et éviter toutNum
s.Int
s etRat
s sont OK, à condition qu'il y en ait au moins unFatRat
pour que Raku sache s'en tenir àFatRat
s.Il y a des fractions dedans
$_
. Ainsi vous avez besoin1 / (1/$_ * $a++)
ou plutôt$_ /$a++
.Par Raku, vous pouvez faire ce calcul étape par étape
la source
andthen
.