2009-07-16 16 views
18

Poszukuję płynnego sposobu określania, czy liczba mieści się w określonym zestawie zakresów. Mój obecny kod wygląda mniej więcej tak:Ustal, czy liczba mieści się w określonym zbiorze przedziałów

int x = 500; // Could be any number 

if ((x > 4199 && x < 6800) || 
    (x > 6999 && x < 8200) || 
    (x > 9999 && x < 10100) || 
    (x > 10999 && x < 11100) || 
    (x > 11999 && x < 12100)) 
{ 
    // More awesome code 
} 

Czy jest lepszy sposób na zrobienie tego?

+2

Lepiej w jakim sensie? To wydaje się całkiem rozsądne. –

+3

@Michael: Wierzę, że Steve prosi o bardziej elokwentne podejście. –

+1

Myślę, że podany przykład jest najlepszym (patrząc i zrozumiałym) sposobem na szczerość! – ThePower

Odpowiedz

36

Metody rozszerzenia?

bool Between(this int value, int left, int right) 
{ 
    return value > left && value < right; 
} 

if(x.Between(4199, 6800) || x.Between(6999, 8200) || ...) 

Można również zrobić to straszny Hack:

bool Between(this int value, params int[] values) 
{ 
    // Should be even number of items 
    Debug.Assert(values.Length % 2 == 0); 

    for(int i = 0; i < values.Length; i += 2) 
     if(!value.Between(values[i], values[i + 1]) 
      return false; 

    return true; 
} 

if(x.Between(4199, 6800, 6999, 8200, ...) 

Awful hack, poprawiła:

class Range 
{ 
    int Left { get; set; } 
    int Right { get; set; } 

    // Constructors, etc. 
} 

Range R(int left, int right) 
{ 
    return new Range(left, right) 
} 

bool Between(this int value, params Range[] ranges) 
{ 
    for(int i = 0; i < ranges.Length; ++i) 
     if(value > ranges[i].Left && value < ranges[i].Right) 
      return true; 

    return false; 
} 

if(x.Between(R(4199, 6800), R(6999, 8200), ...)) 

Albo, jeszcze lepiej (to nie pozwala Duplikat dolne granice):

bool Between(this int value, Dictionary<int, int> ranges) 
{ 
    // Basically iterate over Key-Value pairs and check if value falls within that range 
} 

if(x.Between({ { 4199, 6800 }, { 6999, 8200 }, ... } 
+4

Ja * zdecydowanie * sprawię, że Range będzie niezmienny. Mutowalność jest do niczego podobna ... –

+0

Whoa! _Formatowanie_ w ** komentarze **! @Jon Tak, masz absolutną rację. –

14

Określenie typu zakresu, a następnie kr spróbuj ustawić zakresy i metodę rozszerzenia, aby sprawdzić, czy wartość leży w dowolnym z zakresów. Wtedy zamiast twarde kodowania wartości, można utworzyć zbiór zakresów i być może niektórych poszczególnych zakresach, dając im przydatnych nazw wyjaśnić dlaczego jesteś zainteresowany w nich:

static readonly Range InvalidUser = new Range(100, 200); 
static readonly Range MilkTooHot = new Range (300, 400); 

static readonly IEnumerable<Range> Errors = 
    new List<Range> { InvalidUser, MilkTooHot }; 

... 

// Normal LINQ (where Range defines a Contains method) 
if (Errors.Any(range => range.Contains(statusCode)) 
// or (extension method on int) 
if (statusCode.InAny(Errors)) 
// or (extension methods on IEnumerable<Range>) 
if (Errors.Any(statusCode)) 

Możesz być zainteresowany ogólny typ Range, który jest częścią MiscUtil. Pozwala on na iteracji w prosty sposób, a także:

foreach (DateTime date in 19.June(1976).To(25.December(2005)).Step(1.Days())) 
{ 
    // etc 
} 

(Oczywiście, że jest także korzystanie z niektórych DateTime/TimeSpan związanych z metod rozszerzenie, ale masz pomysł.)

0

jeśli trzeba iteracyjne nad par wartości w pewnym momencie, proponuję uchwycić maksymalną niższą wartość i minimalną wartość górną, jak to zrobić w zmiennych i zrobić:

if (x>max_lower && x <min_upper) 
{ 
    // More awesome code 

} 
+0

co, jeśli to nie jest niemożliwe? –

+0

@ArsenMkrt, powiedział _co, jeśli to nie jest niemożliwe? _, Nie niemożliwe = możliwe? Nie jestem pewien, o co pytasz. –

0

Spróbuj coś takiego:

struct Range 
{ 
    public readonly int LowerBound; 
    public readonly int UpperBound; 

    public Range(int lower, int upper) 
    { LowerBound = lower; UpperBound = upper; } 

    public bool IsBetween(int value) 
    { return value >= LowerBound && value <= UpperBound; } 
} 

public void YourMethod(int someValue) 
{ 
    List<Range> ranges = {new Range(4199,6800),new Range(6999,8200), 
         new Range(9999,10100),new Range(10999,11100), 
         new Range(11999,12100)}; 

    if(ranges.Any(x => x.IsBetween(someValue)) 
    { 
     // your awesome code... 
    } 
} 
1
class Range { 

    public Range(int x, int y) { 
     X = x; 
     Y = y; 
    } 

    public int X { get; set; } 
    public int Y { get; set; } 
} 

var ranges = new List<Range>(); 
ranges.Add(new Range(4199,6800)); 
ranges.Add(new Range(6999,8200)); 
ranges.Add(new Range(9999,10100)); 
ranges.Add(new Range(10999,11100)); 
ranges.Add(new Range(11999,12100)); 

bool inRange = ranges.Count(r => x >= r.X && x <= r.Y) > 0; 
//or -- Based on Jons recommendation 
bool inRange = ranges.Any(r => x >= r.X && x <= r.Y); 
+1

Ogólnie lepiej jest użyć Any() niż Count (...)> 0, ponieważ wtedy może przestać, gdy tylko znajdzie pasujące. –

+0

Dzięki za sugestię, dodam to do wpisu. – Bob

7

Osobiście preferuję metodę rozszerzenia sugerowane przez @Anton - ale jeśli nie można tego zrobić, i będziemy się trzymać z aktualnym kodem, myślę, że można uczynić go bardziej czytelnym poprzez odwrócenie pierwszego zestawu warunków na każdej linii w następujący sposób ...

int x = 500; // Could be any number 
if ((4199 < x && x < 6800) || 
    (6999 < x && x < 8200) || 
    (9999 < x && x < 10100) || 
    (10999 < x && x < 11100) || 
    (11999 < x && x < 12100)) 
{ 
    // More awesome code 
} 
+0

Dzięki temu jest bardziej czytelny. Jest to wskazane w Code Complete i od tego czasu używam tego stylu. – Carra

3

podejście LINQ:

dodać odwołanie:

using System.Linq; 

     /// <summary> 
     /// Test to see if value is in specified range. 
     /// </summary> 
     /// <param name="aStart">int</param> 
     /// <param name="aEnd">int</param> 
     /// <param name="aValueToTest">int</param> 
     /// <returns>bool</returns> 
     public static bool CheckValueInRange(int aStart, int aEnd, int aValueToTest) 
     { 
      // check value in range... 
      bool ValueInRange = Enumerable.Range(aStart, aEnd).Contains(aValueToTest); 
      // return value... 
      return ValueInRange; 
     } 
+2

CheckValueInRange działa tylko przy dolnej granicy zakresu. Ponieważ drugim parametrem Enumerable.Range jest liczba wartości dodawanych do sekwencji (zamiast końca zakresu), wartości wyższe niż górna granica będą nadal zgłaszane jako w zakresie. Przykład: CheckValueInRange (4199,6800, 6801) zwróci wartość True, pomimo że 6801 znajduje się poza pożądaną górną granicą, ponieważ Enumerable.Range (4199,6800) zwraca zakres liczb od 4199 do 10998. –

+0

To podejście jest bardzo złe dla wydajności, ponieważ tablica zostanie przydzielona z długością zakresu, a LINQ spróbuje iterować ją, aby znaleźć swoją wartość testową. –

-1

W Pascal (Delphi) masz następujące oświadczenie:

if x in [40..80] then 
begin 
end 

Więc jeśli wartość x mieści się w tym zakresie swoje polecenie wykonać. Szukałem odpowiednika C#, ale nie mogę znaleźć czegoś tak prostego i "eleganckiego" jak to.

Ta instrukcja if() akceptuje łańcuchy, bajty itp.

+3

To jest pytanie C#. –

+0

Niezbyt istotne. – Mana

Powiązane problemy