Bir sayının aralık dahilinde olup olmadığını zarif bir şekilde nasıl kontrol edebilirim?


158

Bunu C # ve .NET 3.5 / 4 ile zarif bir şekilde nasıl yapabilirim?

Örneğin, bir sayı 1 ile 100 arasında olabilir.

Yeterince basit bir şey biliyorum; ancak bu sorunun anahtar kelimesi zarafettir. Bu benim oyuncak projem için değil üretim için.

Bu sorular hız değil, kod güzelliği ile ilgiliydi. Verimlilik ve benzeri konular hakkında konuşmayı bırakın; koroya vaaz verdiğini hatırla.


23
Re: Sizin "düzenleme" - basit zarif . Şahsen if ifadesini bu kontrolü yapmak için standart olmayan herhangi bir araçtan daha zarif buluyorum ...
Reed Copsey

4
"Her şey mümkün olduğunca basit olmalı, ancak daha basit olmamalıdır." - Albert Einstein
corsiKa

3
@Sergio: Bilgiçlik yaptığımı hissetmiyorum. İnsanların, zaten basit olan şeyleri değiştirmek için genellikle dilde uzantı yöntemlerini ve diğer araçları kötüye kullandığını hissediyorum. İki int değerini karşılaştırmanın yüzlerce yolu vardır, ancak herhangi bir şey kullanmaktan daha açık olanı kötü bir seçimdir, IMO.
Reed Copsey

3
@Sergio: Sanırım, o zaman sorunun anlamını görmüyorum;)
Reed Copsey

6
@Sergio: if"Barok" değilse tamir etmeyin.
StriplingWarrior

Yanıtlar:


155

Birçok seçenek var:

int x = 30;
if (Enumerable.Range(1,100).Contains(x))
    //true

if (x >= 1 && x <= 100)
    //true

Ayrıca, regex seçenekleri için bu SO yazı göz atın.


335
Aralık numaralandırması önce tamsayıların tamsayılarını oluşturmak ve daha sonra onu bulmak için her öğenin üzerinde döngü yapmak zorundadır. Bu, bir değerin kontrol edilmesine kıyasla korkunç bir fikir ve performans büyük ölçüde farklıdır. Bence bir motora sahip olmalıyız, çünkü LINQ Uzantıları serin olduğu için her şey için kullanılması gerektiği anlamına gelmez.
Matthew Abbott


15
Bunun performans açısından korkunç bir fikir olduğunu kabul ediyorum, ancak OP bir ifaçıklamadan daha süslü bir şey istiyor . Bu kesinlikle başarıyor ...;)
Tim Coker

10
İkinci parametrenin "dur" değil, "say" olduğunu belirtmek gerekir. Mesela, Enumerable.Range (150, 300). (400) içerir, true değerini döndürür.
Shathur

5
Lütfen bu cevabı kullanmayın . Aralıklarınız oldukça büyükse korkunç bir performans gösterecektir. Lütfen cevap için @ olivier-jacot-descombes'e bakınız
Aaron Hudon

96

Bunu mu demek istediniz?

if(number >= 1 && number <= 100)

veya

bool TestRange (int numberToCheck, int bottom, int top)
{
  return (numberToCheck >= bottom && numberToCheck <= top);
}

1
İçeride "olan" a ihtiyacınız yok ... Bu derlenmeyecek. (Aksi takdirde,% 100 katılıyorum)
Reed Copsey

4
@ Ben, ben de denemek ve patent kadar :) bekleyin
kemiller2002

Bence bu en sağlam çözüm ama sorgulayıcının aradığı zarif değil, değil mi?
Kevin Simple

Değiştirecek tek şey, yöntem için statik anahtar kelime eklemektir. ;-)
Robert S.

<Vs <= değerine izin vermek için InRange (sayı, altBound, LOWER_IS_INCLUSIVE, Upperbound, UPPER_IS_EXCLUSIVE) sınır bayrakları gerekir. Bunu sinsi olmaya niyetli yazdım ama şimdi düşündüğümde bayraklar aslında arayanı spesifikasyonlarını düzleştirmeye teşvik edecekti.
William T. Mallard

56

Buradaki gürültüye eklemek için bir uzantı yöntemi oluşturabilirsiniz:

public static bool IsWithin(this int value, int minimum, int maximum)
{
    return value >= minimum && value <= maximum;
}

Hangi gibi bir şey yapmanıza izin verir ...

int val = 15;

bool foo = val.IsWithin(5,20);

Olduğu söyleniyor, bu kontrol sadece bir satır olduğunda yapmak aptalca bir şey gibi görünüyor.


1
@Ben: "Bir menzil içinde" yazan konuya gittim (ki bu konuda belirsiz olduğunu düşünmüyorum), ama soru gövdesinin "1 ile 100 arasında" (yani , tabii ki, belirsiz).
Adam Robinson

48

Diğerleri gibi, basit bir if kullanın.

Siparişi düşünmelisiniz.

Örneğin

1 <= x && x <= 100

okumak daha kolay

x >= 1 && x <= 100

19
"Daha kolay" bakanın gözündedir. Ben şahsen sol ve sabit veya değişken üzerinde söz konusu değişken olmasını tercih değil sağdaki söz konusu.
Adam Robinson

15
In Perl 6 şu yazılır 1 <= x <= 100.
Jordão

2
Sayı satırı sırası başlangıçta en net olanıdır - ancak diğer siparişler için gözlerinizi / zihninizi eğitebilirsiniz. Özellikle - sabiti her zaman sola yerleştirme hilesini seviyorum . Eğer yazdığınız zaman derleyici söyleyecektir bunu yaparsak =yerine ==. Eşitlik dışı ilişkisel operatörlere yardımcı olmaz - ancak tutarlı bir şekilde kullanmaya alışmak kolaydır.
davidbak

1
Sadece bu çözümün her durumda yararlı olmadığını eklemek istiyorum. Düşünün xişlev çağrısı veya zaman alıcı Linq ifade bir komplekstir. Bu durumda bunu iki kez yaparsınız ki bu iyi bir şey değildir. Değeri geçici bir yerel değişkente saklamanız gerektiğinden emin olun, ancak işlevlerin yalnızca if veya else-if başarısız olduğunda birbirini ardına çağırmak istediğiniz bazı durumlar vardır (örneğin else-if-statements). Geçici değişkenlerle bunları önceden çağırmanız gerekir. Bir uzatma yöntemi (diğer cevaplarda belirtilmiştir) bu durumlarda en iyi çözümdür.
Robert

4
Sayı satırı sırasını da seviyorum ve ayrıca tamamlayıcı testi için, örneğin x <10 || 20 <x. Bana göre "x 10 - 20 aralığının dışında" diye bağırıyor.
William T. Mallard

44

Üretim kodunda yazarım

1 <= x && x <= 100

Bu anlaşılması kolaydır ve çok okunabilir.


İşte bazı matematik kullanarak karşılaştırma sayısını ikiden bire azaltan akıllı bir yöntem. Fikir, sayı aralığın dışındaysa iki faktörden birinin negatif, sayı sınırlardan birine eşitse sıfır olduğu yönündedir:

Sınırlar dahil ise:

(x - 1) * (100 - x) >= 0

veya

(x - min) * (max - x) >= 0

Sınırlar münhasır ise:

(x - 1) * (100 - x) > 0

veya

(x - min) * (max - x) > 0

3
Standartlarıma göre bu en zarif çözüm, ilginç, benim için her iki ifadeyi de kontrol etmekten biraz daha hızlı çalışıyor gibi görünüyor, bu da daha tutarsız görünüyor (hız daha farklı görünüyor) görmek ilginç olurdu hangisinin daha hızlı olduğu üzerine yapılan bir araştırma varsa.
Thomas Lindvall

3
Çözümünüzü javascript üzerinde ve 14 ondalık basamağa kadar kayan nokta sayıları ile test edin. Çok iyi bir kod pasajı. Eğer yapabilseydim üç kez seni
oyladı

4
Olsa da, büyük pozitif sayılar söz konusu olduğunda küçük bir sorun vardır, taşabilir! XD Kodunuzu yazarken bunu aklınızda bulundurmak isteyebilirsiniz.
BrainStorm.exe

2
Soru zarafet ister ve bu nedenle pratik değerden çok akademiktir. Şahsen ben sadece basit bir 1 < x && x < 100üretken kod kullanırdım. Anlaması daha kolay.
Olivier Jacot-Descombes

1
Performansla ilgili olanlar için 1 < x & x < 100(&& kısa devre yok) derleyiciye x < 100sonucu ne olursa olsun her zaman değerlendirebileceğini bildirir 1 < x. Garip bir şekilde (dal tahmini nedeniyle) bu basit işlemi her zaman yapmak bazen atlamaktan daha hızlıdır.
Tom Leys

23

Bunu öneriyorum:

public static bool IsWithin<T>(this T value, T minimum, T maximum) where T : IComparable<T> {
    if (value.CompareTo(minimum) < 0)
       return false;
    if (value.CompareTo(maximum) > 0)
       return false;
    return true;
}

Örnekler:

45.IsWithin(32, 89)
true
87.2.IsWithin(87.1, 87.15)
false
87.2.IsWithin(87.1, 87.25)
true

ve elbette değişkenlerle:

myvalue.IsWithin(min, max)

Okuması kolaydır (insan diline yakın) ve karşılaştırılabilir herhangi bir türle (tamsayı, çift, özel türler ...) çalışır.

Kodun okunması kolay olması önemlidir çünkü geliştirici bunu anlamak için “beyin döngülerini” boşa harcamaz. Uzun kodlama oturumlarında boşa giden beyin döngüleri geliştiriciyi daha önce yorgun ve hataya eğilimli yapar.


3
i arasındaki kelimeyi kullanarak ve kapsayıcı olup olmadığını belirlemek için bir boolean bayrak sahip daha da basitleştirmek
Ben

İyi. Anlamak kolaydır. IsInRange. I'm not that keen on Ben's inclusive boolean as that requires a few more brain cycles. It has the advantage that it can be used in any class that that implements IComparer. This is in my Extensions now along with LiesWithin / LiesInside. Just can't decide which. NotOutside ismini değiştirdim ama olumsuz koşulları sevmiyorum
Paulustrious

21

Biraz uzatma yöntemi kötüye kullanımı ile, aşağıdaki "zarif" çözümü alabilirsiniz:

using System;

namespace Elegant {
    public class Range {
        public int Lower { get; set; }
        public int Upper { get; set; }
    }

    public static class Ext {
        public static Range To(this int lower, int upper) {
            return new Range { Lower = lower, Upper = upper };
        }

        public static bool In(this int n, Range r) {
            return n >= r.Lower && n <= r.Upper;
        }
    }

    class Program {
        static void Main() {
            int x = 55;
            if (x.In(1.To(100)))
                Console.WriteLine("it's in range! elegantly!");
        }
    }
}

Çözüm gibi! Btw oluşturmak, kapsayıcı destekleyecek enum Inclusivedeğerlerle: Lower, Upper, All. Ve için geçmesi Intip işlevi bir ek parametre enum Inclusivevarsayılan değeri ile Inclusive.All, güncelleştirmek Tokolu işlev gövdesini All, Lower, Upperdeğerler :)
Nikita

7

Bu arızi ise, iftek ihtiyacınız olan basittir . Bu birçok yerde olursa, bu ikisini dikkate almak isteyebilirsiniz:

  • PostSharp . Derleme sonrası yönteme kod 'enjekte' nitelikleri ile yöntemleri süsleyin. Emin değilim, ama bunun için kullanılabileceğini hayal edebiliyorum.

Gibi bir şey:

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • Kod sözleşmeleri . Kısıtlamaların, kodunuzun ve kodunuzu kullanan yerlerin statik olarak doğrulanmasıyla derleme zamanında kontrol edilebilmesi avantajına sahiptir.

Kod sözleşmeleri için +1; bir parametreyi doğrulamaya özgüdür, ancak sık kullanılan bir durumdur ve statik doğrulama son derece yararlı olma potansiyeline sahiptir.
Dan Bryant

5
if (value > 1 && value < 100)
{
    // do work
}
else
{
    // handle outside of range logic
}

5

&&İki karşılaştırmaya katılmak için bir ifade kullanmak , bunu yapmanın en zarif yoludur. Süslü uzatma yöntemlerini ve benzerlerini kullanmayı denerseniz, üst sınırı, alt sınırı veya her ikisini dahil edip etmeyeceğiniz sorusuyla karşılaşırsınız. Ek değişkenler eklemeye veya nelerin dahil edildiğini belirtmek için uzantı adlarını değiştirmeye başladığınızda, kodunuz daha uzun ve okunması zorlaşır (programcıların büyük çoğunluğu için). Ayrıca, karşılaştırmanız mantıklı değilse ( number > 100 && number < 1), Resharper gibi araçlar sizi bir yöntem ('i.IsBetween (100, 1)') kullanırsanız yapmayacaktır.

Yapacağım diğer tek yorum, bir istisna atmak amacıyla girişleri kontrol ediyorsanız, kod sözleşmelerini kullanmayı düşünmenizdir:

Contract.Requires(number > 1 && number < 100)

Bu daha zariftir if(...) throw new Exception(...)ve birileri ilk önce numaranın sınırda olduğundan emin olmadan yönteminizi çağırmaya çalışırsa derleme zamanı uyarıları bile alabilirsiniz.


2
FYI, sözleşmeler statik analizörü alt sınır ve üst sınır kısıtlamaları ayrı gerektirir gerektiren ifadelere ayrıldığında daha mutlu olur.
Dan Bryant

Teşekkürler Dan Bryant, tam da burada aradığım şey buydu. Gereksinimler ve diğer ilgili Kod Sözleşmesi yöntemleri için koşulların tarzı ile ilgili öneriler konusunda fazla malzeme bulunamıyor.
jpierson

3

EDIT: Yeni Yanıt sağlandı. Bu sorunun ilk cevabını yazdığımda C # kullanmaya başladım ve artık "çözümümün" naif ve verimsiz olduğunu anlıyorum.

Orijinal cevabım: Daha basit bir versiyonla giderdim:

if(Enumerable.Range(1,100).Contains(intInQuestion)) { ...DoStuff; }

En iyi yol

Daha verimli başka bir çözüm görmediğim için (en azından testlerime göre), başka bir çözüm vereceğim.

Negatif aralıklarla da çalışan yeni ve daha iyi bir yol :

// Returns true if x is in range [min..max], else false 
bool inRange(int x, int min=1, int max=100) => ((x - max)*(x - min) <= 0);

Bu, hem pozitif hem de negatif aralıklar ve varsayılan olarak bir dizi aralıkla kullanılabilir

1. 100 (dahil) ve kullanımları x kontrol etmek için sayı olarak bunu minve ile tanımlanan isteğe bağlı bir aralık izler max.

İyi Önlemler İçin Örnekler Ekleme

Örnek 1:

// Returns true if x is in range [min..max], else false 
bool inRange(int x, int min=1, int max=100) => ((x - max)*(x - min) <= 0);

Console.WriteLine(inRange(25));
Console.WriteLine(inRange(1));
Console.WriteLine(inRange(100));
Console.WriteLine(inRange(25, 30, 150));
Console.WriteLine(inRange(-25, -50, 0));

İadeler:

True
True
True
False
True

Örnek 2: 1 ile 150 arasında rastgele giriş listesinin kullanılması

// Returns true if x is in range [min..max], else false 
bool inRange(int x, int min=1, int max=100) => ((x - max)*(x - min) <= 0);

// Generate 100000 ints between 1 and 150
var intsToCheck = new List<int>();
var randGen = new Random();
for(int i = 0; i < 100000; ++i){
    intsToCheck.Add(randGen.Next(150) + 1);
}

var counter = 0;
foreach(int n in intsToCheck) {
    if(inRange(n)) ++counter;
}

Console.WriteLine("{0} ints found in range 1..100", counter);

İadeler:

66660 ints found in range 1..100

Yürütme Süresi: 0,016 saniye


Evet, 2013'teki cevabımın bir yorumunu yorumluyorum :) @RyanTheLeach: Bu soruya cevabımın şimdi “kabul edilen” cevaptan farkı nedir? Bunun en etkili geçiş değil, “korkunç” olduğunun farkındayım. 100 ints üzerinden tahsis ve döngü ne kadar kötü olabilir? 1950'de muhtemelen sosyal olarak kabul edilmedi, ama ...
cseder

@RyanTheLeach Seni suçlamıyorum ... Cevabımı güncelledim, bu yüzden, daha verimli bir çözüm biliyorsanız, lütfen detaylandırın!
cseder

1
Artık kalmadıkları için yorumlarımı sildim. Düzeltme için teşekkürler, tamam görünüyor.
Ryan The Leach

2

Basit bir if'dan daha fazla kod yazmak isterseniz, şunları yapabilirsiniz: IsBetween adlı bir Uzantı Yöntemi oluşturun

public static class NumberExtensionMethods
{
    public static bool IsBetween(this long value, long Min, long Max)
    {
        // return (value >= Min && value <= Max);
        if (value >= Min && value <= Max) return true;
        else return false;
    }
}

...

// Checks if this number is between 1 and 100.
long MyNumber = 99;
MessageBox.Show(MyNumber.IsBetween(1, 100).ToString());

Zeyilname:pratikte bir kod tabanında nadiren "sadece eşitliği kontrol et" (veya <,>) olduğunu belirtmek gerekir. (En önemsiz durumlar dışında.) Tamamen örnek olarak, herhangi bir oyun programcısı, temel bir konu olarak her projede aşağıdaki gibi kategoriler kullanır. Bu örnekte, o ortama yerleşik bir işlev (Mathf.Appro approx) kullanmak (olur); pratikte, gerçek sayıların bilgisayar gösterimleri için, mühendislik yaptığınız durumun türü için karşılaştırmaların ne anlama geldiğine dair kendi kavramlarınızı dikkatlice geliştirmeniz gerekir. (Belki bir denetleyici, bir PID denetleyicisi veya benzeri bir şey yapıyorsanız, tüm sorunun merkezi ve çok zor hale geldiğini, projenin doğası haline geldiğini bile söylemeyin.

private bool FloatLessThan(float a, float b)
    {
    if ( Mathf.Approximately(a,b) ) return false;
    if (a<b) return true;
    return false;
    }

private bool FloatLessThanZero(float a)
    {
    if ( Mathf.Approximately(a,0f) ) return false;
    if (a<0f) return true;
    return false;
    }

private bool FloatLessThanOrEqualToZero(float a)
    {
    if ( Mathf.Approximately(a,0f) ) return true;
    if (a<0f) return true;
    return false;
    }

1
return (value >= Min && value <= Max);
İf

karşılaştırmayı yazmanın zarif yolu, (Min <= değer && değeri <= Maks) ise "mantıksal sırada ..." dır. Bu çok daha güzel.
Fattie

2
Bu soruya ek olarak, herhangi bir gerçek dünya projesinde (özellikle bir oyun mühendisiyseniz) hiç kimsenin ana sorundan bahsetmemesi çok şaşırtıcıdır , yaklaşıklık sorunuyla uğraşmak zorunda kalmanızdır . Herhangi bir gerçek dünya yazılımında, esasen asla "sadece bir karşılaştırma yapmayın" (eşitlik olsun ya da <,> olsun), eldeki duruma bağlı olarak hata sorununu dikkate almanız ve bu sorunu ele almanız gerekmez. Daha fazla cevaba izin verilmediği için bu cevaba (burada tek doğru cevap!) Bir zeyilname düzenledim.
Şişman

Bu gözlem ve zeyilname için teşekkür ederim.
Tony

2

Çünkü diğer tüm cevaplar benim tarafımdan icat edilmedi, burada sadece benim uygulamam

public enum Range
{
    /// <summary>
    /// A range that contains all values greater than start and less than end.
    /// </summary>
    Open,
    /// <summary>
    /// A range that contains all values greater than or equal to start and less than or equal to end.
    /// </summary>
    Closed,
    /// <summary>
    /// A range that contains all values greater than or equal to start and less than end.
    /// </summary>
    OpenClosed,
    /// <summary>
    /// A range that contains all values greater than start and less than or equal to end.
    /// </summary>
    ClosedOpen
}

public static class RangeExtensions
{
    /// <summary>
    /// Checks if a value is within a range that contains all values greater than start and less than or equal to end.
    /// </summary>
    /// <param name="value">The value that should be checked.</param>
    /// <param name="start">The first value of the range to be checked.</param>
    /// <param name="end">The last value of the range to be checked.</param>
    /// <returns><c>True</c> if the value is greater than start and less than or equal to end, otherwise <c>false</c>.</returns>
    public static bool IsWithin<T>(this T value, T start, T end) where T : IComparable<T>
    {
        return IsWithin(value, start, end, Range.ClosedOpen);
    }

    /// <summary>
    /// Checks if a value is within the given range.
    /// </summary>
    /// <param name="value">The value that should be checked.</param>
    /// <param name="start">The first value of the range to be checked.</param>
    /// <param name="end">The last value of the range to be checked.</param>
    /// <param name="range">The kind of range that should be checked. Depending on the given kind of range the start end end value are either inclusive or exclusive.</param>
    /// <returns><c>True</c> if the value is within the given range, otherwise <c>false</c>.</returns>
    public static bool IsWithin<T>(this T value, T start, T end, Range range) where T : IComparable<T>
    {
        if (value == null)
            throw new ArgumentNullException(nameof(value));

        if (start == null)
            throw new ArgumentNullException(nameof(start));

        if (end == null)
            throw new ArgumentNullException(nameof(end));

        switch (range)
        {
            case Range.Open:
                return value.CompareTo(start) > 0
                       && value.CompareTo(end) < 0;
            case Range.Closed:
                return value.CompareTo(start) >= 0
                       && value.CompareTo(end) <= 0;
            case Range.OpenClosed:
                return value.CompareTo(start) > 0
                       && value.CompareTo(end) <= 0;
            case Range.ClosedOpen:
                return value.CompareTo(start) >= 0
                       && value.CompareTo(end) < 0;
            default:
                throw new ArgumentException($"Unknown parameter value {range}.", nameof(range));
        }
    }
}

Daha sonra şu şekilde kullanabilirsiniz:

var value = 5;
var start = 1;
var end = 10;

var result = value.IsWithin(start, end, Range.Closed);

1

Eski bir favoride yeni bir bükülme:

public bool IsWithinRange(int number, int topOfRange, int bottomOfRange, bool includeBoundaries) {
    if (includeBoundaries)
        return number <= topOfRange && number >= bottomOfRange;
    return number < topOfRange && number > bottomOfRange;
}

3
Aslında dört / kapsayıcı / kapsayıcı, kapsayıcı / münhasır, münhasır / kapsayıcı ve münhasır / münhasır olmak üzere dört vaka vardır.
William T. Mallard

1

C'de, zaman verimliliği çok önemliyse ve tamsayı taşmaları kaybolursa, bunu yapabilir if ((unsigned)(value-min) <= (max-min)) .... Eğer 'max' ve 'min' bağımsız değişkenler ise, (max-min) için fazladan çıkarma zaman kaybedecektir, ancak bu ifade derleme zamanında önceden hesaplanabiliyorsa veya birçoğunu test etmek için çalışma zamanında bir kez hesaplanabiliyorsa aynı aralığa göre sayılar, değerin aralık dahilinde olduğu durumlarda bile yukarıdaki ifade etkili bir şekilde hesaplanabilir (değerlerin büyük bir kısmı geçerli aralığın altında olacaksa, erkenif ((value >= min) && (value <= max)) ... çıkacağı için kullanımı daha hızlı olabilir değeri ise dakikadan daha az).

Böyle bir uygulamayı kullanmadan önce, birinin hedef makinesini kıyaslayın. Bazı işlemcilerde, iki karşılaştırma ifadesi tüm durumlarda daha hızlı olabilir, çünkü iki karşılaştırma bağımsız olarak yapılabilir, oysa çıkarma ve karşılaştırma yönteminde, karşılaştırma işleminin gerçekleştirilebilmesi için çıkarma işleminin tamamlanması gerekir.


1

Böyle bir şeye ne dersin?

if (theNumber.isBetween(low, high, IntEx.Bounds.INCLUSIVE_INCLUSIVE))
{
}

aşağıdaki gibi uzatma yöntemiyle (test edilmiştir):

public static class IntEx
{
    public enum Bounds 
    {
        INCLUSIVE_INCLUSIVE, 
        INCLUSIVE_EXCLUSIVE, 
        EXCLUSIVE_INCLUSIVE, 
        EXCLUSIVE_EXCLUSIVE
    }

    public static bool isBetween(this int theNumber, int low, int high, Bounds boundDef)
    {
        bool result;
        switch (boundDef)
        {
            case Bounds.INCLUSIVE_INCLUSIVE:
                result = ((low <= theNumber) && (theNumber <= high));
                break;
            case Bounds.INCLUSIVE_EXCLUSIVE:
                result = ((low <= theNumber) && (theNumber < high));
                break;
            case Bounds.EXCLUSIVE_INCLUSIVE:
                result = ((low < theNumber) && (theNumber <= high));
                break;
            case Bounds.EXCLUSIVE_EXCLUSIVE:
                result = ((low < theNumber) && (theNumber < high));
                break;
            default:
                throw new System.ArgumentException("Invalid boundary definition argument");
        }
        return result;
    }
}

1

Ben bir Range nesne, böyle bir şey yapmak:

public class Range<T> where T : IComparable
{
    public T InferiorBoundary{get;private set;}
    public T SuperiorBoundary{get;private set;}

    public Range(T inferiorBoundary, T superiorBoundary)
    {
        InferiorBoundary = inferiorBoundary;
        SuperiorBoundary = superiorBoundary;
    }

    public bool IsWithinBoundaries(T value){
        return InferiorBoundary.CompareTo(value) > 0 && SuperiorBoundary.CompareTo(value) < 0;
    }
}

Sonra bu şekilde kullanırsınız:

Range<int> myRange = new Range<int>(1,999);
bool isWithinRange = myRange.IsWithinBoundaries(3);

Bu şekilde başka bir tür için tekrar kullanabilirsiniz.


Sizin Rangenesne kullanması gerekir CompareToöğeleri değil karşılaştırmak için yöntem <operatörü.
Servy

Haklısınız, ancak IComparable'ı uygularsanız operatörleri de geçersiz kılmalısınız (en azından VS kod analizimin söylediği şey), yani <işe yarayacaktı. Her ne kadar yanlış olabilir, ben fazla deneyimim yok ve bu benim ilk cevabım SO
IEatBagels

Hayır, derleyici olmaz bu işler söylüyorlar. Bu derlenmeyecek. Bir nesnenin operatörü IComparableyüklemesi ve aşırı yüklememesi tamamen mantıklıdır <.
Servy

1

"Sayı" nın bir aralıkta olup olmadığını kontrol ederken, ne demek istediğiniz konusunda net olmanız gerekir ve iki sayı ne anlama gelir? Genel olarak, kayan nokta sayılarını 'epsilon topu' olarak adlandırılan şeye sarmalısınız, bu küçük bir değer seçerek ve iki değer bu kadar yakınsa aynı şey olduklarını söyleyerek yapılır.

    private double _epsilon = 10E-9;
    /// <summary>
    /// Checks if the distance between two doubles is within an epsilon.
    /// In general this should be used for determining equality between doubles.
    /// </summary>
    /// <param name="x0">The orgin of intrest</param>
    /// <param name="x"> The point of intrest</param>
    /// <param name="epsilon">The minimum distance between the points</param>
    /// <returns>Returns true iff x  in (x0-epsilon, x0+epsilon)</returns>
    public static bool IsInNeghborhood(double x0, double x, double epsilon) => Abs(x0 - x) < epsilon;

    public static bool AreEqual(double v0, double v1) => IsInNeghborhood(v0, v1, _epsilon);

Bu iki yardımcı ile ve herhangi bir sayı gerekli doğruluk olmadan bir çift olarak kullanılabileceğini varsayarak. Şimdi ihtiyacınız olan tek şey bir numaralandırma ve başka bir yöntem

    public enum BoundType
    {
        Open,
        Closed,
        OpenClosed,
        ClosedOpen
    }

Diğer yöntem şu şekildedir:

    public static bool InRange(double value, double upperBound, double lowerBound, BoundType bound = BoundType.Open)
    {
        bool inside = value < upperBound && value > lowerBound;
        switch (bound)
        {
            case BoundType.Open:
                return inside;
            case BoundType.Closed:
                return inside || AreEqual(value, upperBound) || AreEqual(value, lowerBound); 
            case BoundType.OpenClosed:
                return inside || AreEqual(value, upperBound);
            case BoundType.ClosedOpen:
                return inside || AreEqual(value, lowerBound);
            default:
                throw new System.NotImplementedException("You forgot to do something");
        }
    }

Şimdi bu istediğinizden çok daha fazlası olabilir, ancak her zaman yuvarlama ile uğraşmanızı ve bir değerin yuvarlatılmış olup olmadığını ve hangi yere olduğunu hatırlamaya çalışmanızı engeller. Gerekirse, herhangi bir epsilon ile çalışmak ve epsilonunuzun değişmesine izin vermek için bunu kolayca genişletebilirsiniz.


1
static class ExtensionMethods
{
    internal static bool IsBetween(this double number,double bound1, double bound2)
    {
        return Math.Min(bound1, bound2) <= number && number <= Math.Max(bound2, bound1);
    }

    internal static bool IsBetween(this int number, double bound1, double bound2)
    {
        return Math.Min(bound1, bound2) <= number && number <= Math.Max(bound2, bound1);
    }
}

kullanım

çift ​​sayıToBeChecked = 7;

var sonuç = numberToBeChecked.IsBetween (100,122);

var sonuç = 5.Is (100,120) arasında;

var sonuç = 8.0. (1.2,9.6) arasında;


1

@Daap tarafından kabul edilen yanıttaki yorumla ilgileniyorsanız ve değeri yalnızca bir kez geçebiliyorsanız, aşağıdakilerden birini deneyebilirsiniz

bool TestRangeDistance (int numberToCheck, int bottom, int distance)
{
  return (numberToCheck >= bottom && numberToCheck <= bottom+distance);
}

//var t = TestRangeDistance(10, somelist.Count()-5, 10);

veya

bool TestRangeMargin (int numberToCheck, int target, int margin)
{
  return (numberToCheck >= target-margin && numberToCheck <= target+margin);
}

//var t = TestRangeMargin(10, somelist.Count(), 5);

1

Zarafet ile ilgili olarak, matematiksel gösterime en yakın olan şey ( a <= x <= b ) okunabilirliği biraz geliştirir:

public static bool IsBetween(this int value, int min, int max)
{
    return min <= value && value <= max;
}

Daha fazla açıklama için:

public static bool IsOutside(this int value, int min, int max)
{
    return value < min || max < value;
}

0

Ben sınırları açık olabilir nerede yapmak için zarif bir yol arıyordum (yani. Değerleri hangi sırada olduğundan emin değilim).

Bu yalnızca C: 'nın var olduğu daha yeni C sürümlerinde çalışır.

bool ValueWithinBounds(float val, float bounds1, float bounds2)
{
    return bounds1 >= bounds2 ?
      val <= bounds1 && val >= bounds2 : 
      val <= bounds2 && val >= bounds1;
}

Açıkçası sizin için = işaretlerini değiştirebilirsiniz. Çok tipi döküm ile fantezi alabilir. Sınırlar içinde bir şamandıra dönüşüne ihtiyacım vardı (veya eşit)


0

Zarif çünkü iki sınır değerinden hangisinin önce daha büyük olduğunu belirlemenizi gerektirmez. Ayrıca şube içermez.

public static bool InRange(float val, float a, float b)
{
    // Determine if val lies between a and b without first asking which is larger (a or b)
    return ( a <= val & val < b ) | ( b <= val & val < a );
}

& + | operatörleri
nelerdir

0

Bilmiyorum ama bu yöntemi kullanıyorum:

    public static Boolean isInRange(this Decimal dec, Decimal min, Decimal max, bool includesMin = true, bool includesMax = true ) {

    return (includesMin ? (dec >= min) : (dec > min)) && (includesMax ? (dec <= max) : (dec < max));
}

Ve bu şekilde kullanabilirim:

    [TestMethod]
    public void IsIntoTheRange()
    {
        decimal dec = 54;

        Boolean result = false;

        result = dec.isInRange(50, 60); //result = True
        Assert.IsTrue(result);

        result = dec.isInRange(55, 60); //result = False
        Assert.IsFalse(result);

        result = dec.isInRange(54, 60); //result = True
        Assert.IsTrue(result);

        result = dec.isInRange(54, 60, false); //result = False
        Assert.IsFalse(result);

        result = dec.isInRange(32, 54, false, false);//result = False
        Assert.IsFalse(result);

        result = dec.isInRange(32, 54, false);//result = True
        Assert.IsTrue(result);
    }

Lütfen kod bloğunun altında bir kullanım örneği sağlayın, bu OP'nin amacına uygun olup olmadığını bilmesine yardımcı olacaktır
Gabriel Balsa Cantú

0

Bunlar yardımcı olabilecek bazı Uzantı yöntemleridir

  public static bool IsInRange<T>(this T value, T min, T max)
where T : System.IComparable<T>
    {
        return value.IsGreaterThenOrEqualTo(min) && value.IsLessThenOrEqualTo(max);
    }


    public static bool IsLessThenOrEqualTo<T>(this T value, T other)
         where T : System.IComparable<T>
    {
        var result = value.CompareTo(other);
        return result == -1 || result == 0;
    }


    public static bool IsGreaterThenOrEqualTo<T>(this T value, T other)
         where T : System.IComparable<T>
    {
        var result = value.CompareTo(other);
        return result == 1 || result == 0;
    }

0

Yöntem parametrelerini doğrulayacaksa, çözümlerin hiçbiri ArgumentOutOfRangeException öğesini atmaz ve kapsayıcı / özel min / maks değerlerinin kolay / doğru yapılandırmasına izin vermez.

Bunun gibi kullan

public void Start(int pos)
{
    pos.CheckRange(nameof(pos), min: 0);

    if (pos.IsInRange(max: 100, maxInclusive: false))
    {
        // ...
    }
}

Ben sadece bu güzel fonksiyonları yazdım. Ayrıca geçerli değerler için dallanma (tek bir if) olmaması avantajına sahiptir. En zor kısım, uygun istisna mesajlarını oluşturmaktır.

/// <summary>
/// Returns whether specified value is in valid range.
/// </summary>
/// <typeparam name="T">The type of data to validate.</typeparam>
/// <param name="value">The value to validate.</param>
/// <param name="min">The minimum valid value.</param>
/// <param name="minInclusive">Whether the minimum value is valid.</param>
/// <param name="max">The maximum valid value.</param>
/// <param name="maxInclusive">Whether the maximum value is valid.</param>
/// <returns>Whether the value is within range.</returns>
public static bool IsInRange<T>(this T value, T? min = null, bool minInclusive = true, T? max = null, bool maxInclusive = true)
    where T : struct, IComparable<T>
{
    var minValid = min == null || (minInclusive && value.CompareTo(min.Value) >= 0) || (!minInclusive && value.CompareTo(min.Value) > 0);
    var maxValid = max == null || (maxInclusive && value.CompareTo(max.Value) <= 0) || (!maxInclusive && value.CompareTo(max.Value) < 0);
    return minValid && maxValid;
}

/// <summary>
/// Validates whether specified value is in valid range, and throws an exception if out of range.
/// </summary>
/// <typeparam name="T">The type of data to validate.</typeparam>
/// <param name="value">The value to validate.</param>
/// <param name="name">The name of the parameter.</param>
/// <param name="min">The minimum valid value.</param>
/// <param name="minInclusive">Whether the minimum value is valid.</param>
/// <param name="max">The maximum valid value.</param>
/// <param name="maxInclusive">Whether the maximum value is valid.</param>
/// <returns>The value if valid.</returns>
public static T CheckRange<T>(this T value, string name, T? min = null, bool minInclusive = true, T? max = null, bool maxInclusive = true)
where T : struct, IComparable<T>
{
    if (!value.IsInRange(min, minInclusive, max, maxInclusive))
    {
        if (min.HasValue && minInclusive && max.HasValue && maxInclusive)
        {
            var message = "{0} must be between {1} and {2}.";
            throw new ArgumentOutOfRangeException(name, value, message.FormatInvariant(name, min, max));
        }
        else
        {
            var messageMin = min.HasValue ? GetOpText(true, minInclusive).FormatInvariant(min) : null;
            var messageMax = max.HasValue ? GetOpText(false, maxInclusive).FormatInvariant(max) : null;
            var message = (messageMin != null && messageMax != null) ?
                "{0} must be {1} and {2}." :
                "{0} must be {1}.";
            throw new ArgumentOutOfRangeException(name, value, message.FormatInvariant(name, messageMin ?? messageMax, messageMax));
        }
    }
    return value;
}

private static string GetOpText(bool greaterThan, bool inclusive)
{
    return (greaterThan && inclusive) ? "greater than or equal to {0}" :
        greaterThan ? "greater than {0}" :
        inclusive ? "less than or equal to {0}" :
        "less than {0}";
}

public static string FormatInvariant(this string format, params object?[] args) => string.Format(CultureInfo.InvariantCulture, format, args);

-2

Arıyorsunuz in [1..100]? Bu sadece Pascal.


2
Doğru değil, sadece Pascal değil. Birçok modern dilde bunun gibi özellikler vardır. Örneğin, Kotlin'de buna "Desen Eşleme" denir. Örnek when (number) { in 0..9 -> println("1 digit") in 10..99 -> println("2 digits") in 100..999 -> println("3 digits") }
this.myself
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.