La beauté exceptionnelle du code source de Doom 3

Ceci est une histoire sur le code source de Doom 3 et à quel point il est beau. Oui, magnifique . Permettez-moi de vous expliquer.

Après avoir sorti mon jeu vidéo Dyad, j'ai fait une petite pause. J'ai lu des livres et regardé des films que j'avais reportés trop longtemps. Je travaillais sur la version européenne de Dyad , mais ce temps-là était surtout en attente des commentaires de l'assurance qualité de Sony, donc j'avais beaucoup de temps libre. Après avoir flâné pendant un mois environ, j'ai commencé à réfléchir sérieusement à ce que j'allais faire ensuite. Je voulais extraire les pièces réutilisables / motorisées de Dyad pour un nouveau projet.

Cet article a été initialement publié le 14 janvier 2013.

Quand j'ai commencé à travailler sur Dyad, il y avait un moteur de jeu très propre et très fonctionnel que j'ai créé à partir d'une accumulation d'années de travail sur d'autres projets. À la fin de Dyad, j'ai eu un désordre horrible.

Au cours des six dernières semaines du développement de Dyad , j'ai ajouté plus de 13 000 lignes de code. MainMenu.cc a gonflé à 24 501 lignes. Le code source autrefois magnifique était un désordre criblé de #ifdefs, de pointeurs de fonction gratuits, de code SIMD et asm en ligne laid - j'ai appris un nouveau terme: «entropie du code». J'ai cherché sur Internet d'autres projets que je pourrais utiliser pour apprendre à organiser des centaines de milliers de lignes de code. Après avoir parcouru plusieurs gros moteurs de jeux, j'étais assez découragé; le code source de Dyad n'était en fait pas si mauvais que tout le reste!

Insatisfait, j'ai continué à chercher et j'ai trouvé une très belle analyse du code source de Doom 3 d'id Software par l'expert en informatique Fabien Sanglard.

J'ai passé quelques jours à parcourir le code source de Doom 3 et à lire l'excellent article de Fabien lorsque j'ai tweeté:

C'était la vérité. Je ne me suis jamais vraiment soucié du code source auparavant. Je ne me considère pas vraiment comme un «programmeur». Je suis bon dans ce domaine, mais pour moi, ce n'est qu'un moyen d'arriver à une fin. Passer en revue le code source de Doom 3 m'a fait vraiment apprécier les bons programmeurs.

Pour mettre les choses en perspective: Dyad a 193k lignes de code, toutes en C ++. Doom 3 a 601k, Quake III a 229k et Quake II a 136k. Cela place Dyad quelque part entre Quake II et Quake III . Ce sont de grands projets.

Quand on m'a demandé d'écrire cet article, je l'ai utilisé comme excuse pour lire plus de code source d'autres jeux et pour en savoir plus sur les normes de programmation. Après des jours de recherche, j'ai été déconcerté par mon propre tweet qui a déclenché tout cela: que signifierait «beau» - ou «beau», d'ailleurs - signifierait en fait en se référant au code source? J'ai demandé à des amis programmeurs ce qu'ils pensaient que cela signifiait. Leurs réponses étaient évidentes, mais méritaient tout de même d'être mentionnées:

Il existe une norme de codage idTech 4 ( .doc ) qui, je pense, vaut la peine d'être lue. Je suis la plupart de ces standards et j'essaierai d'expliquer pourquoi ils sont bons et pourquoi spécifiquement ils rendent le code de Doom 3 si beau.

L'une des choses les plus intelligentes que j'ai vues de Doom est l'utilisation générique de leur analyseur lexical [ 1 ] et parser [ 2 ] . Tous les fichiers de ressources sont des fichiers ascii avec une syntaxe unifiée comprenant: des scripts, des fichiers d'animation, des fichiers de configuration, etc. tout est pareil. Cela permet à tous les fichiers d'être lus et traités par un seul morceau de code. L'analyseur est particulièrement robuste, prenant en charge un sous-ensemble majeur de C ++. En s'en tenant à un analyseur et un lexer unifiés, tous les autres composants du moteur n'ont pas à se soucier de la sérialisation des données car il existe déjà du code pour cela. Cela rend tous les autres aspects du code plus propres.

Le code de Doom est assez rigide, bien que pas assez rigide à mon avis par rapport à const [ 3 ] . Const sert plusieurs objectifs que je pense que trop de programmeurs ignorent. Ma règle est que «tout doit toujours être constant à moins qu'il ne puisse l'être». Je souhaite que toutes les variables en C ++ soient const par défaut. Doom s'en tient presque toujours à une politique de paramètre «pas d'entrée-sortie»; ce qui signifie que tous les paramètres d'une fonction sont soit en entrée, soit en sortie, jamais les deux. Cela permet de comprendre beaucoup plus facilement ce qui se passe avec une variable lorsque vous la passez à une fonction. Par exemple:

Cette définition de fonction ça me fait plaisir!

Juste à partir de quelques consts, je sais beaucoup de choses:

La règle const, et aucun paramètre d'entrée / sortie est probablement la chose la plus importante, à mes yeux, qui sépare un bon code d'un beau code. Cela rend l'ensemble du système plus facile à comprendre et plus facile à éditer ou à refactoriser.

C'est un problème de style, mais une belle chose que Doom fait généralement est de ne pas trop commenter. J'ai vu beaucoup trop de code qui ressemble à:

Je trouve cela extrêmement irritant. Je peux dire ce que fait cette méthode par son nom. Si sa fonction ne peut pas être déduite de son nom, son nom doit être changé. S'il en fait trop pour le décrire dans son nom, faites-le moins. S'il ne peut vraiment pas être remanié et renommé pour décrire son seul objectif, vous pouvez le commenter. Je pense que les programmeurs apprennent à l'école que les commentaires sont bons; ils ne le sont pas. Les commentaires sont mauvais sauf s'ils sont totalement nécessaires et rarement nécessaires. Doom fait un travail raisonnable en minimisant les commentaires. En utilisant l'exemple idSurface :: Split (), voyons comment il est commenté:

// divise la surface en une surface avant et arrière, la surface elle-même reste inchangée
// frontOnPlaneEdges et backOnPlaneEdges stockent éventuellement les index des arêtes qui se trouvent sur le plan de division
// renvoie un SIDE_?

La première ligne est complètement inutile. Nous avons appris toutes ces informations à partir de la définition de la fonction. Les deuxième et troisième lignes sont précieuses. Nous pourrions déduire les propriétés de la deuxième ligne, mais le commentaire supprime toute ambiguïté potentielle.

Le code de Doom est, pour la plupart, judiciaire avec ses commentaires, ce qui le rend beaucoup plus facile à lire. Je sais que cela peut être un problème de style pour certaines personnes, mais je pense vraiment qu'il y a une «bonne» façon de le faire. Par exemple, que se passerait-il si quelqu'un changeait la fonction et supprimait le const à la fin? Ensuite, la surface * POURRAIT * être modifiée à partir de la fonction et maintenant le commentaire est désynchronisé avec le code. Les commentaires superflus nuisent à la lisibilité et à l'exactitude du code, ce qui rend le code plus laid.

Doom ne gaspille pas d'espace vertical:

Voici un exemple de t_stencilShadow :: R_ChopWinding ():

Je peux lire cet algorithme entier sur 1/4 de mon écran, laissant les autres 3/4 comprendre où ce bloc de code se situe par rapport à son code environnant. J'ai vu trop de code comme celui-ci:

Ce sera un autre point qui relève du «style». J'ai programmé pendant plus de 10 ans avec ce dernier style, me forçant à me convertir à la voie plus serrée tout en travaillant sur un projet il y a environ six ans. Je suis content d'avoir changé.

Ce dernier prend 18 lignes contre 11 dans la première. C'est presque le double du nombre de lignes de code pour la même fonctionnalité * EXACT *. Cela signifie que le prochain morceau de code ne rentre pas sur l'écran pour moi. Quel est le prochain morceau?

Ce code n'a aucun sens sans le bloc de boucle for précédent. Si id ne respectait pas l'espace vertical, leur code serait beaucoup plus difficile à lire, plus difficile à écrire, plus difficile à maintenir et moins beau.

Une autre chose que l'id fait et que je crois est «juste» et non un problème de style est qu'ils utilisent * TOUJOURS * {} même lorsqu'ils sont facultatifs. Je pense que c'est un crime de sauter les crochets. J'ai vu tellement de code comme:

C'est un mauvais code, c'est pire que de mettre {} sur leur propre ligne. Je n'ai pas trouvé un seul exemple dans le code de l'identifiant où ils ont ignoré {}. L'omission de l'option {} rend l'analyse de ce bloc while () plus longue que nécessaire. Cela rend également son édition difficile, que faire si je voulais insérer une branche if-statement dans le chemin else if (c> d)?

id a fait un énorme non-non dans le monde C ++. Ils ont réécrit toutes les fonctions STL [ 4 ] requises . J'ai personnellement une relation d'amour-haine avec le TSL. Dans Dyad, je l'ai utilisé dans les versions de débogage pour gérer les ressources dynamiques. Dans la version, j'ai cuit toutes les ressources afin qu'elles puissent être chargées aussi rapidement que possible et n'utiliser aucune fonctionnalité STL. La STL est intéressante car elle fournit des structures de données génériques rapides; c'est mauvais car son utilisation peut souvent être moche et sujette aux erreurs. Par exemple, regardons la classe std :: vector <T>. Disons que je veux parcourir chaque élément:

Cela se simplifie avec C ++ 11:

Personnellement, je n'aime pas l'utilisation d'auto, je pense que cela rend le code plus facile à écrire mais plus difficile à lire. Je pourrais en venir à l'utilisation de l'automobile dans les années à venir, mais pour l'instant, je pense que c'est mauvais. Je ne vais même pas mentionner le ridicule de certains algorithmes STL comme std: for_each ou std :: remove_if.

Supprimer une valeur d'un std :: vector est également stupide:

Gee, ça va être tapé correctement par chaque programmeur à chaque fois!

id supprime toute ambiguïté: ils ont roulé leurs propres conteneurs génériques, classe de chaînes, etc. Ils les ont écrits beaucoup moins génériques que les classes STL, sans doute pour les rendre plus faciles à comprendre. Ils ont un modèle minimal et utilisent des allocateurs de mémoire spécifiques à l'id. Le code STL est tellement rempli d'absurdités de modèle qu'il est impossible à lire.

Le code C ++ peut rapidement devenir indiscipliné et laid sans diligence de la part des programmeurs. Pour voir à quel point les choses peuvent devenir mauvaises, consultez le code source de la STL. Les implémentations STL de Microsoft et de GCC [ 5 ] sont probablement le code source le plus laid que j'aie jamais vu. Même lorsque les programmeurs prennent un soin extrême pour rendre leur code de modèle aussi lisible que possible, c'est toujours un désordre complet. Jetez un coup d'œil à la bibliothèque Loki d'Andrei Alexandrescu , ou aux bibliothèques boost - elles sont écrites par certains des meilleurs programmeurs C ++ au monde et un grand soin a été pris pour les rendre aussi belles que possible, et elles sont toujours laides et fondamentalement illisibles.

id résout ce problème en ne rendant tout simplement pas les choses trop génériques. Ils ont un HashTable <V> et une classe HashIndex. HashTable force le type de clé à être const char *, et HashIndex est une paire int-> int. Ceci est considéré comme une mauvaise pratique C ++. Ils «auraient dû» avoir une seule classe HashTable, une spécialisation partielle écrite pour KeyType = const char *, et une spécialisation complète <int, int>. Ce que fait id est tout à fait correct et rend leur code beaucoup plus beau.

Cela peut être examiné plus en détail en comparant les «bonnes pratiques C ++» pour la génération de hachage et la façon dont id le fait.

Il serait considéré par beaucoup comme une bonne pratique de créer une classe de calcul spécifique en tant que paramètre du HashTable comme ceci:

cela pourrait alors être spécialisé pour un type particulier:

Ensuite, vous pouvez passer le ComputeHashForType en tant que HashComputer pour le HashTable:

C'est similaire à la façon dont je l'ai fait. Cela semble intelligent, mais garçon est-ce moche! Et s'il y avait plus de paramètres de modèle facultatifs? Peut-être un allocateur de mémoire? Peut-être un traceur de débogage? Vous auriez une définition comme:

Les définitions de fonction seraient brutales!

Qu'est ce que ça veut dire? Je ne peux même pas trouver le nom de la méthode sans une coloration syntaxique agressive. Il est concevable qu'il y ait plus de code de définition que de code de corps. Ce n'est clairement pas facile à lire et donc pas beau.

J'ai vu d'autres moteurs gérer ce désordre en déchargeant la spécification d'argument de modèle sur une myriade de typedefs. C'est encore pire! Cela peut rendre le code local plus facile à comprendre, mais cela crée une autre couche de déconnexion entre le code local et la logique système globale, ce qui fait que le code local ne fait pas allusion à la conception du système, ce qui n'est pas beau. Par exemple, disons qu'il y avait du code:

et

et vous avez utilisé les deux et avez fait quelque chose comme:

Il est possible que l'allocateur de mémoire de StringHashTable, StringAllocator, ne contribue pas à la mémoire globale, ce qui vous causerait de la confusion. Vous devrez revenir en arrière dans le code, découvrir que StringHashTable est en fait un typedef d'un désordre de modèles, analyser le code du modèle, découvrir qu'il utilise un allocateur différent, trouver cet allocateur ... bla bla, moche.

Doom fait la «mauvaise» chose complète selon la logique commune du C ++: il écrit des choses aussi non génériques que possible, en utilisant des génériques uniquement lorsque cela a du sens. Que fait le HashTable de Doom lorsqu'il a besoin de générer un hachage de quelque chose? Il appelle idStr :: GetHash (), car le seul type de clé qu'il accepte est un const char *. Que se passerait-il s'il avait besoin d'une clé différente? Je suppose qu'ils modéliseraient la clé et forceraient simplement à appeler key.getHash (), et demanderaient au compilateur de faire en sorte que tous les types de clés aient une méthode int getHash ().

Je ne sais plus à quel point l'équipe de programmation originale d'id est avec la société, mais John Carmack vient au moins d'un fond C. Tous les jeux d'identification avant Quake III ont été écrits en C. Je trouve que de nombreux programmeurs C ++ sans une solide expérience en C sur-C ++ utilisent leur code. L'exemple de modèle précédent n'était qu'un cas. Trois autres exemples que je trouve souvent sont:

id est très judiciaire dans tous ces cas.

On peut souvent créer une classe:

C'est une perte de lignes de code et de temps de lecture. Il faut plus de temps pour l'écrire et le lire que:

Et si vous augmentez souvent var d'un certain nombre n?

contre

Le premier exemple est beaucoup plus facile à lire et à écrire.

id n'utilise pas de chaînes de caractères. Un stringstream contient probablement la bastardisation la plus extrême des surcharges d'opérateurs que j'ai jamais vue: <<.

Par exemple:

C'est moche. Il a de forts avantages: vous pouvez définir l'équivalent de la méthode toString () de Java par classe sans toucher aux vtables d'une classe, mais la syntaxe est offensive et id a choisi de ne pas l'utiliser. Choisir d'utiliser printf () au lieu de stringstreams rend leur code plus facile à lire, et donc je pense que c'est la bonne décision.

Bien mieux!

La syntaxe de l'opérateur de SomeClass << serait également ridicule:

[ Note latérale: John Carmack a déclaré que les outils d'analyse statique ont révélé que leur bogue commun était une correspondance de paramètre incorrecte dans printf (). Je me demande s'ils ont changé en stringstreams dans Rage à cause de cela. GCC et clang trouvent tous les deux des erreurs de correspondance de paramètres printf () avec -Wall, vous n'avez donc pas besoin d'outils d'analyse statique coûteux pour trouver ces erreurs.]

Une autre chose qui rend le code Doom beau est l'utilisation minimale des surcharges d'opérateurs. La surcharge des opérateurs est une fonctionnalité très intéressante de C ++. Cela vous permet de faire des choses comme:

Sans surcharger, ces opérations prendraient plus de temps à écrire et à analyser. Doom s'arrête ici. J'ai vu du code qui ne le fait pas. J'ai vu du code qui surchargerait l'opérateur '%' pour signifier le produit scalaire ou l'opérateur Vector * Vector pour effectuer une multiplication vectorielle par morceaux. Cela n'a pas de sens de créer l'opérateur * pour le produit croisé car cela n'existe qu'en 3D, que faire si vous vouliez faire:
some_2d_vec * some_2d_vec, que devrait-il faire? Qu'en est-il 4d ou plus? La surcharge minimale des opérateurs id ne laisse aucune ambiguïté pour le lecteur du code.

L'une des plus grandes choses que j'ai apprises du code Doom a été un simple changement de style. J'avais des classes qui ressemblaient à:

Selon la norme de codage Doom 3 d' id , ils utilisent de vrais onglets de 4 espaces. Avoir un paramètre d'onglet cohérent pour tous les programmeurs leur permet d'aligner horizontalement leurs définitions de classe:

Ils mettent rarement les fonctions en ligne dans la définition de classe. La seule fois où je l'ai vu, c'est lorsque le code est écrit sur la même ligne que la déclaration de fonction. Il semble que cette pratique ne soit pas la norme et soit probablement mal vue. Cette méthode d'organisation des définitions de classe le rend extrêmement facile à analyser. L'écriture peut prendre un peu plus de temps, car vous auriez retaper un tas d'informations lors de la définition des méthodes:

Je suis contre toute frappe supplémentaire. J'ai besoin de faire les choses aussi vite que possible, mais c'est une situation où je pense qu'un peu plus de frappe lors de la définition de la classe est plus rentable que chaque fois que la définition de classe doit être analysée par un programmeur. Il existe plusieurs autres exemples stylistiques fournis dans les normes de codage Doom 3 ( .doc ) qui contribuent à la beauté du code source de Doom .

Je pense que les règles de dénomination des méthodes de Doom font défaut. Personnellement, j'applique la règle selon laquelle tous les noms de méthodes doivent commencer par un verbe à moins qu'ils ne le puissent pas.

Par exemple:

est bien mieux que:

J'étais vraiment excité d'écrire cet article, car cela m'a donné une excuse pour vraiment réfléchir à ce qu'est un beau code. Je ne pense toujours pas savoir, et c'est peut-être entièrement subjectif. Je pense que les deux choses les plus importantes, du moins pour moi, sont l'indentation stylistique et la constance maximale.

Beaucoup de choix stylistiques sont définitivement mes préférences personnelles, et je suis sûr que d'autres programmeurs auront des opinions différentes. Je pense que le choix du style à utiliser appartient à celui qui doit lire et écrire le code, mais je pense certainement que cela vaut la peine d'y réfléchir.

Je suggérerais à tout le monde de regarder le code source de Doom 3 parce que je pense qu'il illustre un beau code, en tant que package complet: de la conception du système à la façon de tabuler les caractères.

Shawn McGrath est un développeur de jeux basé à Toronto et le créateur du célèbre jeu de course de puzzle psychédélique PlayStation 3 Dyad. En savoir plus sur son jeu . Suivez-le sur Twitter .

Notes de bas de page

[ 1 ] Un analyseur lexical convertit les caractères du code source, (dans le contexte pertinent), en une série de jetons avec une signification sémantique. Le code source peut ressembler à:

x = y + 5;

Un analyseur lexical (ou «lexer» pour faire court), pourrait tokenize cette source comme telle:
x => variable
= => opérateur d'affectation
y => variable
+ => opérateur supplémentaire
5 => entier littéral
; => instruction de fin

Cette chaîne de jetons est la première des nombreuses étapes de conversion du code source en un programme en cours d'exécution. suite à une analyse lexicale, les jetons sont introduits dans un analyseur, puis un compilateur, puis un éditeur de liens, et enfin une machine virtuelle, (dans le cas des langages compilés un CPU). Des étapes intermédiaires peuvent être insérées entre ces étapes principales, mais celles énumérées sont généralement considérées comme les plus fondamentales.

[ 2 ] Un analyseur est (généralement) la prochaine étape logique suivant l'analyse lexicale dans la compréhension machine du langage, (langage informatique / code source dans ce contexte, mais il en va de même pour le langage naturel). L'entrée d'un analyseur est une liste de jetons générée par un analyseur lexical, et produit un arbre syntaxique: un «arbre d'analyse».

Dans l'exemple: x = y + 5, l'arborescence d'analyse ressemblerait à ceci:

[ 3 ] «const» est un mot-clé C ++ qui garantit qu'une variable ne peut pas être modifiée ou qu'une méthode ne changera pas le contenu de sa classe. «Const» est l'abréviation de «constant». Il convient de noter que C ++ inclut une solution de contournement, soit via const_cast [T] ou un cast de style C: (T *). En utilisant ces const breaks complètement, et par souci d'argumentation, je préfère ignorer leur existence et ne jamais les utiliser dans la pratique.

[ 4 ] STL signifie «bibliothèque de modèles standard». C'est un ensemble de conteneurs, d'algorithmes et de fonctions couramment utilisés par les programmeurs C ++. Il est pris en charge par tous les principaux fournisseurs de compilateurs avec différents niveaux d'optimisation et de rapports d'erreurs.

[ 5 ] GCC - GNU Compiler Collection: un ensemble de compilateurs prenant en charge plusieurs langages de programmation. Pour le cas de cet article, il fait référence au compilateur GNU C / C ++. GCC est un compilateur gratuit, avec le code source complet disponible gratuitement et fonctionne sur un large éventail d'ordinateurs et de systèmes d'exploitation. Les autres compilateurs couramment utilisés incluent: clang, Microsoft Visual C ++, IBM XL C / C ++, Intel C ++ Compiler.

Suggested posts

Non, ce n'est pas un jeu Game Boy perdu depuis longtemps

Non, ce n'est pas un jeu Game Boy perdu depuis longtemps

Bottes de pistolet. Ce sont des chaussures qui tirent des balles ou des lasers lorsque vous sautez.

Une femme coréenne donne un coup de pied à Overwatch et est accusée de tricherie [Mise à jour]

Une femme coréenne donne un coup de pied à Overwatch et est accusée de tricherie [Mise à jour]

Rencontrez Geguri. Elle a 17 ans et est très, très talentueuse chez Overwatch.

Related posts

Razer attaque Apple avec un tweet offensant, sexiste et de très mauvais goût

Razer attaque Apple avec un tweet offensant, sexiste et de très mauvais goût

Image : Razer (Twitter). Au moment de la publication de cet article, le tweet n'a pas été supprimé.

LEGO est parfait pour refaire les sprites de jeu

LEGO est parfait pour refaire les sprites de jeu

Les briques LEGO sont en blocs. Les vieux jeux vidéo aussi.

Nous avons joué à Doom VFR et c'était en sueur

Nous avons joué à Doom VFR et c'était en sueur

J'ai joué à Doom VFR pendant une heure tout en portant une veste de vol pour temps froid de l'US Air Force dans une pièce mal ventilée. J'étais plongé dans le troisième cercle du jeu: j'avais chaud, j'avais un engin de torture sur le visage et je jouais à Doom.

MORE COOL STUFF

«Danse avec les stars»: Mel C «éviscéré» sur l'élimination choquante de la graisse nocturne

«Danse avec les stars»: Mel C «éviscéré» sur l'élimination choquante de la graisse nocturne

La célébrité de "Dancing with the Stars" Mel C a été "éviscérée" par son élimination choquante de "Grease" Night après s'être retrouvée dans les deux derniers.

Randall Emmett triche ? Lala de 'Vanderpump Rules' a dit qu'elle ne s'inquiétait pas de lui 'rampant'

Randall Emmett triche ? Lala de 'Vanderpump Rules' a dit qu'elle ne s'inquiétait pas de lui 'rampant'

Lala Kent de 'Vanderpump Rules' a précédemment déclaré qu'elle ne pouvait pas voir Randall Emmett la tromper. Elle a également dit qu'il était son meilleur ami.

Fiancé de 90 jours': Varya Malina collecte des fonds pour son fiancé Geoffrey Paschel – demande aux fans une 'opportunité d'effacer son nom'

Fiancé de 90 jours': Varya Malina collecte des fonds pour son fiancé Geoffrey Paschel – demande aux fans une 'opportunité d'effacer son nom'

Ancienne star de '90 Day Fiancé', Varya Malina confirme qu'elle est avec Geoffrey Paschel et se mobilise pour lui, demandant à ses followers une aide financière.

Comment Colin Powell a-t-il rencontré sa femme, Alma Powell ?

Comment Colin Powell a-t-il rencontré sa femme, Alma Powell ?

Colin Powell et sa femme, Alma Powell, ont été mariés pendant près de six décennies. Le mariage du couple a vraiment duré l'épreuve du temps.

Comment changer votre nom sur Facebook

Comment changer votre nom sur Facebook

Vous voulez changer votre nom sur Facebook ? C'est facile à faire en quelques étapes simples.

7 000 marches sont les nouvelles 10 000 marches

7 000 marches sont les nouvelles 10 000 marches

Si vous êtes toujours en deçà de cet objectif quotidien arbitraire de 10 000 pas, nous avons de bonnes nouvelles. Votre santé peut en bénéficier tout autant si vous faites moins de pas.

Pourquoi ne pouvez-vous pas pomper votre propre gaz dans le New Jersey ?

Pourquoi ne pouvez-vous pas pomper votre propre gaz dans le New Jersey ?

Le Garden State est le seul État des États-Unis où il est illégal de pomper son propre gaz. Ce qui donne?

Vos chances de rencontrer un cerf à l'automne

Vos chances de rencontrer un cerf à l'automne

Et au fait, conduire au crépuscule et pendant les pleines lunes ne vous rend pas service non plus.

Shameless' Emma Kenney prétend que l'ensemble est devenu un 'endroit plus positif' après la sortie d'Emmy Rossum

Shameless' Emma Kenney prétend que l'ensemble est devenu un 'endroit plus positif' après la sortie d'Emmy Rossum

Emma Kenney, une ancienne sans vergogne, a parlé de son expérience de travail avec Emmy Rossum dans la série Showtime.

La star d'Hamilton Javier Muñoz sur le fait d'être immunodéprimé dans la pandémie : « J'étais littéralement terrorisé »

La star d'Hamilton Javier Muñoz sur le fait d'être immunodéprimé dans la pandémie : « J'étais littéralement terrorisé »

"Il n'y avait aucune chance à saisir", a déclaré à PEOPLE Javier Muñoz, séropositif et survivant du cancer.

Rachael Ray dit qu'elle est reconnaissante d'être en vie après l'incendie d'une maison et l'inondation d'un appartement

Rachael Ray dit qu'elle est reconnaissante d'être en vie après l'incendie d'une maison et l'inondation d'un appartement

"Tant de gens m'ont écrit et m'ont contacté et m'ont dit que nous avions tellement perdu aussi", a déclaré Rachael Ray sur Extra.

Freida Pinto, enceinte, partage des photos de sa baby shower 'Sweet' : 'Je me sens tellement bénie et chanceuse'

Freida Pinto, enceinte, partage des photos de sa baby shower 'Sweet' : 'Je me sens tellement bénie et chanceuse'

Freida Pinto, qui attend son premier enfant avec son fiancé Cory Tran, a fêté son petit en chemin avec une baby shower en plein air.

Authentification JWT et actualisation du jeton sur la plate-forme API

Authentification JWT et actualisation du jeton sur la plate-forme API

LexikJWTAuthenticationBundle fournit une authentification JWT (Json Web Token) pour votre API Symfony. Installation Si vous le souhaitez, vous pouvez modifier JWT_PASSPHRASE avant de générer les clés ssh.

18 idées de projets de science des données pour les débutants

18 idées de projets de science des données pour les débutants

Choisissez-en un ou tous, celui qui vous semble le plus amusant. Les projets de science des données sont un excellent moyen pour les débutants de se familiariser avec certaines des compétences et des langages de base en science des données dont vous aurez besoin pour poursuivre la science des données comme passe-temps ou comme carrière.

Comment vérifier NaN dans JavaScript

Vérifier NaN («pas un nombre») est aussi simple que vérifier l'auto-égalité dans JavaScript.

Comment vérifier NaN dans JavaScript

En JavaScript, la valeur spéciale NaN (qui signifie «pas un nombre») est utilisée pour représenter le résultat d'un calcul mathématique qui ne peut pas être représenté comme un nombre significatif. - Joshua Clanton sur une goutte de JavaScript La valeur spéciale NaN apparaît dans JavaScript lorsque les fonctions Math échouent (Math.

Suremploi : la tendance à travailler deux emplois à distance

Ces employés doubles de la FMH dépassent les attentes en matière de tenue de secrets, mais le risque en vaut-il la peine ? De mon podcast Digital Workspace Works. Veuillez profiter de l'épisode et des notes de l'émission : Lien du podcast : https://www.

Language