“Bilinmeyen” ve “eksik” arasındaki değişkenleri bir değişkende saklarken, “bilinmeyen” ve “eksik” arasındaki farkı nasıl koruyabilirim?


57

Bunu "akademik" bir soru olarak kabul edin. Zaman zaman NULL'lardan kaçınmayı merak ediyordum ve bu tatmin edici bir çözüm bulamadığım bir örnek.


Varsayalım ki, ölçümlerin bazen imkansız (veya eksik) olduğu bilinen yerlerde saklıyorum. NULL'dan kaçınırken bu "boş" değeri bir değişkende saklamak istiyorum. Diğer zamanlarda değer bilinmiyor olabilir. Bu nedenle, belirli bir zaman dilimi için ölçümlere sahip olmak, o zaman içindeki bir ölçüm hakkında bir sorgu 3 türden yanıt verebilir:

  • O zamanki gerçek ölçüm (örneğin, sayısal değerler dahil 0)
  • "Eksik" / "boş" bir değer (yani, bir ölçüm yapıldı ve değerin o noktada boş olduğu biliniyor ).
  • Bilinmeyen bir değer (yani, o noktada herhangi bir ölçüm yapılmamıştır. Boş olabilir, ancak başka bir değer de olabilir).

Önemli Açıklama:

Varsayalım ki get_measurement()"boş", "bilinmeyen" ve "tamsayı" türünde bir değer döndüren bir işleve sahipsin . Sayısal bir değere sahip olmak, belirli değerlerin geri dönüş değerinde (çarpma, bölme, ...) yapılabileceği anlamına gelir, ancak NULL'lerde bu tür işlemlerin kullanılması yakalanmazsa uygulamanın çökmesine neden olur.

NULL çeklerden kaçınarak kod yazabilmek istiyorum, örneğin (sözde kod):

>>> 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()

printİfadelerin hiçbirinin istisnalara neden olmadığına dikkat edin (NULL kullanılmadığı için). Böylece boş ve bilinmeyen değerler gerektiği gibi yayılır ve bir değerin gerçekten "bilinmeyen" veya "boş" olup olmadığının kontrol edilmesi gerçekten gerekli olana kadar ertelenebilir (değeri bir yerde saklamak / seri hale getirmek gibi).


Not: NULL’lardan kaçınmamın nedeni, öncelikle bir beyin teaserıdır. Eğer işleri halletmek istersem NULL kullanmaya karşı değilim, fakat onlardan kaçınmanın bazı durumlarda kodu daha güçlü hale getirebileceğini öğrendim.


19
"Ölçüm yapıldı, ancak boş değer" ile "ölçüm yok" arasındaki farkı ayırt etmek neden istiyorsunuz? Aslında, "ölçüm yapıldı ancak boş değer" ne anlama geliyor? Sensör geçerli bir değer üretemedi mi? Bu durumda, "bilinmeyen" den ne farkı var? Zamanda geriye gidip doğru değeri alamayacaksınız.
DaveG

3
@DaveG Bir sunucudaki CPU sayısını aldığınızı varsayalım. Sunucu kapatılmışsa veya hurdaya alınmışsa, bu değer basitçe mevcut değildir. Hiç mantıklı gelmeyen bir ölçüm olacak (belki de "eksik" / "boş" en iyi terimler değildir). Ancak değer saçma olduğu "bilinir". Sunucu varsa, ancak değeri alma işlemi çöker, ölçüm geçerli olur, ancak "bilinmeyen" bir değerle sonuçlanır.
exhuma

2
@exhuma O zaman "uygulanabilir değil" olarak tanımlarım.
Vincent

6
Merak etmeden, "boş" un hangi ölçeğin sıfıra eşit olmadığı yerde ne tür bir ölçüm alıyorsunuz? "Bilinmeyen" / "eksik" Bir sensör takılı değilse veya sensörün ham çıktısının bir nedenden ötürü çöp olup olmadığını, ancak her durumda "boş" durumunun daha tutarlı olabileceğini düşünüyorum ile temsil edilen 0, []veya {}(skaler 0, sırasıyla boş liste ve boş harita). Ayrıca, "eksik" / "bilinmeyen" değer temelde tam olarak ne nulliçindir - orada bir nesne olabileceğini , ancak olmadığını gösterir.
Nic Hartley

7
Bunun için ne kullanıyorsanız kullanın, kendinize NULL'ları ortadan kaldırmak istediğinize benzer sorunlardan muzdarip olup olmadığını sorduğunuzdan emin olun.
Ray,

Yanıtlar:


85

Bunu yapmanın en yaygın yolu, en azından işlevsel dillerle, ayrımcı bir birlik kullanmaktır. Bu daha sonra geçerli bir int olan, "eksik" veya "bilinmeyen" anlamına gelen bir değer olan bir değerdir. F # 'da şöyle bir şey olabilir:

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

Bir Measurementdeğer daha sonra Readingint değerine sahip bir a Missing, veya a (veya gerekirse) Unknownham verileriyle birlikte bir valueolacaktır.

Bununla birlikte, ayrımcı sendikaları ya da eşdeğerlerini destekleyen bir dil kullanmıyorsanız, bu modelin sizin için pek faydası olmaz. Böylece, örneğin, üçünden hangisinin doğru verileri içerdiğini gösteren enum alanlı bir sınıf kullanabilirsiniz.


7
OO dillerinde toplam türler yapabilirsiniz ancak bunların çalışmasını sağlamak için oldukça fazla miktarda kazan plakası vardır. stackoverflow.com/questions/3151702/…
jk.

11
“[İşlevsel olmayan dillerde] bu modelin sizin için pek faydası olmaz” - OOP'ta oldukça yaygın bir model. GOF bu şablonun bir varyasyonuna sahiptir ve C ++ gibi diller onu kodlamak için yerel yapılar sunar.
Konrad Rudolph

14
@jk. Evet sayılmazlar (sanırım yapıyorlar; güvenlik senaryosundan dolayı bu senaryoda çok kötüler). Demek istediğim std::variant(ve onun manevi öncülleri).
Konrad Rudolph

2
@Ewan Hayır, “Ölçüm ya… ya da… olan bir veri türü” diyor.
Konrad Rudolph

2
@DavidArno DU'lar olmadan bile, bunun için geçerli ve geçersiz değerler için alt sınıflara sahip bir üst sınıf değerine sahip olmak için OOP'da “kanonik” bir çözüm var. Ancak bu muhtemelen çok uzağa gidiyor (ve pratikte çoğu kod tabanının, diğer cevaplarda gösterildiği gibi, bunun için bir bayrak lehine alt sınıf polimorfizminden kaçınıyor gibi görünüyor).
Konrad Rudolph

58

Bir monadın ne olduğunu zaten bilmiyorsanız, bugün öğrenmek için harika bir gün olurdu. Burada OO programcıları için nazik bir giriş var:

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

Senaryonuz, Nullable<T>C # ve Optional<T>diğer dillerde de bilinen "belki monad" in küçük bir uzantısıdır .

Monad'ı temsil edecek soyut bir türün olduğunu varsayalım:

abstract class Measurement<T> { ... }

ve sonra üç alt sınıf:

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 ...}

Bind uygulamasına ihtiyacımız var:

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 ...
  }

Bundan basitleştirilmiş Bind versiyonunu yazabilirsiniz:

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

Ve şimdi bitti. Bir Measurement<int>eliniz var. İkiye katlamak istiyorsun:

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

Ve mantığı takip edin; eğer molduğunu Empty<int>daha sonra asStringis Empty<String>, mükemmel.

Benzer şekilde, eğer varsa

Measurement<int> First()

ve

Measurement<double> Second(int i);

o zaman iki ölçümü birleştirebiliriz:

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

ve yine eğer First()olduğunu Empty<int>daha sonra dise Empty<double>vb.

Önemli adım bağlama işlemini doğru yapmaktır . Bunun hakkında çok düşünün.


4
Monad'lar (çok şükür) anlamaktan çok kullanmak daha kolaydır. :)
Guran

11
@leftaroundabout: Kesinlikle, çünkü o saçları bölme ayrımına girmek istemedim; Orijinal posterin dediği gibi, birçok insan monadlarla uğraşmak konusunda güven duymaz. Jargon yüklü kategori teorisi basit işlemlerin karakteristik özellikleri, güven ve anlayış duygusu geliştirmeye karşı çalışır.
Eric Lippert

2
Demek tavsiyen değiştirmektir Nullile Nullablebazı Demirbaş kodu +? :)
Eric Duminil,

3
@Claude: Öğreticimi okumalısın. Bir monad, belirli kuralları izleyen ve bir işlem zincirini birbirine bağlama yeteneğini sağlayan jenerik bir tiptir, bu durumda, Measurement<T>monadik tiptir.
Eric Lippert

5
@daboross: Devletli monadların monadları tanıtmanın iyi bir yolu olduğu konusunda hemfikir olsam da, devleti bir monad'ı karakterize eden şey olarak taşımayı düşünmüyorum. Bir dizi işlevi birbirine bağlayabileceğiniz gerçeğinin, zorlayıcı şey olduğunu düşünüyorum; durumluluk sadece bir uygulama detayıdır.
Eric Lippert

18

Bu durumda bir Boş Nesne Örüntüsündeki bir varyasyonun yararlı olacağını düşünüyorum:

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
    };
}

Bunu bir yapıya dönüştürebilir, Equals / GetHashCode / ToString'i geçersiz kılabilir, üstü kapalı ya da üstü kapalı dönüşümler ekleyebilir intve NaN benzeri davranışlar istiyorsanız kendi aritmetik işleçlerinizi de uygulayabilirsiniz; Measurement.Unknown * 2 == Measurement.Unknown.

Bu, C # 'nin Nullable<int>bütün bunları uygular, farklı ihmal türleri arasında ayrım yapamayacağınız tek ihtarla null. Java insanı değilim, ama benim anladığım kadarıyla Java'nın OptionalIntbenzer olduğu ve diğer dillerin muhtemelen bir Optionaltürü temsil etmek için kendi olanaklarına sahip oldukları .


6
Bu kalıpta gördüğüm en yaygın uygulama kalıtımsallıktır. İki alt sınıf için bir durum olabilir: MissingMeasurement ve UnknownMeasurement. Ana Ölçüm sınıfındaki yöntemleri uygulayabilir veya geçersiz kılabilirler. +1
Greg Burghardt,

2
Boş Nesne Örüntüsünün noktası geçersiz değerlerde başarısız olmadığınız değil, hiçbir şey yapmıyor musunuz?
Chris Wohlert

2
@ChrisWohlert bu durumda cisim gerçekten Valuebir Unknowngeri dönüşemediğiniz için kesinlikle başarısız olması gereken getter dışında herhangi bir metoda sahip değildir int. Ölçümün, örneğin bir SaveToDatabase()metoda sahip olması durumunda, eğer mevcut nesne boş bir nesne ise (bir singleton ile kıyaslama veya metot geçersiz kılma ile) iyi bir uygulama muhtemelen bir işlem gerçekleştirmez.
Maciej Stachowski

3
@ MaciejStachowski Evet, hiçbir şey yapmaması gerektiğini söylemiyorum, Boş Nesne Örüntüsünün uygun olmadığını söylüyorum . Çözümün iyi olabilir, ama ben buna Null Object Pattern diyemezdim .
Chris Wohlert

14

Kelimenin tam anlamıyla bir tamsayı kullanmanız ZORUNLU ise, sadece bir olası çözüm var. Olası değerlerden bazılarını 'eksik' ve 'bilinmeyen' anlamına gelen 'sihirli sayılar' olarak kullanın.

örneğin 2,147,483,647 ve 2,147,483,646

Sadece 'gerçek' ölçümler için int'e ihtiyacınız varsa, daha karmaşık bir veri yapısı oluşturun

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

Önemli Açıklama:

Sınıf için operatörleri aşırı yükleyerek matematik gereksinimini elde edebilirsiniz

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

10
@KakturusOption<Option<Int>>
Bergi

5
@Bergi Muhtemelen uzaktan bile kabul edilebilir olduğunu düşünemezsin ..
BlueRaja - Danny Pflughoeft 14:18

8
@ BlueRaja-DannyPflughoeft Aslında iç içe bir yapıya sahip olan OPs tanımına oldukça iyi uyuyor. Kabul edilebilir olmak için elbette uygun bir tür takma adı (veya "yeni tür") ekleriz - ancak type Measurement = Option<Int>bir tamsayı veya boş bir okuma olan bir sonuç Option<Measurement>için bir sorun yok. .
Bergi

7
@ NaP "NaN yakın Integers"? Bununla ne demek istediğini açıklayabilir misin? Bir sayının, sayı olmayan bir şey kavramının “yakınında” olduğunu söylemek biraz ters görünmektedir.
Nic Hartley

3
@Nic Hartley Sistemimizde "doğal olarak" olabilecek en düşük negatif tamsayı olan bir grup NaN olarak ayrıldı. Bu alanı, baytların meşru verilerden başka bir şeyi temsil etmesinin nedenlerini çeşitli kodlamak için kullandık. (onlarca yıl önceydi ve bazı detaylara değinmiş olabilirdim, ama eğer matematikle uğraşmaya çalıştıysanız NaN'i atmasını sağlamak için bir tamsayı değerine koyabileceğiniz bir dizi bit vardı.
arp

11

Değişkenleriniz kayan nokta sayılarıysa, IEEE754 (çoğu modern işlemci ve dil tarafından desteklenen kayan nokta sayı standardı) sizin arkanıza sahiptir: az bilinen bir özelliktir, ancak standart bir tanesini değil tüm ailesini tanımlar . Rasgele uygulama tanımlı anlamlar için kullanılabilecek NaN (sayı olmayan) değerler. Örneğin, tek kesinlikli yüzdürmelerde, 2 ^ {22} geçersiz değer türlerini ayırt etmek için kullanabileceğiniz 22 boş bitiniz vardır.

Normal olarak, programlama arayüzleri bunlardan sadece birini gösterir (örneğin, Numpy'ler nan); Açık uçlu manipülasyon dışında başkalarını üretmenin yerleşik bir yolu olup olmadığını bilmiyorum, ancak bu sadece birkaç düşük düzeyli rutin yazma meselesi. (Bunları ayırmak için birine de ihtiyacınız olacak, çünkü tasarım gereği, a == bbir tanesi NaN olduğunda her zaman yanlış döndürür.)

Bunları kullanmak, geçersiz verileri işaret etmek için kendi "sihirli numaranızı" yeniden icat etmekten daha iyidir, çünkü doğru şekilde yayılır ve geçersizliği işaret eder: örneğin, eğer bir average()işlev kullanırsanız ve kontrol etmeyi unutursanız ayaklarınızı vurma riskini almazsınız . Özel değerlerin.

Tek risk, kütüphanelerin onları tam olarak desteklememesidir, çünkü bunlar oldukça belirsiz bir özelliktir: örneğin, bir seri hale getirme kütüphanesi, hepsini aynı şekilde 'düzleştirebilir' nan(bu çoğu amaç için ona eşdeğer görünür).


6

David Arno'nun cevabını takiben, OOP’daki ayrımcı bir birliğe, Scala’nın sağladığı, Java 8 fonksiyonel tipleri veya Vavr veya Fugue gibi bir Java FP kitaplığı gibi adil bir şekilde hissettiği gibi nesneye bağlı bir tarzda yapabilirsiniz. gibi bir şey yazmak için doğal:

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));

baskı

Value(4)
Empty()
Unknown()

( Özet olarak tam uygulama .)

Bir FP dili veya kitaplığı, burada da kullanılabilecek Try(aka Maybe) (bir değer veya bir hata Eitheriçeren bir nesne ) ve (bir başarı değeri veya bir başarısızlık değeri içeren bir nesne) gibi başka araçlar sağlar .


2

Sorununuz için ideal çözüm, bilinen bir başarısızlık ile bilinen güvenilir olmayan bir ölçüm arasındaki farka neden önem verdiğinize ve hangi akış aşağı süreçleri desteklemek istediğinize bağlı olacaktır. Not, bu durum için 'aşağı süreçler', insan operatörleri veya diğer geliştiricileri kapsamaz.

Basitçe bir "ikinci lezzet" ile gelip, aşağı havzadaki süreçleri makul bir davranışlar kümesi elde etmek için yeterli bilgi vermez.

Bunun yerine akış aşağı kodla yapılan kötü davranışların kaynağı hakkındaki bağlamsal varsayımlara güveniyorsanız, buna kötü mimarlık derim.

Bir başarısızlık nedeni ile bilinen bir nedeni olmayan bir başarısızlık arasındaki farkı ayırt etmek için yeterli bilginiz varsa ve bu bilgi gelecekteki davranışları bildirecektir, bu bilgiyi aşağı yönde iletmeli veya satır içi olarak kullanmalısınız.

Bunu ele almak için bazı desenler:

  • Toplam türleri
  • Ayrımcı sendikalar
  • İşlemin sonucunu temsil eden bir enum ve sonuç için bir alan içeren nesneler veya yapılar
  • Normal işlemle elde edilmesi mümkün olmayan sihirli dizeler veya sihirli sayılar
  • İstisnalar, bu kullanımın deyimsel olduğu dillerde
  • Bu iki senaryo arasında ayrım yapmanın ve sadece bunu kullanmanın aslında hiçbir değeri olmadığını fark etmek null

2

Zarif bir çözümden ziyade "bir şeyi yapmak" ile ilgileniyorsam, hızlı ve kirli kesmek basitçe "bilinmeyen", "eksik" ve "sayısal değerimin dizgi gösterimini" kullanmaktır. bir dizeden dönüştürülmüş ve gerektiği gibi kullanılmıştır. Bunu yazmaktan daha hızlı ve en azından bazı durumlarda tamamen yeterli. (Şimdi aşağı oy sayısına göre bir bahis havuzu oluşturuyorum ...)


"Bir şeyleri yaptırmaktan" bahsettiği için oy verildi
barbekü

4
Bazı insanlar bunun NULL kullanımıyla aynı sorunların çoğunu yaşadığını, yani NULL çeklerden "bilinmeyen" ve "eksik" çeklere ihtiyaç duyulduğundan, ancak şanslı, sessiz veri bozulmasından dolayı çalışma süresinin çökmesine neden olduğunu belirtiyorlar. bir çek unuttum sadece göstergeler gibi şanssız. Eksik NULL çekleri bile, linterlerin onları yakalayabileceği avantajına sahiptir, ancak bu onu kaybeder. Yine de "bilinmeyen" ve "eksik" arasında bir ayrım eklese, bu yüzden ... BOŞ orada yener
8bittree

2

Eğer soru şu gibi görünüyorsa: "Tek bir int döndüren bir yöntemden iki ilgisiz bilgi parçasını nasıl geri gönderirim? Dönüş değerleriimi asla kontrol etmek istemiyorum ve boş değerler kötüdür, bunları kullanmayın."

Neyi geçmek istediğine bakalım. Neden int veremediğiniz için bir int veya int dışı bir gerekçe geçiyorsunuz. Soru, yalnızca iki nedenin olacağını iddia ediyor, ancak enum yapan herkes herhangi bir listenin artacağını biliyor. Diğer gerekçeleri belirleme kapsamı anlamlıdır.

Başlangıçta, o zaman, bu bir istisna atmak için iyi bir durum olabilir gibi görünüyor.

Arayan kişiye dönüş türünde olmayan özel bir şey söylemek istediğinizde, istisnalar genellikle uygun sistemdir: istisnalar yalnızca hata durumları için değildir ve neden yapabileceğinizi açıklamak için çok fazla bağlam ve mantık döndürmenizi sağlar. Bugün int değil.

Ve bu, SADECE garantili-geçerli girişleri iade etmenizi sağlayan ve sisteme giren her int operatörünün ve metodunun, null ya da sihirli değerler gibi geçersiz değerleri kontrol etmek zorunda kalmadan bu metodun dönüş değerini kabul edebileceğini garanti eden SADECE sistemdir.

Ancak, istisnalar, yalnızca adından da anlaşılacağı gibi, işin normal seyri değil, istisnai bir durumsa, gerçekten geçerli bir çözümdür .

Ve bir dene / yakala ve işle, nihayetinde itiraz edilen boş bir çek kadar kazandır.

Ve eğer arayan / deneyen / yakalayan yoksa, arayanın yapması gerekenler vb.


Saf bir ikinci geçiş "Bu bir ölçümdür. Olumsuz mesafe ölçümlerinin olasılığı düşüktür" demektir. Yani, bazı ölçümler için, sadece

  • -1 = bilinmiyor,
  • -2 = ölçülmesi imkansız,
  • -3 = cevap vermeyi reddetti,
  • -4 = bilinen ancak gizli
  • -5 = Ay fazına bağlı olarak değişir, bakınız tablo 5a,
  • -6 = dört boyutlu, başlıkta verilen ölçümler,
  • -7 = dosya sistemi okuma hatası,
  • -8 = ileride kullanılmak üzere ayrılmıştır,
  • -9 = kare / kübik yani Y, X ile aynı
  • -10 = bir monitör ekranıdır, bu nedenle X, Y ölçümleri kullanılmaz: X'i ekran çapraz olarak kullanın,
  • -11 = bir makbuzun arkasına yapılan ölçümleri yazdı ve okunaksızlığa çarptı, ancak 5 ya da 17 olduğunu düşünüyorum.
  • -12 = ... fikri anladın.

Bu, pek çok eski C sisteminde ve hatta int için gerçek bir kısıtlamanın olduğu modern sistemlerde yapılmasının şeklidir ve bunu bir tür yapıya veya monad'a saramazsınız.

Ölçümler negatif olabilirse, o zaman veri türünüzü daha büyük hale getirin (örneğin, uzun int) ve sihir değerlerinin int aralığından daha yüksek olmasını sağlayın ve ideal olarak, hata ayıklayıcıda açıkça görünecek bir değerle başlayın.

Yine de, onları sadece sihirli sayılara sahip olmak yerine, ayrı bir değişken olarak kullanmak için iyi sebepler var. Örneğin, katı yazma, bakım ve beklentilere uygunluk.


Üçüncü girişimde, int-dışı değerlere sahip olmanın normal iş akışının olduğu durumlara bakarız. Örneğin, bu değerlerden oluşan bir koleksiyon birden çok tamsayı olmayan girdi içeriyorsa. Bu, bir istisna işleyicisinin yanlış yaklaşım olabileceği anlamına gelir.

Bu durumda, int ve mantığı geçen bir yapı için iyi bir durum görünüyor. Yine, bu mantık, sadece yukarıdaki gibi bir yapı olabilir, ancak ikisini de aynı int'de tutmak yerine, bunları bir yapının farklı parçaları olarak saklarsınız. İlk olarak, gerekçe belirlenirse, int'nin ayarlanmayacağına dair bir kural vardır. Fakat biz artık bu kurala bağlı değiliz; Gerektiğinde geçerli sayılar için de rasyonlar sağlayabiliriz.

Her iki şekilde de, onu her aradığınızda, int'nin geçerli olup olmadığını görmek için gerekçeyi test etmek için yine de bir platforma ihtiyacınız var, ardından gerekçe bize izin veriyorsa int bölümünü çekin ve kullanın.

"Boş kullanmayın" arkasındaki nedeninizi araştırmanız gereken yer burasıdır.

İstisnalar gibi, null istisnai bir durumu belirtmek içindir.

Eğer bir arayan bu metodu çağırıyorsa ve yapının "mantık" kısmını tamamen görmezden geliyorsa, herhangi bir hata işlemeden bir sayı bekler ve sıfır alırsa, sıfırı sayı olarak ele alır ve hatalı olur. Sihirli bir sayı alırsa, bunu bir sayı olarak görür ve yanlış olur. Ama eğer boşalırsa, yapması gerektiği gibi düşecek .

Bu nedenle, bu yöntemi her çağırdığınızda, dönüş değerini kontrol etmelisiniz, ancak bant içi veya bant dışı, geçersiz değerleri ele almayı deneyin, yakalayın, bir "mantıksal" bileşen için yapıyı kontrol edin, int'yi kontrol edin sihirli bir numara için veya boş bir int için denetleme ...

Alternatif, geçersiz bir int ve "Köpeğim bu ölçümü yedi" gibi bir mantık içerebilecek bir çıktının çarpımını ele almanın alternatifini, bu yapı için çarpma operatörünü aşırı yüklemektir.

... Daha sonra, uygulamanızda bu verilere uygulanabilecek tüm diğer operatörleri aşırı yükleyin.

... Ve sonra, giriş yapabilen tüm yöntemleri aşırı yükleyin.

... Ve bu aşırı yüklerin hepsinin hala geçersiz girişler için çekler içermesi gerekecek; bu nedenle, bu yöntemi geri döndürme türünü, aradığınız zaman geçerli bir int olmuş gibi ele alabilirsiniz.

Bu yüzden orijinal öncül çeşitli şekillerde yanlıştır:

  1. Geçersiz değerleriniz varsa, değerleri geçersiz kıldığınız koddaki herhangi bir noktada bu geçersiz değerleri kontrol etmekten kaçınamazsınız.
  2. Bir int dışında bir şey döndürüyorsanız, bir int döndürmezsiniz, bu nedenle bir int gibi davranamazsınız. Operatör aşırı yükleme yapmanıza olanak sağlar taklit etmek, ama bu sadece taklit ediyor.
  3. Sihirli sayıları olan bir int (NULL, NAN, Inf ... dahil) artık gerçekten bir int değil, fakir bir insanın yapısı.
  4. Boş değerlerden kaçınmak, kodu daha sağlam hale getirmez, yalnızca ints ile ilgili sorunları gizler veya karmaşık bir istisna işleme yapısına taşır.

1

Sorunuzun öncülünü anlamıyorum, ama işte yüz değeri cevabı. Eksik veya Boş için yapabilirsin math.nan(Sayı Değil). Herhangi bir matematiksel işlem gerçekleştirebilirsiniz math.nanve kalır math.nan.

NoneBilinmeyen bir değer için (Python's null) kullanabilirsiniz . Yine de bilinmeyen bir değeri manipüle etmemelisiniz ve bazı diller (Python onlardan biri değil) özel boş operatörlere sahipler, böylece işlem yalnızca değer boş değilse, aksi halde değer boş kalır.

Diğer dillerin güvenlik cümleleri var (Swift veya Ruby gibi) ve Ruby şartlı erken dönüşe sahip.

Bunu Python'da birkaç farklı yolla çözdüm:

  • Bir sarıcı veri yapısına sahip olduğundan, sayısal bilgiler genellikle bir varlık ile ilgilidir ve bir ölçüm süresine sahiptir. Sarıcı, __mult__Bilinmeyen veya Eksik değerleriniz ortaya çıktığında istisnalar ortaya çıkmayacak şekilde sihirli yöntemleri geçersiz kılabilir . Numpy ve pandalar da bu yeteneklere sahip olabilirler.
  • sentinel değeri olan ( Unknownveya -1 / -2 gibi) ve bir if ifadesiyle
  • ayrı bir boolean bayrağı ile
  • tembel bir veri yapısı ile fonksiyonunuz yapı üzerinde bir işlem gerçekleştirir, sonra döner, gerçek sonucu gerektiren en dış fonksiyon tembel veri yapısını değerlendirir
  • tembel bir işlem hattına sahip - öncekine benzer, ancak bu bir veri kümesinde veya bir veritabanında kullanılabilir

1

Değerin bellekte nasıl saklandığı, dile ve uygulama detaylarına bağlıdır. Bence demek istediğim, programcının programcıya nasıl davranması gerektiğidir. (Bu soruyu böyle okudum, yanlış olup olmadığımı söyle.)

Sorunuza zaten bir cevap verdiniz: Herhangi bir matematiksel işlemi kabul eden ve bir istisna oluşturmadan kendisini veren kendi sınıfınızı kullanın. Bunu istediğini söylüyorsun çünkü boş kontrollerden kaçınmak istiyorsun.

1. Çözüm: boş denetimlerden kaçının

Missingolarak temsil edilebilir math.nan
Unknownolarak temsil edilebilirNone

Birden fazla değeriniz varsa filter(), işlemi yalnızca olmayan Unknownveya Missingveya işlev için yoksaymak istediğiniz değerlere uygulayabilirsiniz.

Tek bir skalar üzerinde etkili olan bir işlevi boş denetlemeye ihtiyaç duyduğunuz bir senaryo düşünemiyorum. Bu durumda, boş denetimleri zorlamak iyidir.


2. Çözüm: istisnaları yakalayan bir dekoratör kullanın

Bu durumda, Missingyükseltebilir MissingExceptionve Unknownyükseltebilir UnknownExceptionoperasyonlar üzerinde gerçekleştirildiğinde.

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

Bu yaklaşımın avantajı özellikleri olduğunu Missingve Unknownonları bastırılması için açıkça sorduğunuzda sadece bastırılır. Bir başka avantaj, bu yaklaşımın kendi kendini belgelemesidir: her işlev, bilinmeyen veya eksik bir şey olup olmadığını ve nasıl işlev göreceğini gösterir.

Bir işlev çağırdığınızda, bir Eksik'in Eksik olmasını beklemiyorsanız, işlev derhal yükselecek ve sizi sessizce başarısız olmak yerine yanlış bir hatanın oluştuğunu ve çağrı zincirinin bir Eksik zincirini çoğaltmak yerine ortaya çıkaracağını gösterecektir. Aynı bilinmeyen için de geçerli.

sigmoidHala çağırabilir sinbir beklemediğini rağmen MissingOr Unknownberi, sigmoidistisna yakalayacak 'ın dekoratörü.


1
aynı soruya iki cevap gönderme noktasının ne olduğunu merak ediyorum (bu sizin önceki cevabınız , yanlış bir şeyler var mı?)
gnat

@gnat Bu cevap neden yazarın gösterdiği gibi yapılmaması gerektiğinin gerekçesini sunar ve iki cevabı farklı fikirlerle entegre etme zorluğundan geçmek istemedim - bağımsız olarak okunabilecek iki cevap yazmak daha kolay . Neden birilerinin zararsız mantığını bu kadar umursadığını anlamıyorum.
noɥʇʎԀʎzɐɹƆ

0

Bir sunucudaki CPU sayısını aldığınızı varsayalım. Sunucu kapatılmışsa veya hurdaya alınmışsa, bu değer basitçe mevcut değildir. Hiç mantıklı gelmeyen bir ölçüm olacak (belki de "eksik" / "boş" en iyi terimler değildir). Ancak değer saçma olduğu "bilinir". Sunucu varsa, ancak değeri alma işlemi çöker, ölçüm geçerli olur, ancak "bilinmeyen" bir değerle sonuçlanır.

Her ikisi de bu ses koşullarına benzer, bu nedenle buradaki en iyi seçeneğin, get_measurement()her ikisini de hemen istisnalar olarak atmak olduğunu ( sırasıyla DataSourceUnavailableExceptionveya SpectacularFailureToGetDataExceptionsırasıyla) kullanmak olduğuna karar verdim . Daha sonra, bu sorunlardan herhangi biri meydana gelirse, veri toplama kodu derhal tepki verebilir (ikinci durumda tekrar denemek gibi) ve get_measurement()yalnızca intverileri veriden başarılı bir şekilde alabilmesi durumunda geri göndermesi gerekir. kaynak - ve intbunun geçerli olduğunu biliyorsunuz .

Durumunuz istisnaları desteklemiyorsa veya bunlardan fazla yararlanamıyorsa, belki de ayrı bir çıktıyla döndürülen hata kodlarını kullanmak iyi bir alternatiftir get_measurement(). Bu, gerçek çıkışın bir giriş göstergesinde saklandığı ve bir hata kodunun geri dönüş değeri olarak geri iletildiği C'deki deyimatik kalıptır.


0

Verilen cevaplar iyi, ancak değer, boş ve bilinmeyen arasındaki hiyerarşik ilişkiyi yansıtmıyor.

  • En yüksek bilinmeyen geliyor .
  • Sonra bir değeri kullanmadan önce boş bırakılmalıdır .
  • Son ile hesaplanacak değer gelir .

Çirkin (başarısız soyutlama için), ancak tamamen operasyonel olacaktır (Java'da):

Optional<Optional<Integer>> unknowableValue;

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

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

İşte güzel tip sistemli fonksiyonel diller daha iyi.

Aslında: / boş eksik ve bilinmeyen * olmayan değerler bazı süreç devlet, bazı üretim boru hattının oldukça kısmını görünüyor. Excel gibi diğer hücrelere atıfta bulunan formülleri ile yayılmış levha hücreleri. Biri belki bağlamsal lambdaları depolamayı düşünürdü. Bir hücreyi değiştirmek, özyinelemeli bağımlı tüm hücreleri yeniden değerlendirir.

Bu durumda bir int tedarikçisi int şirketi tarafından bir int değeri elde ederdi. Boş bir değer, bir int tedarikçisine boş bir istisna atma veya boşa değerlendirme (tekrarlı olarak yukarı doğru) verecektir. Ana formülünüz tüm değerleri birbirine bağlar ve muhtemelen boş bir değer verir (değer / istisna). Bilinmeyen bir değer, istisna atarak değerlendirmeyi devre dışı bırakacaktır.

Değerler muhtemelen bir java bağlantılı mülk gibi gözlemlenebilir ve dinleyicileri değişime bildirir.

Kısacası: Boş ve bilinmeyen ek durumlara sahip tekrarlayan değerlerin tekrar eden kalıbı, bağlı özellikler veri modelinin daha geniş bir forma sahip sayfanın daha iyi olabileceğini göstermektedir.


0

Evet, birçok farklı NA türü kavramı bazı dillerde var; daha doğrusu istatistiksel olanlarda, bunun daha anlamlı olduğu yerler (örn. Rastgele At - Eksik, Rastgele At - Eksik, Rastgele Değil ) arasındaki büyük fark .

  • eğer sadece widget uzunluklarını ölçüyorsak, 'sensör arızası' veya 'elektrik kesintisi' veya 'ağ arızası' arasında ayrım yapmak çok önemli değildir ('sayısal taşma' bilgi iletse de)

  • ancak örneğin veri madenciliği veya ankette, katılımcılardan gelirlerini veya HIV durumlarını sormalarını istemek, 'Bilinmeyen'in bir sonucu olarak' cevap vermeyi reddet 'seçeneğinden farklıdır ve daha sonrakileri nasıl etkileyeceğine ilişkin önceki varsayımlarımızın öncekinden farklı olmak. Dolayısıyla, SAS gibi diller birden fazla NA türünü destekler; R dili değildir, ancak kullanıcılar bu konuda sık sık kesmek zorundadır; Bir boru hattında farklı noktalardaki NA'lar çok farklı şeyleri belirtmek için kullanılabilir.

  • Aynı zamanda, tek bir giriş için birden fazla NA değişkenine sahip olduğumuz bir durum söz konusudur ("çoklu değerlendirme"). Örnek: Bir kişinin yaşı, posta kodu, eğitim düzeyi veya geliri hakkında hiçbir fikrim yoksa, gelirlerini zorlamak zordur.

Farklı NA türlerini, onları desteklemeyen genel amaçlı dillerde nasıl temsil ettiğinize gelince, genellikle insanlar tam sayı için kayan nokta-NaN (dönüştürme tam sayıları gerektirir), enums veya nöbetçiler (örneğin 999 veya -1000) gibi şeyleri hacklerler. kategorik değerler. Genelde çok net bir cevap yoktur, üzgünüm.


0

R yerleşik eksik değer desteğine sahiptir. https://medium.com/coinmonks/dealing-with-missing-data-using-r-3ae428da2d17

Düzenleme: reddedildiğim için biraz açıklayacağım.

İstatistiklerle ilgilenecekseniz, R gibi istatistikçiler için yazılmış olan R gibi bir istatistik dili kullanmanızı tavsiye ederim. Eksik değerler size bütün bir dönem boyunca öğretecekleri çok büyük bir konudur. Ve sadece eksik değerler hakkında büyük kitaplar var.

Bununla birlikte, bir noktayı veya "eksik" ya da her neyse, eksik verileri işaretlemek isteyebilirsiniz. R'de ne demek istediğinizi eksik olarak tanımlayabilirsiniz . Onları dönüştürmenize gerek yok.

Kayıp değeri tanımlamanın normal yolu, onları işaretlemektir NA.

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

Sonra hangi değerlerin eksik olduğunu görebilirsiniz;

is.na(x)

Ve sonra sonuç olacak;

FALSE FALSE  TRUE FALSE FALSE

Gördüğünüz gibi ""eksik değil. ""Bilinmeyen olarak tehdit edebilirsiniz . Ve NAkayıp.


@Hulk, hangi işlevsel diller eksik değerleri destekliyor? Eksik değerleri destekleseler bile, bunları yalnızca bir kod satırında istatistiksel yöntemlerle dolduramayacağınızdan eminim.
ilhan

-1

*Operatörün işlevselliğinin bunun yerine değiştirilememesinin bir nedeni var mı ?

Cevapların çoğu bir çeşit arama değeri içerir, ancak bu durumda matematiksel operatörü değiştirmek daha kolay olabilir.

Daha sonra tüm projenizde benzer empty()/ unknown()işlevselliğe sahip olacaksınız .


4
Bu, tüm operatörlere aşırı yükleme yapmanız gerektiği anlamına gelir
boru
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.