IT story

숫자가 범위 내에 있는지 우아하게 확인하는 방법은 무엇입니까?

hot-time 2020. 6. 23. 07:19
반응형

숫자가 범위 내에 있는지 우아하게 확인하는 방법은 무엇입니까?


C # 및 .NET 3.5 / 4로 어떻게 우아하게 수행 할 수 있습니까?

예를 들어, 숫자는 1과 100 사이 일 수 있습니다.

충분하다면 단순하다는 것을 알고 있습니다. 그러나이 질문의 키워드는 우아합니다. 프로덕션 용이 아닌 장난감 프로젝트 용입니다.

이 질문은 속도가 아니라 코드의 아름다움에 관한 것입니다. 효율성과 그에 대한 이야기를 중단하십시오. 성가대에게 설교한다는 것을 기억하십시오.


많은 옵션이 있습니다 :

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

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

또한이 SO 게시물 에서 정규식 옵션을 확인하십시오 .


당신은 의미합니까?

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

또는

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

여기에 노이즈를 추가하기 위해 확장 방법을 만들 수 있습니다.

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

다음과 같은 작업을 수행 할 수 있습니다 ...

int val = 15;

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

말하자면, 이것은 수표 자체가 단 한 줄일 때 해야하는 바보 같은 것 같습니다.


다른 사람들이 말했듯이 간단한 if를 사용하십시오.

주문에 대해 생각해야합니다.

예 :

1 <= x && x <= 100

보다 읽기 쉽다

x >= 1 && x <= 100

수학을 사용하여 비교 횟수를 2 개에서 1 개로 줄일 수 있습니다. 아이디어는 숫자가 범위를 벗어나면 두 요소 중 하나가 음수가되고 숫자가 범위 중 하나와 같으면 0이되는 것입니다.

범위가 포함 된 경우 :

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

또는

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

범위가 독점적 인 경우 :

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

또는

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

그러나 프로덕션 코드에서 간단히 작성 1 < x && x < 100하면 이해하기 쉽습니다.


약간의 확장 방법 남용으로 다음과 같은 "우아한"솔루션을 얻을 수 있습니다.

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!");
        }
    }
}

나는 이것을 제안한다 :

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

예 :

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

물론 변수가있는 경우 :

myvalue.IsWithin(min, max)

읽기 쉽고 (인간의 언어에 가깝다) 모든 비교 가능한 유형 (정수, 이중, 사용자 정의 유형 ...)과 함께 작동합니다.

개발자가 코드를 이해하기 위해 "브레인 사이클"을 낭비하지 않기 때문에 코드를 쉽게 읽을 수 있어야합니다. 코딩 세션이 길어지면 뇌주기가 낭비되어 개발자가 더 일찍 피곤해지고 버그가 발생하기 쉽습니다.


이것이 부수적 인 경우 간단 if합니다. 여러 곳에서 이런 일이 발생하면 다음 두 가지를 고려할 수 있습니다.

  • PostSharp . 컴파일 후 메소드에 코드를 '주입'하는 속성을 가진 메소드를 장식하십시오. 확실하지는 않지만, 이것을 사용할 수 있다고 상상할 수 있습니다.

다음과 같은 것 :

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • 코드 계약 . 코드의 정적 검증 및 코드를 사용하는 위치를 컴파일하여 제약 조건을 컴파일 타임에 확인할 수 있다는 이점이 있습니다.

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

&&표현식을 사용하여 두 비교를 결합하는 것이 가장 우아한 방법입니다. 멋진 확장 방법 등을 사용하려고하면 상한, 하한 또는 둘 다를 포함할지에 대한 문제가 발생합니다. 추가 변수를 추가하거나 포함 이름을 나타 내기 위해 확장명을 변경하면 코드가 길어지고 읽기 어려워집니다 (대부분의 프로그래머에게). 또한 Resharper와 같은 도구는 비교가 이해가되지 않으면 ( number > 100 && number < 1) 경고합니다 . 방법을 사용하면 ( 'i.IsBetween (100, 1)') 사용할 수 없습니다.

내가 유일하게 할 말은 예외를 던질 의도로 입력을 검사하는 경우 코드 계약 사용을 고려해야한다는 것입니다.

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

이것은보다 우아하며 if(...) throw new Exception(...), 누군가 숫자가 먼저 범위 내에 있는지 확인하지 않고 메소드를 호출하려고하면 컴파일 타임 경고를받을 수 있습니다.


간단한 경우보다 더 많은 코드를 작성하려면 다음을 수행하십시오. IsBetween이라는 확장 메서드 만들기

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

추가:실제로는 코드베이스에서 "평등을 검사하는 것"(또는 <,>)을 거의 사용하지 않는다는 점에 주목할 가치가 있습니다. (가장 사소한 상황이 아닌 경우) 순수한 예를 들어, 모든 게임 프로그래머는 모든 프로젝트에서 다음과 같은 범주를 기본 사항으로 사용합니다. 이 예제에서는 해당 환경에 내장 된 함수 (대략)를 사용합니다. 실제로는 일반적으로 엔지니어링중인 상황 유형에 따라 실수의 컴퓨터 표현에 대한 비교의 의미에 대한 자체 개념을 신중하게 개발해야합니다. (아마도 컨트롤러, PID 컨트롤러 등과 같은 작업을 수행하는 경우 전체 문제가 핵심적이고 매우 어려워 프로젝트의 본질이된다는 것을 언급조차하지 마십시오.

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

다른 모든 대답은 나에 의해 발명되지 않았기 때문에 여기에 내 구현 만 있습니다.

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

그런 다음 다음과 같이 사용할 수 있습니다.

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

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

오래된 마음에 드는 새로운 트위스트 :

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

C에서 시간 효율성이 중요하고 정수 오버플로가 줄어든다면 할 수 if ((unsigned)(value-min) <= (max-min)) ...있습니다. 'max'및 'min'이 독립 변수 인 경우 (max-min)에 대한 추가 빼기는 시간을 낭비하지만 컴파일 시간에 해당 표현식을 사전 계산하거나 런타임에 한 번 계산하여 많은 테스트를 수행 할 수있는 경우 동일한 범위에 해당하는 숫자 인 경우, 값이 범위 내에있는 경우에도 위의 표현식을 효율적으로 계산할 수 있습니다 (값의 큰 부분이 유효한 범위 아래에 있으면 값이 빠른 경우 종료if ((value >= min) && (value <= max)) ... 되므로 사용하는 것이 더 빠를 수 있습니다) 최소 미만입니다).

그러나 그러한 구현을 사용하기 전에 대상 시스템을 벤치마킹하십시오. 일부 프로세서에서는 두 부분의 표현이 모든 경우에 더 빠를 수 있습니다. 두 비교는 독립적으로 수행 될 수 있지만 빼기 및 비교 방법에서는 비교가 실행되기 전에 빼기가 완료되어야합니다.


이런 건 어때?

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

다음과 같은 확장 방법으로 (테스트) :

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

나는 Range 객체를 다음과 같이 할 것이다.

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

그런 다음이 방법을 사용하십시오.

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

그렇게하면 다른 유형에 재사용 할 수 있습니다.


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

용법

double numberToBeChecked = 7;

var result = numberToBeChecked.IsBetween (100,122);

var 결과 = 5. IsBetween (100,120);

var 결과 = 8.0. IsBetween (1.2,9.6);


더 간단한 버전으로 갈 것입니다.

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

나는 경계가 전환 될 수있는 곳을 수행하는 우아한 방법을 찾고있었습니다 (즉, 값이 어떤 순서인지 확실하지 않음).

이것은? :가 존재하는 최신 버전의 C #에서만 작동합니다.

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

분명히 목적에 따라 = 기호를 변경할 수 있습니다. 타입 캐스팅으로도 화려할 수 있습니다. 방금 범위 내에서 float 반환이 필요했습니다.


"숫자"가 범위 내에 있는지 확인할 때 의미가 무엇인지 명확해야하며 두 숫자가 같은 의미는 무엇입니까? 일반적으로 모든 엡실론 볼에 모든 부동 소수점 숫자를 래핑해야합니다.이 값은 작은 값을 선택하고 두 값이 가까운 경우 동일한 것입니다.

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

이 두 헬퍼를 배치하고 필요한 수를 두 배로 캐스팅 할 수 있다고 가정합니다. 열거 형과 다른 방법 만 있으면됩니다.

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

다른 방법은 다음과 같습니다.

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

이제 이것은 원하는 것보다 훨씬 많을 수 있지만 항상 반올림을 처리하고 값이 반올림되었는지 여부와 장소를 기억하려고하지 않습니다. 필요한 경우 엡실론에서 작동하고 엡실론을 변경할 수 있도록 이것을 쉽게 확장 할 수 있습니다.


두 경계 값 중 어느 것이 더 큰지 결정할 필요가 없기 때문에 우아합니다. 또한 가지가 없습니다.

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

모르겠지만이 방법을 사용합니다.

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

그리고 이것이 내가 사용할 수있는 방법입니다.

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

이것들은 도움이 될 수있는 확장 방법입니다

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

허용 된 답변에 대한 @Daap의 의견에 관심이 있고 값을 한 번만 전달할 수있는 경우 다음 중 하나를 시도 할 수 있습니다

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

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

또는

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

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

찾고있는 in [1..100]? 파스칼뿐입니다.

참고 URL : https://stackoverflow.com/questions/3188672/how-to-elegantly-check-if-a-number-is-with-a-range

반응형