web-dev-qa-db-fra.com

Comment dois-je stocker les valeurs "inconnu" et "manquant" dans une variable, tout en conservant la différence entre "inconnu" et "manquant"?

Considérez ceci comme une question "académique". Je me demandais comment éviter les NULL de temps en temps et c'est un exemple où je ne peux pas trouver de solution satisfaisante.


Supposons que je stocke les mesures là où la mesure est parfois impossible (ou manquante). Je voudrais stocker cette valeur "vide" dans une variable tout en évitant NULL. D'autres fois, la valeur peut être inconnue. Ainsi, ayant les mesures pour un certain laps de temps, une requête sur une mesure dans cette période de temps pourrait renvoyer 3 types de réponses:

  • La mesure réelle à ce moment (par exemple, toute valeur numérique incluant 0)
  • Une valeur "manquante"/"vide" (c'est-à-dire qu'une mesure a été effectuée, et la valeur est connue comme étant vide à ce point).
  • Une valeur inconnue (c'est-à-dire qu'aucune mesure n'a été effectuée à ce stade. Elle peut être vide, mais il peut également s'agir de toute autre valeur).

Précision importante:

En supposant que vous disposiez d'une fonction get_measurement() renvoyant l'une des valeurs "vide", "inconnu" et une valeur de type "entier". Avoir une valeur numérique implique que certaines opérations peuvent être effectuées sur la valeur de retour (multiplication, division, ...) mais l'utilisation de telles opérations sur des valeurs NULL plantera l'application si elle n'est pas interceptée.

J'aimerais pouvoir écrire du code, en évitant les contrôles NULL, par exemple (pseudocode):

>>> value = get_measurement()  # returns `2`
>>> print(value * 2)
4

>>> value = get_measurement()  # returns `Empty()`
>>> print(value * 2)
Empty()

>>> value = get_measurement()  # returns `Unknown()`
>>> print(value * 2)
Unknown()

Notez qu'aucune des instructions print n'a provoqué d'exceptions (car aucun NULL n'a été utilisé). Ainsi, les valeurs vides et inconnues se propageraient si nécessaire et la vérification si une valeur est réellement "inconnue" ou "vide" pourrait être retardée jusqu'à ce qu'elle soit vraiment nécessaire (comme stocker/sérialiser la valeur quelque part).


Side-Note: La raison pour laquelle j'aimerais éviter les NULLs, c'est principalement un casse-tête. Si je veux faire avancer les choses, je ne suis pas opposé à l'utilisation de NULL, mais j'ai trouvé que les éviter peut rendre le code beaucoup plus robuste dans certains cas.

57
exhuma

La façon courante de le faire, au moins avec les langages fonctionnels, est d'utiliser une union discriminée. Il s'agit alors d'une valeur faisant partie d'un entier valide, d'une valeur indiquant "manquant" ou d'une valeur indiquant "inconnu". En F #, cela pourrait ressembler à quelque chose comme:

type Measurement =
    | Reading of value : int
    | Missing
    | Unknown of value : RawData

Une valeur Measurement sera alors un Reading, avec une valeur int, ou un Missing, ou un Unknown avec les données brutes comme value (si nécessaire).

Cependant, si vous n'utilisez pas un langage qui prend en charge les syndicats discriminés, ou leur équivalent, ce modèle ne vous sera probablement pas très utile. Donc là, vous pouvez par exemple utiliser une classe avec un champ enum qui indique lequel des trois contient les données correctes.

85
David Arno

Si vous ne savez pas déjà ce qu'est une monade, aujourd'hui serait une excellente journée pour apprendre. J'ai une introduction douce pour les programmeurs OO ici:

https://ericlippert.com/2013/02/21/monads-part-one/

Votre scénario est une petite extension de la "peut-être monade", également appelée Nullable<T> En C # et Optional<T> Dans d'autres langues.

Supposons que vous ayez un type abstrait pour représenter la monade:

abstract class Measurement<T> { ... }

puis trois sous-classes:

final class Unknown<T> : Measurement<T> { ... a singleton ...}
final class Empty<T> : Measurement<T> { ... a singleton ... }
final class Actual<T> : Measurement<T> { ... a wrapper around a T ...}

Nous avons besoin d'une implémentation de Bind:

abstract class Measurement<T>
{ 
    public Measurement<R> Bind(Func<T, Measurement<R>> f)
  {
    if (this is Unknown<T>) return Unknown<R>.Singleton;
    if (this is Empty<T>) return Empty<R>.Singleton;
    if (this is Actual<T>) return f(((Actual<T>)this).Value);
    throw ...
  }

À partir de cela, vous pouvez écrire cette version simplifiée de Bind:

public Measurement<R> Bind(Func<A, R> f) 
{
  return this.Bind(a => new Actual<R>(f(a));
}

Et maintenant vous avez terminé. Vous avez un Measurement<int> En main. Vous voulez le doubler:

Measurement<int> m = whatever;
Measurement<int> doubled = m.Bind(a => a * 2);
Measurement<string> asString = m.Bind(a => a.ToString());

Et suivez la logique; si m est Empty<int> alors asString est Empty<String>, excellent.

De même, si nous avons

Measurement<int> First()

et

Measurement<double> Second(int i);

on peut alors combiner deux mesures:

Measurement<double> d = First().Bind(Second);

et encore, si First() est Empty<int> alors d est Empty<double> et ainsi de suite.

L'étape clé consiste à obtenir l'opération de liaison correcte . Réfléchissez bien.

58
Eric Lippert

Je pense que dans ce cas une variation sur un modèle d'objet nul serait utile:

public class Measurement
{
    private int value;
    private bool isUnknown = false;
    private bool isMissing = false;

    private Measurement() { }
    public Measurement(int value) { this.value = value; }

    public int Value {
        get {
            if (!isUnknown && !isMissing)
            {
                return this.value;
            }
            throw new SomeException("...");
        }                   
    }

    public static readonly Measurement Unknown = new Measurement
    {
        isUnknown = true
    };

    public static readonly Measurement Missing = new Measurement
    {
        isMissing = true
    };
}

Vous pouvez le transformer en une structure, remplacer Equals/GetHashCode/ToString, ajouter des conversions implicites depuis ou vers int, et si vous voulez un comportement de type NaN, vous pouvez également implémenter vos propres opérateurs arithmétiques de sorte que par exemple. Measurement.Unknown * 2 == Measurement.Unknown.

Cela dit, C # est Nullable<int> implémente tout cela, la seule mise en garde étant que vous ne pouvez pas différencier les différents types de null. Je ne suis pas une personne Java, mais je crois savoir que OptionalInt de Java est similaire, et d'autres langages ont probablement leurs propres installations pour représenter un type Optional .

18
Maciej Stachowski

Si vous DEVEZ littéralement utiliser un entier, il n'y a qu'une seule solution possible. Utilisez certaines des valeurs possibles comme "nombres magiques" qui signifient "manquant" et "inconnu"

par exemple 2.147.483.647 et 2.147.483.646

Si vous avez juste besoin de l'int pour des mesures "réelles", créez une structure de données plus compliquée

class Measurement {
    public bool IsEmpty;
    public bool IsKnown;
    public int Value {
        get {
            if(!IsEmpty && IsKnown) return _value;
            throw new Exception("NaN");
            }
        }
}

Précision importante:

Vous pouvez atteindre l'exigence mathématique en surchargeant les opérateurs pour la classe

public static Measurement operator+ (Measurement a, Measurement b) {
    if(a.IsEmpty) { return b; }
    ...etc
}
14
Ewan

Si vos variables sont des nombres à virgule flottante, IEEE754 (la norme de nombre à virgule flottante qui est prise en charge par la plupart des processeurs et des langages modernes) a votre dos: c'est une fonctionnalité peu connue, mais la norme n'en définit pas un, mais ne famille entière de NaN (pas un nombre) valeurs, qui peuvent être utilisées pour des significations arbitraires définies par l'application. Dans les flottants simple précision, par exemple, vous avez 22 bits libres que vous pouvez utiliser pour distinguer entre 2 ^ {22} types de valeurs non valides.

Normalement, les interfaces de programmation n'exposent qu'une seule d'entre elles (par exemple, nan) de Numpy; Je ne sais pas s'il existe un moyen intégré de générer les autres autres que la manipulation explicite de bits, mais il s'agit simplement d'écrire quelques routines de bas niveau. (Vous en aurez également besoin pour les différencier, car, par conception, a == b Renvoie toujours false lorsque l'un d'eux est un NaN.)

Il est préférable de les utiliser que de réinventer votre propre "numéro magique" pour signaler des données invalides, car elles se propagent correctement et signalent l'invalidité: par exemple, vous ne risquez pas de vous tirer une balle dans le pied si vous utilisez un average() fonction et oubliez de vérifier vos valeurs spéciales.

Le seul risque est que les bibliothèques ne les prennent pas correctement en charge, car elles sont une fonctionnalité assez obscure: par exemple, une bibliothèque de sérialisation peut les `` aplatir '' toutes au même nan (ce qui lui semble équivalent dans la plupart des cas).

11
Federico Poloni

Après réponse de David Arno , vous pouvez faire quelque chose comme une union discriminée dans la POO, et dans un style fonctionnel objet tel que celui offert par Scala, par Java 8 types fonctionnels, ou une Java FP bibliothèque telle que Vavr ou Fugue il semble assez naturel de écrire quelque chose comme:

var value = Measurement.of(2);
out.println(value.map(x -> x * 2));

var empty = Measurement.empty();
out.println(empty.map(x -> x * 2));

var unknown = Measurement.unknown();
out.println(unknown.map(x -> x * 2));

impression

Value(4)
Empty()
Unknown()

( Implémentation complète sous forme de Gist .)

Un FP langage ou bibliothèque fournit d'autres outils comme Try (aka Maybe ) (un objet qui contient soit une valeur, soit une erreur) et Either (un objet qui contient soit une valeur de réussite soit une valeur d'échec) qui pourrait également être utilisé ici.

6
David Moles

La solution idéale à votre problème dépendra de la raison pour laquelle vous vous souciez de la différence entre une défaillance connue et une mesure non fiable connue, et des processus en aval que vous souhaitez prendre en charge. Notez que les "processus en aval" dans ce cas n'excluent pas les opérateurs humains ou les autres développeurs.

Le simple fait de proposer une "deuxième version" de null ne donne pas à l'ensemble de processus en aval suffisamment d'informations pour dériver un ensemble raisonnable de comportements.

Si vous vous fiez plutôt à des hypothèses contextuelles sur la source des mauvais comportements du code en aval, j'appellerais cette mauvaise architecture.

Si vous en savez suffisamment pour faire la distinction entre un motif d'échec et un échec sans raison connue, et que ces informations vont éclairer les comportements futurs, vous devez communiquer ces connaissances en aval ou les gérer en ligne.

Quelques modèles pour gérer cela:

  • Types de somme
  • Syndicats discriminés
  • Objets ou structures contenant une énumération représentant le résultat de l'opération et un champ pour le résultat
  • Chaînes magiques ou nombres magiques impossibles à obtenir en fonctionnement normal
  • Exceptions, dans les langues où cette utilisation est idiomatique
  • Réaliser qu'il n'y a en fait aucune valeur à différencier ces deux scénarios et simplement utiliser null
2
Iron Gremlin

Si je voulais "faire quelque chose" plutôt qu'une solution élégante, le hack rapide et sale serait simplement d'utiliser les chaînes "inconnu", "manquant" et "représentation de chaîne de ma valeur numérique", qui serait alors converti à partir d'une chaîne et utilisé selon les besoins. Mis en œuvre plus rapidement que d'écrire ceci, et dans au moins certaines circonstances, tout à fait adéquat. (Je forme maintenant un pool de paris sur le nombre de downvotes ...)

The Gist si la question semble être "Comment puis-je retourner deux informations non liées à partir d'une méthode qui retourne un seul int? Je ne veux jamais vérifier mes valeurs de retour, et les valeurs nulles sont mauvaises, ne les utilisez pas."

Voyons ce que vous voulez réussir. Vous passez soit un int, soit un non-int justification pour laquelle vous ne pouvez pas donner l'int. La question affirme qu'il n'y aura que deux raisons, mais quiconque a déjà fait une énumération sait que toute liste s'allongera. La possibilité de spécifier d'autres justifications est tout à fait logique.

Au départ, cela semble donc être un bon argument pour lever une exception.

Lorsque vous voulez dire à l'appelant quelque chose de spécial qui n'est pas dans le type de retour, les exceptions sont souvent le système approprié: les exceptions ne sont pas seulement pour les états d'erreur, et vous permettent de renvoyer beaucoup de contexte et de justification pour expliquer pourquoi vous pouvez simplement 't int aujourd'hui.

Et c'est le SEUL système qui vous permet de renvoyer des entiers valides garantis et de garantir que chaque opérateur int et chaque méthode qui accepte des ints peuvent accepter la valeur de retour de cette méthode sans jamais avoir besoin de vérifier les valeurs invalides comme null ou les valeurs magiques.

Mais les exceptions ne sont vraiment une solution valable que si, comme son nom l'indique, il s'agit d'un cas exceptionnel , et non du cours normal des affaires.

Et un try/catch et un gestionnaire est tout autant un passe-partout qu'une vérification nulle, ce à quoi on s'est opposé en premier lieu.

Et si l'appelant ne contient pas le try/catch, alors l'appelant de l'appelant doit, et ainsi de suite.


Une deuxième passe naïve consiste à dire "C'est une mesure. Des mesures de distance négatives sont peu probables." Donc, pour une mesure Y, vous pouvez simplement avoir des constantes pour

  • -1 = inconnu,
  • -2 = impossible à mesurer,
  • -3 = a refusé de répondre,
  • -4 = connu mais confidentiel,
  • -5 = varie selon la phase de la lune, voir tableau 5a,
  • -6 = quatre dimensions, mesures données dans le titre,
  • -7 = erreur de lecture du système de fichiers,
  • -8 = réservé pour une utilisation future,
  • -9 = carré/cube donc Y est identique à X,
  • -10 = est un écran de contrôle, donc n'utilise pas les mesures X, Y: utilisez X comme diagonale d'écran,
  • -11 = écrit les mesures au dos d'un reçu et il a été blanchi dans l'illisibilité mais je pense que c'était soit 5 ou 17,
  • -12 = ... vous avez l'idée.

C'est ainsi que cela se fait dans de nombreux anciens systèmes C, et même dans les systèmes modernes où il existe une véritable contrainte à int, et vous ne pouvez pas l'encapsuler dans une structure ou une monade d'un certain type.

Si les mesures peuvent être négatives, vous agrandissez simplement votre type de données (par exemple, long int) et les valeurs magiques sont supérieures à la plage de l'int, et commencez idéalement par une valeur qui apparaîtra clairement dans un débogueur.

Il y a de bonnes raisons de les avoir comme variable distincte, plutôt que d'avoir simplement des nombres magiques. Par exemple, typage strict, maintenabilité et conformité aux attentes.


Dans notre troisième tentative, nous examinons donc les cas dans lesquels le cours normal des affaires a des valeurs non int. Par exemple, si une collection de ces valeurs peut contenir plusieurs entrées non entières. Cela signifie qu'un gestionnaire d'exceptions peut être la mauvaise approche.

Dans ce cas, cela semble être un bon cas pour une structure qui passe l'int, et la justification. Encore une fois, cette justification peut simplement être une constante comme celle ci-dessus, mais au lieu de les deux dans le même int, vous les stockez en tant que parties distinctes d'une structure. Initialement, nous avons la règle que si la justification est définie, l'int ne sera pas défini. Mais nous ne sommes plus liés à cette règle; nous pouvons également fournir des justifications pour les nombres valides, le cas échéant.

Quoi qu'il en soit, chaque fois que vous l'appelez, vous avez toujours besoin d'un passe-partout, pour tester la justification pour voir si l'int est valide, puis retirez et utilisez la partie int si la justification nous le permet.

C'est là que vous devez enquêter sur votre raisonnement derrière "ne pas utiliser null".

Comme les exceptions, null est censé signifier un état exceptionnel.

Si un appelant appelle cette méthode et ignore complètement la partie "justification" de la structure, s'attendant à un nombre sans aucune gestion d'erreur, et il obtient un zéro, alors il traitera le zéro comme un nombre et se trompera. S'il obtient un nombre magique, il le traitera comme un nombre et se trompera. Mais s'il obtient une valeur nulle, il tombera , comme il devrait le faire.

Donc, chaque fois que vous appelez cette méthode, vous devez vérifier sa valeur de retour, mais vous gérez les valeurs non valides, qu'elles soient dans la bande ou hors bande, essayez/rattrapez, vérifiez la structure pour un composant "justification", vérifiez l'int pour un nombre magique, ou vérifier un int pour un nul ...

L'alternative, pour gérer la multiplication d'une sortie qui pourrait contenir un int invalide et une justification comme "Mon chien a mangé cette mesure", est de surcharger l'opérateur de multiplication pour cette structure.

... Et puis surchargez tous les autres opérateurs de votre application susceptibles d'être appliqués à ces données.

... Et puis surcharger toutes les méthodes qui pourraient prendre des pouces.

... Et tous de ces surcharges devront toujours contenir des vérifications pour les entiers invalides, juste pour que vous puissiez traiter le renvoie le type de cette méthode comme s'il s'agissait toujours d'un entier valide au moment où vous l'appelez.

La prémisse d'origine est donc fausse de diverses manières:

  1. Si vous avez des valeurs non valides, vous ne pouvez pas éviter de rechercher ces valeurs non valides à tout moment dans le code où vous gérez les valeurs.
  2. Si vous retournez autre chose qu'un int, vous ne retournez pas un int, vous ne pouvez donc pas le traiter comme un int. La surcharge de l'opérateur vous permet de faire semblant , mais c'est juste de faire semblant.
  3. Un int avec des nombres magiques (y compris NULL, NAN, Inf ...) n'est plus vraiment un int, c'est une structure de pauvre.
  4. Éviter les valeurs nulles ne rendra pas le code plus robuste, il masquera simplement les problèmes avec les entiers, ou les déplacera dans une structure de gestion des exceptions complexe.
2
Dewi Morgan

Je ne comprends pas la prémisse de votre question, mais voici la réponse nominale. Pour manquant ou vide, vous pouvez faire math.nan (Pas un nombre). Vous pouvez effectuer toutes les opérations mathématiques sur math.nan et il restera math.nan.

Vous pouvez utiliser None (null de Python) pour une valeur inconnue. De toute façon, vous ne devriez pas manipuler une valeur inconnue, et certains langages (Python n'en fait pas partie) ont des opérateurs null spéciaux afin que l'opération ne soit effectuée que si la valeur est non nulle, sinon la valeur reste nulle.

D'autres langages ont des clauses de garde (comme Swift ou Ruby), et Ruby a un retour anticipé conditionnel).

J'ai vu cela résolu dans Python de différentes manières:

  • avec une structure de données wrapper, car les informations numériques concernent généralement une entité et ont un temps de mesure. L'encapsuleur peut remplacer les méthodes magiques comme __mult__ afin qu'aucune exception ne soit levée lorsque vos valeurs inconnues ou manquantes apparaissent. Numpy et pandas pourraient avoir une telle capacité en eux.
  • avec une valeur sentinelle (comme votre Unknown ou -1/-2) et une instruction if
  • avec un drapeau booléen séparé
  • avec une structure de données paresseuse - votre fonction effectue une opération sur la structure, puis elle revient, la fonction la plus externe qui a besoin du résultat réel évalue la structure de données paresseuse
  • avec un pipeline d'opérations paresseux - similaire au précédent, mais celui-ci peut être utilisé sur un ensemble de données ou une base de données
1
noɥʇʎԀʎzɐɹƆ

La façon dont la valeur est stockée en mémoire dépend du langage et des détails d'implémentation. Je pense que vous voulez dire comment l'objet doit se comporter avec le programmeur. (Voici comment je lis la question, dites-moi si je me trompe.)

Vous avez déjà proposé une réponse à cela dans votre question: utilisez votre propre classe qui accepte toute opération mathématique et se retourne sans lever d'exception. Vous dites que vous le voulez parce que vous voulez éviter les contrôles nuls.

Solution 1: n'évitez pas les contrôles nuls

Missing peut être représenté par math.nan
Unknown peut être représenté par None

Si vous avez plusieurs valeurs, vous pouvez filter() pour n'appliquer l'opération qu'aux valeurs qui ne sont pas Unknown ou Missing, ou toutes les valeurs que vous souhaitez ignorer pour la fonction.

Je ne peux pas imaginer un scénario où vous avez besoin d'un contrôle nul sur une fonction qui agit sur un seul scalaire. Dans ce cas, il est bon de forcer les vérifications nulles.


Solution 2: tilisez un décorateur qui détecte les exceptions

Dans ce cas, Missing peut augmenter MissingException et Unknown peut augmenter UnknownException lorsque des opérations sont effectuées sur celui-ci.

@suppressUnknown(value=Unknown) # if an UnknownException is raised, return this value instead
@suppressMissing(value=Missing)
def sigmoid(value):
    ...

L'avantage de cette approche est que les propriétés de Missing et Unknown ne sont supprimées que lorsque vous demandez explicitement qu'elles soient supprimées. Un autre avantage est que cette approche est auto-documentée: chaque fonction montre si elle attend ou non une inconnue ou une manquante et comment la fonction.

Lorsque vous appelez une fonction ne s'attend pas à ce qu'un manquant obtienne un manquant, la fonction augmentera immédiatement, vous montrant exactement où l'erreur s'est produite au lieu d'échouer et de propager silencieusement un manquant dans la chaîne d'appels. Il en va de même pour Unknown.

sigmoid peut toujours appeler sin, même s'il n'attend pas de Missing ou Unknown, puisque le décorateur de sigmoid interceptera l'exception .

1
noɥʇʎԀʎzɐɹƆ

Supposons que l'on récupère le nombre de CPU d'un serveur. Si le serveur est éteint ou a été mis au rebut, cette valeur n'existe tout simplement pas. Ce sera une mesure qui n'a aucun sens (peut-être que "manquant"/"vide" ne sont pas les meilleurs termes). Mais la valeur est "connue" pour être absurde. Si le serveur existe, mais que le processus de récupération de la valeur se bloque, sa mesure est valide, mais échoue, entraînant une valeur "inconnue".

Ces deux conditions ressemblent à des erreurs, donc je dirais que la meilleure option ici est simplement d'avoir get_measurement() lever immédiatement ces deux exceptions (telles que DataSourceUnavailableException ou SpectacularFailureToGetDataException, respectivement). Ensuite, si l'un de ces problèmes se produit, le code de collecte de données peut y réagir immédiatement (par exemple en réessayant dans ce dernier cas), et get_measurement() n'a qu'à retourner un int dans le cas où il peut obtenir avec succès les données de la source de données - et vous savez que le int est valide.

Si votre situation ne prend pas en charge les exceptions ou ne peut pas en faire grand cas, alors une bonne alternative consiste à utiliser des codes d'erreur, peut-être renvoyés via une sortie distincte à get_measurement(). Il s'agit du modèle idiomatique en C, où la sortie réelle est stockée dans un pointeur d'entrée et un code d'erreur est renvoyé comme valeur de retour.

0
TheHansinator

R a un support de valeur manquante intégré. https://medium.com/coinmonks/dealing-with-missing-data-using-r-3ae428da2d17

Edit: parce que j'ai été sous-voté, je vais expliquer un peu.

Si vous allez traiter des statistiques, je vous recommande d'utiliser un langage statistique tel que R car R est écrit par des statisticiens pour des statisticiens. Les valeurs manquantes sont un sujet tellement important qu'elles vous enseignent tout un semestre. Et il n'y a de gros livres que sur les valeurs manquantes.

Vous pouvez cependant vous souhaitez marquer vos données manquantes, comme un point ou "manquant" ou autre chose. Dans R, vous pouvez définir ce que vous entendez par manquer. Vous n'avez pas besoin de les convertir.

La manière normale de définir la valeur manquante est de les marquer comme NA.

x <- c(1, 2, NA, 4, "")

Ensuite, vous pouvez voir quelles valeurs manquent;

is.na(x)

Et puis le résultat sera;

FALSE FALSE  TRUE FALSE FALSE

Comme vous pouvez le voir "" n'est pas manquant. Vous pouvez menacer "" comme inconnu. Et NA est manquant.

0
ilhan

Les réponses données sont bonnes, mais ne reflètent toujours pas la relation hiérarchique entre la valeur, vide et inconnue.

  • Le plus haut vient inconn.
  • Ensuite, avant d'utiliser une valeur d'abord vide doit être clarifié.
  • Vient ensuite la valeur avec laquelle calculer.

Moche (pour son abstraction défaillante), mais pleinement opérationnel serait (en Java):

Optional<Optional<Integer>> unknowableValue;

unknowableValue.ifPresent(emptiableValue -> ...);
Optional<Integer> emptiableValue = unknowableValue.orElse(Optional.empty());

emptiableValue.ifPresent(value -> ...);
int value = emptiableValue.orElse(0);

Ici, les langages fonctionnels avec un système de type Nice sont meilleurs.

En fait: Le vide/manquant et inconnu * non -les valeurs semblent plutôt faire partie d'un état de processus, d'un pipeline de production. Comme les cellules de feuille de calcul Excel avec des formules référençant d'autres cellules. Là, on penserait peut-être à stocker des lambdas contextuelles. Changer une cellule réévaluerait toutes les cellules dépendantes récursivement.

Dans ce cas, une valeur int serait obtenue par un fournisseur int. Une valeur vide donnerait un fournisseur int levant une exception vide, ou évaluant à vide (récursivement vers le haut). Votre formule principale relierait toutes les valeurs et renverrait éventuellement une valeur vide (valeur/exception). Une valeur inconnue désactiverait l'évaluation en lançant une exception.

Les valeurs seraient probablement observables, comme une propriété liée Java, informant les auditeurs du changement.

En bref: Le schéma récurrent de besoin de valeurs avec des états supplémentaires vides et inconnus semble indiquer qu'une feuille de calcul plus semblable à un modèle de données de propriétés liées pourrait être meilleure.

0
Joop Eggen

Oui, le concept de plusieurs types de NA différents existe dans certaines langues; plus encore dans les statistiques, où il est plus significatif (à savoir l'énorme distinction entre Missing-At-Random, Missing-Completely-At-Random, Missing-Not-At- Aléatoire ).

  • si nous mesurons uniquement les longueurs des widgets, il n'est pas crucial de faire la distinction entre "défaillance du capteur" ou "coupure de courant" ou "défaillance du réseau" (bien que le "débordement numérique" transmette des informations)

  • mais par exemple l'exploration de données ou une enquête, demandant par exemple aux répondants leur revenu ou leur statut sérologique, un résultat de "Inconnu" est distinct de "Refuser de répondre", et vous pouvez voir que nos hypothèses antérieures sur la façon d'imputer ces derniers auront tendance à être différentes des premières. Ainsi, des langages comme SAS prennent en charge plusieurs types de NA différents; le langage R ne le fait pas, mais les utilisateurs doivent très souvent le contourner; les NA à différents points d'un pipeline peuvent être utilisés pour désigner des choses très différentes .

  • il y a aussi le cas où nous avons plusieurs variables NA pour une seule entrée ("imputation multiple"). Exemple: si je ne connais pas l'âge, le code postal, le niveau d'éducation ou le revenu d'une personne, il est plus difficile d'imputer son revenu.

En ce qui concerne la façon dont vous représentez différents types de NA dans des langages à usage général qui ne les prennent pas en charge, les gens piratent généralement des éléments tels que NaN à virgule flottante (nécessite la conversion d'entiers), des énumérations ou des sentinelles (par exemple 999 ou -1000) pour les nombres entiers ou valeurs catégoriques. Habituellement, il n'y a pas de réponse très claire, désolé.

0
smci