Làm thế nào để kiểm tra một cách tao nhã nếu một số nằm trong một phạm vi?


157

Làm cách nào tôi có thể thực hiện việc này một cách thanh lịch với C # và .NET 3.5 / 4?

Ví dụ, một số có thể nằm trong khoảng từ 1 đến 100.

Tôi biết một đơn giản nếu sẽ đủ; nhưng từ khóa cho câu hỏi này là sự thanh lịch. Đó là cho dự án đồ chơi của tôi không dành cho sản xuất.

Câu hỏi này không phải là về tốc độ, mà là về vẻ đẹp mã. Ngừng nói về hiệu quả và như vậy; hãy nhớ rằng bạn đang giảng cho dàn hợp xướng.


23
Re: "chỉnh sửa" của bạn - đơn giản là thanh lịch . Cá nhân tôi thấy câu lệnh if thanh lịch hơn bất kỳ phương tiện phi tiêu chuẩn nào khi thực hiện kiểm tra này ...
Reed Copsey

4
"Mọi thứ nên được làm đơn giản nhất có thể, nhưng không đơn giản hơn." - Albert Einstein
corsiKa

3
@Sergio: Tôi không cảm thấy mình là người phạm tội. Tôi cảm thấy rằng mọi người thường lạm dụng các phương thức mở rộng và các công cụ khác trong ngôn ngữ để thay thế những thứ vốn đã đơn giản. Có hàng trăm cách để so sánh hai giá trị int, nhưng sử dụng bất cứ thứ gì nhưng rõ ràng hơn là một lựa chọn kém, IMO.
Sậy Copsey

3
@Sergio: Tôi đoán, sau đó, tôi không thấy điểm của câu hỏi;)
Reed Copsey

6
@Sergio: nếu ifkhông "baroque" thì đừng sửa nó.
StriplingWar Warrior

Câu trả lời:


151

Có rất nhiều lựa chọn:

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

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

Ngoài ra, kiểm tra bài SO này cho các tùy chọn regex.


334
Enumerable.Range phải tạo ra vô số các số nguyên trước, sau đó lặp qua từng mục để tìm nó. Đó là một ý tưởng và hiệu suất khủng khiếp so với việc kiểm tra một giá trị là rất khác nhau. Tôi nghĩ rằng chúng ta nên áp dụng một moto, chỉ vì Tiện ích mở rộng LINQ rất tuyệt, không có nghĩa là chúng nên được sử dụng cho mọi thứ.
Matthew Abbott


15
Tôi đồng ý rằng đây là một ý tưởng tồi về hiệu năng, nhưng OP muốn thứ gì đó lạ mắt hơn là một iftuyên bố. Điều này chắc chắn hoàn thành điều đó ...;)
Tim Coker

10
Cần lưu ý rằng tham số thứ hai không phải là "dừng", mà là "đếm". Vì vậy, ví dụ, Enumerable.Range (150, 300) .Contains (400) sẽ trả về true.
Shathur

5
Xin đừng sử dụng câu trả lời này . Nó sẽ có hiệu suất khủng khiếp nếu phạm vi của bạn khá lớn. Vui lòng xem câu trả lời của @ olivier-jacot-descombes
Aaron Hudon

95

Ý bạn là?

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

hoặc là

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

1
Bạn không cần "là" trong đó ... Điều này sẽ không được biên dịch. (Nếu không, tôi đồng ý 100%)
Reed Copsey

4
@Ben, chỉ cần đợi cho đến khi tôi thử và cấp bằng sáng chế cho nó :)
kemiller2002

Tôi nghĩ rằng đây là giải pháp vững chắc nhất nhưng không phải là người hỏi đang tìm kiếm, phải không?
Kevin Simple

Điều duy nhất tôi sẽ thay đổi là thêm từ khóa tĩnh vào phương thức. ;-)
Robert S.

Cần các cờ ranh giới, tức là InRange (số, lowBound, LOWER_IS_INCLUSIVE, Upperward, UPPER_IS_EXCLUSIVE) để cho phép <vs <=. Tôi đã viết điều này có ý định là ngớ ngẩn nhưng bây giờ tôi nghĩ về nó, những lá cờ thực sự sẽ khuyến khích người gọi để có được đặc điểm kỹ thuật của họ thẳng.
William T. Mallard

56

Chỉ cần thêm vào tiếng ồn ở đây, bạn có thể tạo một phương thức mở rộng:

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

Điều đó sẽ cho phép bạn làm một cái gì đó như ...

int val = 15;

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

Điều đó đang được nói, điều này có vẻ như là một điều ngớ ngẩn để làm khi bản thân kiểm tra chỉ là một dòng.


1
@Ben: Tôi đã đi vào chủ đề, nói "trong một phạm vi" (mà tôi không nghĩ là mơ hồ về vấn đề đó), nhưng bạn nói đúng rằng cơ quan câu hỏi nói "từ 1 đến 100" (nghĩa là , tất nhiên, mơ hồ).
Adam Robinson

48

Như những người khác nói, sử dụng một đơn giản nếu.

Bạn nên suy nghĩ về việc đặt hàng.

ví dụ

1 <= x && x <= 100

dễ đọc hơn

x >= 1 && x <= 100

19
"Dễ dàng hơn" là trong mắt của kẻ si tình. Cá nhân tôi thích có biến trong câu hỏi bên trái và hằng số hoặc biến không trong câu hỏi bên phải.
Adam Robinson

15
Trong Perl 6 , bạn sẽ viết 1 <= x <= 100.
Jordão

2
Thứ tự dòng số là rõ ràng nhất ban đầu - nhưng bạn có thể đào tạo mắt / tâm trí của bạn cho các đơn hàng khác. Cụ thể - Tôi thích thủ thuật đặt hằng số ở bên trái, luôn luôn. Nếu bạn làm điều đó, trình biên dịch sẽ cho bạn biết khi bạn gõ =thay vì ==. Nó không giúp với các toán tử quan hệ không bình đẳng - nhưng nó dễ dàng làm quen với việc sử dụng nó một cách nhất quán.
davidbak

1
Tôi chỉ muốn thêm rằng giải pháp này không hữu ích trong mọi trường hợp. Xem xét xlà một cuộc gọi chức năng phức tạp hoặc biểu thức Linq tốn thời gian. Trong trường hợp này, bạn sẽ làm điều này hai lần, đó không phải là một điều tốt. Chắc chắn bạn nên lưu trữ giá trị vào một biến cục bộ tạm thời nhưng có một số trường hợp (ví dụ: trong các câu lệnh if-if-if khác) trong đó bạn chỉ muốn gọi các hàm sau nếu khác hoặc nếu không - nếu thất bại. Với các biến tạm thời, bạn phải gọi chúng bằng mọi giá. Một phương pháp mở rộng (được đề cập trong các câu trả lời khác) là imho giải pháp tốt nhất trong những trường hợp đó.
Robert S.

4
Tôi cũng thích thứ tự dòng số, và cũng cho bài kiểm tra bổ sung, ví dụ x <10 || 20 <x. Đối với tôi, nó hét "x nằm ngoài phạm vi 10 - 20".
William T. Mallard

44

Trong mã sản xuất tôi chỉ đơn giản là viết

1 <= x && x <= 100

Điều này là dễ hiểu và rất dễ đọc.


Dưới đây là một phương pháp thông minh giúp giảm số lượng so sánh từ hai xuống một bằng cách sử dụng một số phép toán. Ý tưởng là một trong hai yếu tố trở thành âm nếu số nằm ngoài phạm vi và bằng 0 nếu số đó bằng một trong các giới hạn:

Nếu giới hạn được bao gồm:

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

hoặc là

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

Nếu giới hạn là độc quyền:

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

hoặc là

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

3
Theo tiêu chuẩn của tôi, đây là giải pháp tao nhã nhất, thú vị là đối với tôi nó dường như chạy nhanh hơn cả việc kiểm tra cả hai biểu thức, điều đó nói rằng nó cũng có vẻ không nhất quán hơn (tốc độ dường như thay đổi nhiều hơn) sẽ rất thú vị khi nhìn thấy nếu có bất kỳ nghiên cứu nào được thực hiện trên cái nào nhanh hơn.
Thomas Lindvall

3
Đã thử nghiệm giải pháp của bạn trên javascript và độ chính xác của nó với các số dấu phẩy động lên đến 14 số thập phân. Đó là một đoạn mã rất tốt. Nó sẽ nâng bạn lên ba lần nếu tôi có thể
rubbyrubber

4
Mặc dù, có một vấn đề nhỏ nếu số lượng lớn tích cực có liên quan, nó có thể tràn! XD Bạn có thể muốn ghi nhớ điều đó khi viết mã của mình.
BrainStorm.exe

2
Câu hỏi yêu cầu sự thanh lịch và do đó mang tính học thuật nhiều hơn là giá trị thực tế. Cá nhân tôi sẽ chỉ sử dụng một 1 < x && x < 100mã đơn giản trong mã sản xuất. Nó dễ hiểu hơn.
Olivier Jacot-Descombes

1
Đối với những người quan tâm về hiệu suất, 1 < x & x < 100(không && ngắn mạch) hướng dẫn trình biên dịch rằng nó luôn có thể đánh giá x < 100bất kể kết quả của 1 < x. Thật kỳ lạ (do dự đoán chi nhánh) sẽ nhanh hơn khi luôn thực hiện thao tác đơn giản này hơn là đôi khi bỏ qua nó.
Tom Leys

23

Tôi đề xuất này:

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

Ví dụ:

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

và tất nhiên với các biến:

myvalue.IsWithin(min, max)

Thật dễ đọc (gần với ngôn ngữ của con người) và hoạt động với bất kỳ loại so sánh nào (số nguyên, gấp đôi, loại tùy chỉnh ...).

Có mã dễ đọc là rất quan trọng vì nhà phát triển sẽ không lãng phí "chu kỳ não" để hiểu nó. Trong các phiên mã hóa dài, các chu kỳ não bị lãng phí khiến nhà phát triển mệt mỏi sớm hơn và dễ bị lỗi.


3
tôi sẽ đơn giản hóa hơn nữa bằng cách sử dụng từ giữa và có một cờ boolean để xác định xem có bao gồm hay không
Ben

Tốt Thật dễ hiểu. Tôi đã thay đổi tên 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 sẽ hoạt động nhưng tôi không thích điều kiện tiêu cực
Paulustrious

21

Với một chút lạm dụng phương thức mở rộng, chúng ta có thể có được giải pháp "thanh lịch" sau:

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

Giống như giải pháp! Btw để hỗ trợ toàn diện, tạo ra enum Inclusivevới các giá trị: Lower, Upper, All. Và pass cho các Inchức năng một tham số bổ sung các loại enum Inclusivevới giá trị mặc định Inclusive.All, cập nhật các Tocơ quan chức năng để xử lý All, Lower, Uppergiá trị :)
Nikita

7

Nếu điều này là ngẫu nhiên, đơn giản iflà tất cả những gì bạn cần. Nếu điều này xảy ra ở nhiều nơi, bạn có thể muốn xem xét hai điều này:

  • PostSharp . Trang trí các phương thức với các thuộc tính 'tiêm' mã vào phương thức sau khi biên dịch. Tôi không biết chắc chắn, nhưng tôi có thể tưởng tượng nó có thể được sử dụng cho việc này.

Cái gì đó như:

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • Hợp đồng mã . Có lợi thế là các ràng buộc có thể được kiểm tra tại thời điểm biên dịch, bằng cách xác minh tĩnh mã của bạn và các địa điểm sử dụng mã của bạn.

+1 cho hợp đồng mã; nó đặc trưng để xác thực một tham số, nhưng đó là trường hợp sử dụng thường xuyên và xác minh tĩnh có khả năng cực kỳ hữu ích.
Dan Bryant

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

5

Sử dụng một &&biểu thức để tham gia hai so sánh đơn giản là cách thanh lịch nhất để làm điều này. Nếu bạn thử sử dụng các phương thức mở rộng ưa thích và như vậy, bạn sẽ gặp phải câu hỏi là nên bao gồm giới hạn trên, giới hạn dưới hay cả hai. Khi bạn bắt đầu thêm các biến bổ sung hoặc thay đổi tên mở rộng để chỉ ra những gì được bao gồm, mã của bạn sẽ trở nên dài hơn và khó đọc hơn (đối với đại đa số các lập trình viên). Hơn nữa, các công cụ như Resharper sẽ cảnh báo bạn nếu so sánh của bạn không có ý nghĩa ( number > 100 && number < 1), điều mà họ sẽ không làm nếu bạn sử dụng một phương thức ('i.IsB between (100, 1)').

Nhận xét khác mà tôi đưa ra là nếu bạn đang kiểm tra đầu vào với ý định ném ngoại lệ, bạn nên xem xét sử dụng hợp đồng mã:

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

Điều này thanh lịch hơn if(...) throw new Exception(...), và bạn thậm chí có thể nhận được các cảnh báo về thời gian biên dịch nếu ai đó cố gắng gọi phương thức của bạn mà không đảm bảo rằng số đó nằm trong giới hạn trước.


2
FYI, bộ phân tích tĩnh hợp đồng hạnh phúc hơn khi các ràng buộc ràng buộc dưới và ràng buộc trên được tách thành các câu lệnh Yêu cầu riêng biệt.
Dan Bryant

Cảm ơn Dan Bryant, đó chính xác là những gì tôi đã ở đây để tìm kiếm. Không thể tìm thấy nhiều tài liệu về các đề xuất về kiểu điều kiện cho Yêu cầu và các phương thức Hợp đồng mã liên quan khác.
jpierson

2

Nếu bạn muốn viết nhiều mã hơn đơn giản, có lẽ bạn có thể: Tạo Phương thức tiện ích mở rộng có tên IsB between

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

Phụ lục:Điều đáng chú ý là trong thực tế, bạn rất hiếm khi "chỉ kiểm tra sự bình đẳng" (hoặc <,>) trong một cơ sở mã. (Khác với những tình huống tầm thường nhất.) Hoàn toàn là một ví dụ, bất kỳ lập trình viên trò chơi nào cũng sẽ sử dụng các danh mục giống như sau trong mọi dự án, như một vấn đề cơ bản. Lưu ý rằng trong ví dụ này, nó (tình cờ) sử dụng một hàm (Mathf.Approimate) được xây dựng trong môi trường đó; trong thực tế, bạn thường phải phát triển một cách cẩn thận các khái niệm của riêng bạn về ý nghĩa so sánh đối với các biểu diễn trên máy tính của các số thực, cho loại tình huống bạn đang thiết kế. (Thậm chí đừng đề cập rằng nếu bạn đang làm một cái gì đó như, có thể là bộ điều khiển, bộ điều khiển PID hoặc tương tự, toàn bộ vấn đề trở thành trung tâm và rất khó khăn, nó trở thành bản chất của dự án.

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
Thay thế if và khác bằngreturn (value >= Min && value <= Max);
AeroX 27/11/14

cách thanh lịch để viết so sánh là "theo thứ tự logic ..." if (Min <= value && value <= Max). Đó là đẹp hơn nhiều.
Fattie

2
Hơn nữa về câu hỏi này, thật đáng ngạc nhiên khi không ai đề cập đến vấn đề trung tâm trong bất kỳ dự án thực tế nào (đặc biệt nếu bạn là kỹ sư trò chơi) là bạn phải giải quyết vấn đề gần đúng . Trong bất kỳ phần mềm trong thế giới thực nào, về cơ bản, bạn không bao giờ "chỉ làm một phép so sánh" (dù là bình đẳng hay <,>), bạn phải xem xét và xử lý vấn đề lỗi, tùy thuộc vào tình huống hiện tại. Tôi đã chỉnh sửa trong phần phụ lục cho câu trả lời này (câu trả lời đúng duy nhất ở đây!) Vì không có thêm câu trả lời nào được cho phép.
Fattie

Cảm ơn bạn đã quan sát và phụ lục này.
Tony

2

Vì tất cả các câu trả lời khác không phải do tôi phát minh ra, đây chỉ là cách thực hiện của tôi:

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

Sau đó bạn có thể sử dụng nó như thế này:

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

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

2

EDIT: Trả lời mới được cung cấp. Tôi mới bắt đầu sử dụng C # khi tôi viết câu trả lời đầu tiên cho câu hỏi này và sau khi nhận ra, bây giờ tôi nhận ra rằng "giải pháp" của tôi là / ngây thơ và không hiệu quả.

Câu trả lời ban đầu của tôi: Tôi sẽ dùng phiên bản đơn giản hơn:

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

Một cách tốt hơn

Vì tôi chưa thấy giải pháp nào hiệu quả hơn (ít nhất là theo thử nghiệm của tôi), tôi sẽ thử lại lần nữa.

Cách mới và tốt hơn cũng hoạt động với các phạm vi tiêu cực :

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

Điều này có thể được sử dụng với cả phạm vi tích cực và tiêu cực và mặc định cho một phạm vi

1..100 (đã bao gồm) và sử dụng xlàm số để kiểm tra theo sau là một phạm vi tùy chọn được xác định bởi minmax.

Thêm ví dụ cho biện pháp tốt

Ví dụ 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));

Trả về:

True
True
True
False
True

Ví dụ 2: Sử dụng danh sách các số nguyên ngẫu nhiên trong khoảng từ 1 đến 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);

Trả về:

66660 ints found in range 1..100

Thời gian thực hiện: 0,016 giây


Yeay, tôi đang bình luận về một nhận xét cho câu trả lời của tôi từ năm 2013 :) @RyanTheLeach: Câu trả lời của tôi cho câu hỏi này khác với câu trả lời hiện tại như thế nào? Tôi nhận ra rằng đó không phải là giao dịch hiệu quả nhất, nhưng có phải là khủng khiếp không? Làm thế nào xấu có thể phân bổ và lặp qua 100 ints? Vào năm 1950, nó có thể không được xã hội chấp nhận, nhưng ...
cseder

@RyanTheLeach Tôi không trách bạn ... Tôi đã cập nhật câu trả lời của mình, vì vậy, nếu bạn biết về một giải pháp thậm chí còn hiệu quả hơn, xin hãy giải thích!
cseder

1
Tôi đã xóa bình luận của tôi khi họ không còn đứng. Cảm ơn đã sửa chữa, có vẻ như ok.
Ryan The Leach

1

Một sự pha trộn mới trong một khuynh hướng cũ:

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

3
Thực tế có bốn trường hợp, bao gồm / bao gồm, bao gồm / độc quyền, độc quyền / bao gồm và độc quyền / độc quyền.
William T. Mallard

1

Trong C, nếu hiệu quả thời gian là rất quan trọng và tràn số nguyên sẽ bao trùm, người ta có thể làm if ((unsigned)(value-min) <= (max-min)) .... Nếu 'max' và 'min' là các biến độc lập, phép trừ thêm cho (max-min) sẽ lãng phí thời gian, nhưng nếu biểu thức đó có thể được tính toán trước vào thời gian biên dịch hoặc nếu nó có thể được tính một lần vào thời gian chạy để kiểm tra nhiều lần các số so với cùng một phạm vi, biểu thức trên có thể được tính toán hiệu quả ngay cả trong trường hợp giá trị nằm trong phạm vi (nếu một phần lớn các giá trị sẽ nằm dưới phạm vi hợp lệ, có thể sử dụng nhanh hơn if ((value >= min) && (value <= max)) ...vì nó sẽ thoát sớm nếu giá trị nhỏ hơn phút).

Tuy nhiên, trước khi sử dụng một triển khai như vậy, điểm chuẩn của máy mục tiêu của một người. Trên một số bộ xử lý, biểu thức hai phần có thể nhanh hơn trong mọi trường hợp do hai phép so sánh có thể được thực hiện độc lập trong khi trong phương pháp trừ và so sánh, phép trừ phải hoàn thành trước khi phép so sánh có thể thực hiện.


1

Còn những thứ như thế này thì sao?

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

với phương pháp mở rộng như sau (đã thử nghiệm):

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

Tôi sẽ làm một đối tượng Range, đại loại như thế này:

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

Sau đó, bạn sử dụng nó theo cách này:

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

Bằng cách đó bạn có thể tái sử dụng nó cho loại khác.


RangeĐối tượng của bạn cần sử dụng CompareTophương thức để so sánh các mục, không phải <toán tử.
Phục vụ

Bạn đã đúng, mặc dù nếu triển khai IComparable, bạn cũng nên ghi đè các toán tử (ít nhất đó là những gì phân tích mã VS của tôi đang nói), có nghĩa là <sẽ hoạt động. Mặc dù tôi có thể sai nhưng tôi không có nhiều kinh nghiệm và đây là câu trả lời đầu tiên của tôi trên SO
IEatBagels

Không, trình biên dịch của bạn sẽ không nói rằng điều này hoạt động. Điều này sẽ không được biên dịch. Nó là hoàn toàn hợp lý cho một đối tượng để thực hiện IComparablevà không quá tải <toán tử.
Phục vụ

1

Khi kiểm tra xem "Số" có nằm trong phạm vi không, bạn phải rõ ý của bạn là gì và hai số có nghĩa là gì? Nói chung, bạn nên bọc tất cả các số dấu phẩy động trong cái gọi là 'bóng epsilon', điều này được thực hiện bằng cách chọn một số giá trị nhỏ và nói nếu hai giá trị này gần nhau thì chúng là cùng một thứ.

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

Với hai người trợ giúp này và giả định rằng nếu có bất kỳ số nào có thể được gọi là gấp đôi mà không có độ chính xác cần thiết. Tất cả những gì bạn cần bây giờ là một enum và một phương thức khác

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

Phương pháp khác như sau:

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

Bây giờ điều này có thể nhiều hơn nhiều so với những gì bạn muốn, nhưng nó giúp bạn không phải làm việc suốt thời gian và cố gắng nhớ nếu một giá trị đã được làm tròn và đến nơi nào. Nếu bạn cần, bạn có thể dễ dàng mở rộng nó để hoạt động với bất kỳ epsilon nào và cho phép epsilon của bạn thay đổi.


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

Sử dụng

số képToBeChecked = 7;

var result = numberToBeChecked.IsB between (100.122);

kết quả var = 5.IsB between (100.120);

kết quả var = 8.0.IsB between (1.2.9.6);


1

Nếu bạn quan tâm đến nhận xét của @Daap về câu trả lời được chấp nhận và chỉ có thể chuyển giá trị một lần, bạn có thể thử một trong các cách sau

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

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

hoặc là

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

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

1

Về sự thanh lịch, điều gần gũi nhất với ký hiệu toán học ( a <= x <= b ) cải thiện một chút khả năng đọc:

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

Để minh họa thêm:

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

0

Tôi đang tìm kiếm một cách thanh lịch để làm điều đó trong đó giới hạn có thể được chuyển đổi (nghĩa là không chắc chắn các giá trị được đặt theo thứ tự nào).

Điều này sẽ chỉ hoạt động trên các phiên bản mới hơn của C # trong đó tồn tại ?:

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

Rõ ràng bạn có thể thay đổi các dấu = trong đó cho mục đích của bạn. Có thể nhận được ưa thích với loại đúc quá. Tôi chỉ cần một sự trở lại nổi trong giới hạn (hoặc bằng)


0

Thanh lịch bởi vì nó không yêu cầu bạn xác định giá trị nào trong hai giá trị biên trước lớn hơn. Nó cũng không chứa chi nhánh.

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

& + | là các toán tử bitwise
nelsontruran

0

Tôi không biết nhưng tôi sử dụng phương pháp này:

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

Và đây là cách tôi có thể sử dụng nó:

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

Vui lòng cung cấp một ví dụ về việc sử dụng bên dưới khối mã, điều này sẽ giúp OP biết liệu nó có phù hợp với mục đích của anh ấy không
Gabriel Balsa Cantú

0

Đây là một số phương pháp mở rộng có thể giúp

  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

Nếu đó là để xác thực các tham số phương thức, không có giải pháp nào ném ArgumentOutOfRangeException và cho phép cấu hình dễ dàng / phù hợp các giá trị tối thiểu / tối thiểu bao gồm / độc quyền.

Sử dụng như thế này

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

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

Tôi chỉ viết những chức năng đẹp này. Nó cũng có lợi thế là không có phân nhánh (một nếu) cho các giá trị hợp lệ. Phần khó nhất là tạo các thông điệp ngoại lệ thích hợp.

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

Bạn đang tìm kiếm in [1..100]? Đó chỉ là Pascal.


2
Không đúng, nó không chỉ Pascal. Nhiều ngôn ngữ hiện đại có các tính năng như thế này. Trong Kotlin, ví dụ, nó được gọi là "Kết hợp mẫu". Ví dụ when (number) { in 0..9 -> println("1 digit") in 10..99 -> println("2 digits") in 100..999 -> println("3 digits") }
this.myself 13/03/19
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.