Underscore: _.isNumber(NaN) renvoie vrai

Créé le 15 déc. 2011  ·  38Commentaires  ·  Source: jashkenas/underscore

Comme NaN signifie "Pas un numéro", la vérification isNumber dans ce cas semble devoir retourner false. Je remarque d'autres discussions que c'est en fait exprès. Peut-être que la documentation devrait refléter ce fait explicitement.

enhancement fixed

Commentaire le plus utile

Bon alors j'ai commencé avec un problème simple. J'ai des entrées qui sont des chaînes, des nombres et éventuellement NaN. J'ai une solution simple en JavaScript de base utilisant parseInt et isFinite pour tester si l'analyse int réussit. Simple mais pas entièrement propre ou auto-descriptif de mon objectif. Donc, je décide d'utiliser ma bibliothèque goto totalement géniale pour faire ce genre de tâches. Je trouve une fonction lors de l'inspection initiale qui dit qu'elle prend une valeur et vous dit s'il s'agit d'un nombre, ce qui, selon ma définition commune, est ce que je pense que je veux.

Tout d'abord, pouvez-vous voir comment l'ajout d'une ligne à la documentation aiderait les utilisateurs de la bibliothèque à obtenir la bonne fonction pour le travail la première fois ? Deuxièmement, je demande à nouveau un exemple où cela est significatif.

On dirait que vous savez tout ce qu'il y a à savoir sur la langue. Donc, plutôt que de rejeter à l'aveuglette les solutions sombres que mon frère de 8e année pourrait me donner, ce serait peut-être bien si vous pouviez utiliser une partie de cette maîtrise pour nous enseigner le reste via une documentation impressionnante. Merci pour votre temps.

Tous les 38 commentaires

Tu as raison, c'est fait exprès. Et je ne pense pas qu'il faille le préciser explicitement. Quiconque a déjà travaillé avec les flottants IEEE 754 sait probablement que NaN n'est qu'une autre valeur à virgule flottante.

Connexe : discussion dans #321

Ce que je demande, c'est que, lorsque j'effectue des tâches Web courantes, une fonction nommée isNumber semble être un bon moyen de vérifier si une valeur trouvée répond à la définition utilisateur courante de "est-ce un nombre ?".

De plus, il me semble que la valeur NaN a été introduite en premier lieu pour aider à identifier les valeurs qui cassent les calculs. Dans l'esprit de la spécification, cela semble être la bonne réponse lorsque vous posez la question « est-ce un nombre ? » est en fait que ce n'est "pas un numéro". Si vous pouvez citer un exemple courant où vous voudriez que la réponse soit vraie pour cette question, je vais me taire maintenant :)

Vous recherchez isFinite , que les personnes connaissant JS devraient déjà connaître.

Bon alors j'ai commencé avec un problème simple. J'ai des entrées qui sont des chaînes, des nombres et éventuellement NaN. J'ai une solution simple en JavaScript de base utilisant parseInt et isFinite pour tester si l'analyse int réussit. Simple mais pas entièrement propre ou auto-descriptif de mon objectif. Donc, je décide d'utiliser ma bibliothèque goto totalement géniale pour faire ce genre de tâches. Je trouve une fonction lors de l'inspection initiale qui dit qu'elle prend une valeur et vous dit s'il s'agit d'un nombre, ce qui, selon ma définition commune, est ce que je pense que je veux.

Tout d'abord, pouvez-vous voir comment l'ajout d'une ligne à la documentation aiderait les utilisateurs de la bibliothèque à obtenir la bonne fonction pour le travail la première fois ? Deuxièmement, je demande à nouveau un exemple où cela est significatif.

On dirait que vous savez tout ce qu'il y a à savoir sur la langue. Donc, plutôt que de rejeter à l'aveuglette les solutions sombres que mon frère de 8e année pourrait me donner, ce serait peut-être bien si vous pouviez utiliser une partie de cette maîtrise pour nous enseigner le reste via une documentation impressionnante. Merci pour votre temps.

Je suis intéressé par ce que pense

Tout d'abord, pouvez-vous voir comment l'ajout d'une ligne à la documentation aiderait les utilisateurs de la bibliothèque à obtenir la bonne fonction pour le travail la première fois ?

Peut-être, bien sûr.

Deuxièmement, je demande à nouveau un exemple où cela est significatif.

Test de [[Classe]] == "Nombre" ? Cela devrait être évident, cela correspond aux chiffres. Ce ne sont peut-être pas des nombres naturels ou des nombres entiers ou des nombres finis ou des nombres réels ou des nombres rationnels ou tout autre sous-ensemble auquel vous vous attendiez, mais ce sont sûrement tous des nombres.

Cela dit, il y a des moments où ajouter trop de peluches dans la documentation peut être nocif. Mais je ne pense pas que ce soit un de ces moments. Cela pourrait potentiellement être utile pour les développeurs qui peuvent, dans leur état actuel, avoir une interprétation contradictoire du concept abstrait d'un nombre. Comme dans votre cas, où vous aviez déjà établi un modèle mental de l'ensemble pour lequel vous vouliez tester, puis trouvé une fonction qui _apparaissait_ pour effectuer ce test. +1 , même si je suis toujours intéressé par ce que pense

Oui -- il n'est pas du tout évident que _.isNumber renverra true ou false fois passé NaN . Je ne m'en souviendrais certainement pas sans l'avoir essayé. Ajout d'une note à la documentation dans le commit ci-dessus. Merci.

Merci les gars. Heureux de savoir que quelqu'un d'autre ne rencontrera peut-être pas le même problème.

Cela semble définitivement être un cas où la sémantique devrait l'emporter sur les faits techniques. Oui, NaN peut être un nombre selon certaines spécifications. Mais si je pose quelque chose, la question « Etes-vous un numéro ? » et il dit "Je ne suis pas un numéro" alors je devrais le croire. Pas un nombre - NaN - n'est définitivement pas un nombre... et isNumber devrait retourner faux.

Mettre le Gotcha dans les documents - au lieu de réparer les choses pour qu'ils aient un sens pour les humains - conduit simplement à moins de temps à coder et plus de temps à regarder la documentation qui vous gratte la tête.

Cela peut-il être reconsidéré s'il vous plaît ?

@contentfree : NaN est membre des flotteurs. Chaque nombre en JS est un flottant. Je pense que cela fait de NaN plus grand nombre possible. NaN ne signifie pas "Je ne suis pas un numéro". C'est la représentation numérique des non-nombres. Affaire classée dans mon livre. Utilisez isFinite si vous voulez tester des nombres qui ne sont pas NaN / Infinity / -Infinity .

Pas pour battre un cheval mort mais...

Pourquoi ne pas simplement faire
obj instanceOf Number

Il me semble que s'il existe un moyen plus lisible d'effectuer l'action nécessaire qui existe déjà dans la spécification javascript, je vais le faire de cette façon en n'utilisant pas une bibliothèque avec une bibliothèque inconnue (à moins que je perde du temps à enquêter).

Je vote que si nous ne réparons pas cela pour qu'il soit sémantiquement correct, nous devrions simplement supprimer les frais généraux inutiles et les maux de tête de la bibliothèque.

L'opérateur instanceof ne fonctionnera que pour les objets, pas les primitives : new Number(5) instanceof Number == true; 5 instanceof Number == false . Il ne fonctionnera pas non plus à travers les cadres dans les navigateurs. [[Class]] vérification de Object::toString est généralement considérée comme la méthode de vérification de type la plus fiable en JavaScript.

+1 pour rendre la bibliothèque plus utile et dans le même cas éduquer les utilisateurs novices en JavaScript.

Suggestion:
_.isNumber(objet, [isFinite]);

Cela permettra un simple ajout d'un vrai à votre implémentation actuelle lorsque vous serez pris par ce piège tout en apprenant que isFinite était probablement ce que vous recherchiez en premier lieu.

Mon 2c

@nickl- Idée intéressante, mais... pourquoi ne pas simplement utiliser isFinite dans ce cas ?

Alternativement, puisque _.isNaN existe déjà, _.isFinite pourrait potentiellement être ajouté pour la symétrie :

_.isFinite = function (value) {
  return value > -1 / 0 && value < 1 / 0;
};

@kitcambridge

Le seul problème que je prévois avec cela est que les chaînes passeraient comme finies "0x0", "0xF", "2", etc. Donc, quoi qu'il en soit, il devra être combiné avec _.isNumber ou équivalent :

_.isFinite = function (obj) {
  return obj > -1/0 && obj < 1/0 && _.isNumber(obj);
};

Peut éliminer cinq caractères en utilisant val === +val pour les tests de nombre :

_.isFinite = function (obj) {
  return obj > -1/0 && obj < 1/0 && obj === +obj;
};

@octatone Bien sûr, je pense que c'est juste... techniquement, ces chaînes _sont_ finies, car elles peuvent être utilisées dans des comparaisons numériques (l'interpréteur devrait les contraindre à des nombres), mais votre proposition est plus cohérente avec les fonctions de vérification de type Underscore existantes.

Qu'en est-il de _,isValidNumber qui est en ligne avec le _.isValidDate existant et ne confond pas l'argument isFinite ?

@nickl-

NaN et Infinity _sont_ des nombres valides. Je pense que nommer cette fonction isValidNumber confondrait son objectif ou voulez-vous dire renommer isNumber en isValidNumber?

NaN et Infinity _sont_ des nombres valides. Je pense que nommer cette fonction isValidNumber confondrait son objectif.

Je suis d'accord.

Je n'ai pas dit renommer...

J'ai aussi été surpris de découvrir que _.isNumber(NaN) === true .

Je pense que j'ai tendance à être d'accord avec https://github.com/jashkenas/underscore/issues/406#issuecomment -4144992 de @contentfree. En génie logiciel, nous avons même un principe pour ce cas précis : Principe du moindre étonnement . ;-)

Et après avoir vu autant de gens confus à ce sujet, je pense qu'il serait logique de faire en sorte que la fonction fasse ce que les humains non-hardcore-js-codeurs attendent qu'elle fasse quand ils regardent son nom.

Juste mon petit 2¢…™

_.isNaN est également déroutant. Dans le dossier :

Remarque : ce n'est pas la même chose que la fonction native isNaN, qui renverra également true pour de nombreuses autres valeurs non numériques, telles que undefined.

au sens normal, undefined est en effet Not-A-Number.

Je vote pour la sémantique constructive, isNumber == Not a number lol. Je pense que si vous avez besoin d'entrer dans les détails sur la vérification des valeurs à virgule flottante, faites-le d'une autre manière ?

Je vote pour la sémantique constructive,

Non, c'est un nombre par [[Class]] tout comme -Infinity , Infinity , & Object(2) . C'est l'une de ces choses que les développeurs apprennent, comme les fonctions sont aussi des objets. Il y a de fortes chances que vous souhaitiez effectuer une certaine forme de validation sur votre numéro, ce qui est hors de portée de cette méthode. Par exemple, est-il supérieur à -1 , inférieur à Math.pow(2,32) ou à un nombre entier. Dans les cas de -Infinity , Infinity ou NaN j'utilise _.isFinite comme validateur. De même, _.isDate ne valide pas si l'objet date représente une date valide.

Qu'en est-il de la mise à jour de la documentation avec un "voir aussi: isFinite" et "voir aussi: «essai énumérant des méthodes pour déterminer si quelque chose est une valeur numérique que vous pouvez réellement utiliser»"

@michaelficarra Je sais que dans la spécification NaN est vraiment un type numérique. Mais est-ce ce que pense un programmeur lorsqu'il demande s'il s'agit d'un nombre ?

Ce que la plupart d'entre nous veulent savoir, la plupart du temps, est-ce que je peux l'utiliser pour l'arithmétique de base. Donc, vous devez aller isNumber(x) && isFinite(x). Et c'est bien je suppose. Mais c'est un gros piège pour un nouveau programmeur et ne lit pas bien.

D'un point de vue strictement anglais, Not a Number (NaN) renvoyant vrai à partir d'un test appelé isNumber n'a aucun sens. Cela ne serait-il pas mieux nommé isNumeric ou isNumericType ?

Je ne doute pas que cela ajoutera des bogues et gaspillera de nombreuses heures, du moins la première fois que les gens se heurteront à cela.

Donc, vous devez aller isNumber(x) && isFinite(x).

J'ai récemment aligné une implémentation de _.isFinite pour suivre ES6 Number.isFinite .
Il garantit que la valeur est un nombre primitif, qu'elle est finie et qu'elle doit gérer ce cas courant.

Pas besoin de changer le comportement de _.isNumber qui s'aligne avec les autres méthodes de vérification [[Class]] .

D'un point de vue strictement anglais, Not a Number (NaN) renvoyant vrai à partir d'un test appelé isNumber n'a aucun sens. Cela ne serait-il pas mieux nommé isNumeric ou isNumericType ?

Voir mon commentaire sur la validation, _.isNumber , et _.isDate .

@jdalton Vous avez raison de dire que le comportement est constant avec _.isDate. Et vous faites un excellent travail avec le soulignement, donc je pense que la décision vous appartient vraiment.

Mais cela semble tellement contre-intuitif que quelque chose appelé pas un nombre est un nombre.

@Walms 100% d'accord. Je pense que dans ce cas, le programmeur et l'intuition devraient annuler la "correction" absolue de la déclaration donnée NaN est _techniquement_ numérique.

Mais cela semble tellement contre-intuitif que quelque chose appelé pas un nombre est un nombre.

Dans ce contexte, est-ce plus contre-intuitif que _.isNumber renvoyant true pour les objets numériques, Object(2) qui sont de type object ?

Ce sont des méthodes [[Class]] . Peut-être que cela doit être précisé dans la documentation.

J'ai déjà proposé une alternative viable qui consiste simplement à faire suivre à _.isFinite ES6 Number.isFinite . Si vous voulez un is-number basique, utilisez typeof x == 'number' . Si vous voulez une confirmation que la valeur n'est pas NaN , Infinity , ou -Infinity , tous les [[Class]] de Number , alors _.isFinite est la voie à suivre.

Regardez underscore-contrib pour des méthodes de nombre plus fines. Il a _.isNumeric , _.isInteger , _.isZero , _.isEven , _.isOdd , _.isFloat , _.isNegative , & _.isPositive .

@jdalton Je pense que vos deux derniers commentaires ont vraiment aidé à clarifier pourquoi je pense que cela prête à confusion.

Au départ, je n'ai pas vu que _.isNumber avait un contexte du fait qu'il était préfixé par is, ce qui signifie qu'il s'agissait d'une vérification de type. Avec ce contexte, vous avez tout à fait raison, cela n'a aucun sens que _.isNumber renvoie false pour NaN.

Mais alors underscore-contrib et isFinite semblent briser ce contexte. Comme ils commencent par l'est, ils regardent pourtant la valeur plutôt que le type. Et c'est là que je pense que la confusion est qu'il n'y a pas de moyen clair de déterminer le contexte à partir du nom de la fonction.

Cela dit, je ne vois aucun moyen de résoudre ce problème.

Je pense que le problème ici est qu'il y a certaines choses à propos de Numbers qui ne sont pas intuitives. Cependant, ils se comportent de manière cohérente. Il serait possible de rendre _.isNumber plus intuitif dans ce cas en le rendant moins cohérent, ce qui au final le rendrait moins intuitif dans d'autres cas.

Voici quelques exemples concrets :

# If you add two Numbers together you always get another Number back
# This function should always return true no matter what you pass it
closedUnderAddition = (a, b) -> !isNumber(a) || !isNumber(b) || isNumber(a + b)

closedUnderAddition(1,1) == true
closedUnderAddition(Number.MAX_INT,2**970) == true # false if isNumber checks finiteness

# If you divide two Numbers you always get another Number back
closedUnderDivision = (a, b) -> !isNumber(a) || !isNumber(b) || isNumber(a / b)

closedUnderDivision(1, 2) == true
closedUnderDivision(1, 0) == true # false if isNumber checks finiteness
closedUnderDivision(0, 0) == true # false if isNumber checks finiteness or NaN-ness

# Anything you cast to a Number is a Number
castsToNumber = (x) -> isNumber(Number(x))

castsToNumber(1) == true
castsToNumber(Infinity) == true # false if isNumber checks finiteness
castsToNumber("bees") == true # false if isNumber checks finiteness or NaN-ness
castsToNumber("3") == true

Comme l'a dit michaelficarra :

NaN ne veut pas dire "Je ne suis pas un numéro". C'est la représentation numérique des non-nombres.

Ou, pour le dire autrement, il y a des "nombres" et des nombres. NaN n'est peut-être pas un "nombre", mais c'est absolument un nombre avec Infinity, -Infinity et des choses ridicules comme -0. Aussi sauvage et laineux soit-il, la définition de Nombre est bien spécifiée et se comporte de manière cohérente.

"nombre" d'autre part est un concept mal défini qui, selon la personne à qui vous parlez, pourrait inclure tout ou rien de ce qui précède , et peut-être des chaînes numériques, des bools, minuit et toutes sortes de choses. C'est bien, mais il n'y aura jamais de solution qui adhère à la définition de tout le monde.

Je pense que pour cette raison, il s'agit essentiellement d'un problème de documentation.

Est-ce que "nombre" a également cessé de ressembler à un mot pour quelqu'un d'autre ?

Bons points, @sgentle. Je vote pour la cohérence plutôt que pour l'intuitivité, car la première est une mesure objective alors que nous ne serons pas tous d'accord sur ce qui est intuitif (comme le montre ce fil).

Ne traitez pas NaN comme « Pas un numéro ». NaN est une valeur spéciale.

Ne traitez pas NaN comme « Pas un numéro ». NaN est une valeur spéciale.

Ouais mais ça s'appelle "Pas un numéro".
Pour moi c'est comme ça
var _false = "true" ;
Oui, vous pouvez apprendre que _false est vrai, mais c'est déroutant sans raison valable.

@Walms C'est une mauvaise réputation mais ce n'est pas la faute de JS. Nous pouvons blâmer le gars qui l'a nommé "NaN". http://en.wikipedia.org/wiki/NaN

Aargh, je souhaite que _.isNumber(NaN) revienne false ... de sérieux grattages de tête ici jusqu'à ce que je réalise que c'était la raison.

if (isNaN(Number(value))) {
  alert('Number required.');
}

@pspi D'accord. Faire en sorte que _.isNumber() se comporte correctement au sens _sémantique_ signifie que je ne l'utiliserai jamais réellement. ._isFinite() semble être la fonction qui fonctionne comme je m'y attendais.

Et bien sûr, un jour après avoir posté ceci, je découvre que _.isFinite('1') est true , même si l'argument n'est pas un nombre.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

arypbatista picture arypbatista  ·  3Commentaires

clouddueling picture clouddueling  ·  3Commentaires

zackschuster picture zackschuster  ·  5Commentaires

haggholm picture haggholm  ·  8Commentaires

jdalton picture jdalton  ·  4Commentaires