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


157

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

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

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

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


23
다시 : 당신의 "편집"- 간단한 우아 합니다. 나는 개인적으로이 확인을 수행하는 비표준 수단보다 if 문이 더 우아하다고 생각합니다.
Reed Copsey

4
"모든 것이 가능한 한 단순해야하지만 단순하지 않아야합니다." -알버트 아인슈타인
corsiKa

3
@Sergio : 나는 현란한 느낌이 들지 않습니다. 사람들은 종종 간단한 방법을 대체하기 위해 언어의 확장 방법과 다른 도구를 남용한다고 생각합니다. 두 개의 int 값을 비교할 수있는 수백 가지 방법이 있지만 더 분명한 것 이외의 것을 사용하는 것은 좋지 않은 선택입니다.
리드 경찰

3
@Sergio : 그렇다면 질문의 요점을 보지 못하는 것 같습니다.)
Reed Copsey

6
@Sergio : if"바로크"가 아닌 경우 수정하지 마십시오.
StriplingWarrior

답변:


154

많은 옵션이 있습니다 :

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

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

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


334
열거 가능 범위는 먼저 열거 가능 정수를 생성 한 다음 각 항목을 반복하여 찾아야합니다. 그것은 가치를 확인하는 것과 비교할 때 끔찍한 아이디어와 성능입니다. LINQ Extensions가 시원하기 때문에 모토를 채택해야한다고 생각하지만 모든 것에 사용해야한다는 의미는 아닙니다.
Matthew Abbott


15
나는 이것이 성능 측면에서 끔찍한 아이디어라는 데 동의하지만, OP는 if진술 보다 더 멋진 것을 원합니다 . 이것은 확실히 그것을 달성합니다 ...;)
Tim Coker

10
두 번째 매개 변수는 "중지"가 아니라 "계수"입니다. 예를 들어 Enumerable.Range (150, 300) .Contains (400)는 true를 반환합니다.
Shathur

5
이 답변을 사용하지 마십시오 . 범위가 상당히 크면 끔찍한 성능을 발휘합니다. 로 대답을 참조하십시오 @ 올리비에 - jacot-descombes을
아론 Hudon

95

당신은 의미합니까?

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

또는

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

1
거기에 "is"가 필요 없습니다 ... 컴파일되지 않습니다. (그렇지 않으면, 나는 100 % 동의)
리드 Copsey

4
@ 벤, 그냥 내가 시도 할 때까지 기다리 특허가 너무 :)
kemiller2002

나는 이것이 가장 확실한 해결책이라고 생각하지만 그 질문을 우아하게 찾고 있지 않습니까?
Kevin Simple

내가 바꿀 유일한 것은 정적 키워드를 메소드에 추가하는 것입니다. ;-)
Robert S.

<vs <=를 허용하기 위해 경계 플래그, 즉 InRange (number, lowerBound, LOWER_IS_INCLUSIVE, Upperbound, UPPER_IS_EXCLUSIVE)가 필요합니다. 나는 이것을 으르렁 거리는 의도로 썼지 만 이제는 플래그가 실제로 호출자가 사양을 똑바로 갖도록 장려 할 것이라고 생각합니다.
William T. Mallard

56

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

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

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


1
@Ben : 나는 "범위 내에서"라고 말하는 주제에 갔지만 (그런 점에서 모호하지 않다고 생각합니다), 질문 본문에 "1과 100 사이"라는 말이 맞습니다. 물론, 모호합니다).
Adam Robinson

48

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

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

예 :

1 <= x && x <= 100

보다 읽기 쉽다

x >= 1 && x <= 100

19
"쉬운"은 보는 사람의 눈에 있습니다. 나는 개인적으로 왼쪽과 상수 또는 변수에 해당 변수를 선호 하지 오른쪽에 해당합니다.
Adam Robinson

15
펄 6 , 당신은 작성합니다 1 <= x <= 100.
Jordão

2
번호 줄 순서는 처음에 가장 명확하지만 다른 명령에 대해 눈 / 마음을 훈련시킬 수 있습니다. 특히- 항상 상수 를 왼쪽에 배치하는 트릭이 좋습니다. 그렇게하면 컴파일러가 =대신 입력 할 때 알려줍니다 ==. 평등하지 않은 관계 연산자에는 도움이되지 않지만 일관되게 사용하는 것은 쉽습니다.
davidbak

1
나는이 솔루션이 어떤 경우에도 유용하지 않다고 덧붙이고 싶습니다. x복잡한 함수 호출 또는 시간 소모적 인 Linq- 표현을 고려하십시오 . 이 경우 좋은 일이 아닌 두 번 수행합니다. 물론 값을 임시 로컬 변수에 저장해야하지만 다른 if 또는 else-if가 실패한 후에 만 ​​함수를 호출하려는 경우가 있습니다 (예 : else-if-statements). 임시 변수를 사용하면 어쨌든 변수를 호출해야합니다. 이러한 경우 확장 방법 (다른 답변에서 언급)이 가장 좋은 해결책입니다.
Robert S.

4
나는 번호 줄 순서를 좋아하고 보완 테스트를 위해도 좋아합니다. 예를 들어 x <10 || 20 <x. 나에게 "x는 10-20 범위 밖에있다"고 외쳤다.
William T. Mallard

44

프로덕션 코드에서는 간단히 작성합니다.

1 <= x && 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

3
내 표준에 따르면 이것은 가장 우아한 해결책으로 흥미 롭습니다. 나에게도 두 표현을 모두 확인하는 것보다 약간 더 빠르게 실행되는 것 같습니다. 어느 것이 더 빠른지에 대한 연구가 있다면.
Thomas Lindvall

3
최대 14 개의 부동 소수점 숫자를 사용하여 Javascript 및 해당 솔루션에서 솔루션을 테스트했습니다. 매우 좋은 코드 스 니펫입니다. 내가 할 수 있다면 그것은 당신에게 3 번
찬성했습니다

4
그러나 큰 양수가 포함되면 사소한 문제가 있지만 오버플로가 발생할 수 있습니다! XD 코드를 작성할 때 명심하십시오.
BrainStorm.exe

2
이 질문은 우아함을 요구하므로 실용적 가치보다 학문적입니다. 개인적으로 나는 단순한 1 < x && x < 100생산 코드를 사용하려고 합니다. 이해하기 쉽습니다.
Olivier Jacot-Descombes 2016

1
성능에 관심이있는 사용자는 1 < x & x < 100(&& short circuit 없음) 컴파일러에게 x < 100결과에 상관없이 항상 평가할 수 있도록 지시합니다 1 < x. 이상하게도 (분기 예측으로 인해) 때로는이 간단한 작업을 건너 뛰는 것보다 항상이 간단한 작업을 수행하는 것이 더 빠릅니다.
Tom Leys

23

나는 이것을 제안한다 :

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)

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

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


3
나는 사이에 단어를 사용하고 포함 여부를 결정하기 위해 부울 플래그를 사용하여 훨씬 더 단순화합니다
Ben

좋은. 이해하기 쉽습니다. 나는 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 라는 이름을 변경 했지만 부정적인 조건을 좋아하지 않습니다
Paulustrious

21

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

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

해결책처럼! BTW 만들고, 포괄적 지원하는 enum Inclusive값 : Lower, Upper, All. 그리고 통할 In유형의 기능을 하나 추가 매개 변수 enum Inclusive의 기본 값을 Inclusive.All업데이트 To핸들 함수 본문을 All, Lower, Upper값 :
니키타

7

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

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

다음과 같은 것 :

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

코드 계약의 경우 +1; 매개 변수의 유효성을 검사하는 데 고유하지만 자주 사용되는 사례이며 정적 검증은 매우 유용 할 수 있습니다.
Dan Bryant

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

5

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

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

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

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


2
참고로, 하한 및 상한 제약 조건이 별도의 Need 문으로 분할되면 계약 정적 분석기가 더 행복합니다.
Dan Bryant

감사합니다. Dan Bryant, 그것이 바로 내가 찾던 것입니다. 요구 사항 및 기타 관련 규약 계약 방법의 조건 스타일에 대한 제안에서 많은 자료를 찾을 수 없습니다.
jpierson

2

간단한 경우보다 더 많은 코드를 작성하려면 다음을 수행하십시오. 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;
    }

1
경우 다른 사람으로 교체return (value >= Min && value <= Max);
AeroX

비교를 작성하는 우아한 방법은 "논리적 순서로 ..."if (Min <= value && value <= Max)입니다. 훨씬 더 예쁘다.
Fattie

2
이 질문에 대해, 실제 프로젝트에서 (특히 게임 엔지니어 인 경우) 핵심 문제를 언급 한 사람이 근사 문제를 다루지 않는다는 것이 놀랍습니다 . 실제 소프트웨어에서는 본질적으로 "상대적으로 비교하거나"(평등 또는 <,>) 절대로 상황에 따라 오류 문제를 고려하고 처리해야합니다. 더 이상 답변이 허용되지 않으므로이 답변에 대한 부록 (여기서 올바른 답변입니다!)을 편집했습니다.
Fattie

이 관찰과 부록에 감사드립니다.
Tony

2

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

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

2

편집 : 새로운 답변 제공. 이 질문에 대한 첫 번째 답변을 썼을 때 C #을 사용하기 시작했으며, 이제 "솔루션"이 순진하고 비효율적이라는 것을 알게되었습니다.

내 원래 답변 : 더 간단한 버전으로 갈 것입니다.

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

더 나은 방법

더 효율적인 다른 솔루션을 보지 못했기 때문에 (적어도 내 테스트에 따르면) 다른 해결책을 제시 할 것입니다.

음수 범위에서도 작동 하는 새롭고 더 나은 방법 :

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

양수 범위와 음수 범위를 모두 사용할 수 있으며 기본값은

1..100 (포함) 및 사용 x수 등은에 의해 정의 된 임의의 범위가 다음에 확인하는 min하고 max.

좋은 측정을위한 예제 추가

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

보고:

True
True
True
False
True

예 2 : 1에서 150 사이의 임의의 정수 목록 사용

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

보고:

66660 ints found in range 1..100

실행 시간 : 0.016 초


Yeay, 나는 2013 년부터 내 대답에 대한 의견에 대해 언급하고 있습니다 :) @RyanTheLeach :이 질문에 대한 나의 대답은 현재“허용 된”대답과 어떻게 다릅니 까? 나는 그것이 가장 효과적인 순회가 아니라 "끔찍한"것이라는 것을 알고 있습니다. 100 정수를 할당하고 반복하는 것이 얼마나 나쁜가요? 1950 년에는 아마도 사회적으로 받아 들여지지 않았지만 ...
cseder

@RyanTheLeach 나는 당신을 비난하지 않습니다 ... 나는 내 대답을 업데이트 했으므로 더 효율적인 솔루션에 대해 알고 있다면 정교하게하십시오!
cseder

1
댓글이 더 이상 표시되지 않아 삭제했습니다. 수정 해 주셔서 감사합니다. 괜찮아 보입니다.
Ryan The Leach

1

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

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

3
포함 / 포함, 포함 / 독점, 독점 / 포함 및 독점 / 독점의 네 가지 경우가 있습니다.
William T. Mallard

1

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

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


1

이런 건 어때?

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

1

다음과 같이 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);

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


귀하의 Range개체는 사용할 필요 CompareTo항목이 아닌 비교하는 방법을 <연산자.
Servy

맞습니다. 그러나 IComparable을 구현하는 경우 연산자를 재정의해야합니다 (적어도 VS 코드 분석에서 말하는 것임). 내가 잘못 될 수도 있지만, 나는 많은 경험이없는 이는 SO에 대한 내 첫 번째 대답
IEatBagels

아니요, 컴파일러 이것이 작동한다고 말하지 않습니다 . 컴파일되지 않습니다. 객체가 연산자를 IComparable오버로드하지 않고 구현하는 것이 전적으로 합리적입니다 <.
Servy

1

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

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

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


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

용법

double numberToBeChecked = 7;

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

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

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


1

허용 된 답변에 대한 @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);

1

우아함과 관련하여 수학 표기법에 가장 가까운 것은 ( a <= x <= b ) 가독성을 약간 향상시킵니다.

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

자세한 설명 :

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

0

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

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

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

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


0

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

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

& + | 비트 연산자
nelsontruran

0

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

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

코드 블록 아래에 사용 예를 제공하십시오. OP가 자신의 목적에 맞는지 알 수 있습니다.
Gabriel Balsa Cantú

0

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

  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

메소드 매개 변수의 유효성을 검사하는 경우 어떤 솔루션도 ArgumentOutOfRangeException을 발생시키지 않으며 포괄적 / 배타적 최소 / 최대 값을 쉽고 적절하게 구성 할 수 없습니다.

이런 식으로 사용

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

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

방금이 아름다운 기능들을 썼습니다. 또한 유효한 값에 대해 분기 (단일 if)가없는 장점이 있습니다. 가장 어려운 부분은 적절한 예외 메시지를 작성하는 것입니다.

/// <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

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


2
파스칼뿐만 아니라 사실도 아닙니다. 많은 현대 언어에는 이와 같은 기능이 있습니다. 예를 들어 Kotlin에서는 "패턴 일치"라고합니다. 예 when (number) { in 0..9 -> println("1 digit") in 10..99 -> println("2 digits") in 100..999 -> println("3 digits") }
this.myself
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.