Bagaimana cara memeriksa secara elegan apakah suatu angka berada dalam suatu rentang?

157

Bagaimana saya bisa melakukan ini secara elegan dengan C # dan .NET 3.5 / 4?

Misalnya, angka bisa antara 1 dan 100.

Saya tahu sederhana jika cukup; tetapi kata kunci untuk pertanyaan ini adalah keanggunan. Ini untuk proyek mainan saya bukan untuk produksi.

Pertanyaan ini bukan tentang kecepatan, tetapi tentang keindahan kode. Berhentilah berbicara tentang efisiensi dan semacamnya; ingat Anda sedang mengabar ke paduan suara.

Sergio Tapia
sumber
23
Re: "Sunting" Anda - sederhana itu elegan . Saya pribadi menemukan pernyataan if lebih elegan daripada cara non-standar melakukan pemeriksaan ini ...
Reed Copsey
4
"Semuanya harus dibuat sesederhana mungkin, tetapi tidak sederhana." - Albert Einstein
corsiKa
3
@Sergio: Saya tidak merasa saya menjadi orang yang bertele-tele. Saya merasa bahwa orang sering menyalahgunakan metode ekstensi dan alat lain dalam bahasa untuk mengganti hal-hal yang sudah sederhana. Ada ratusan cara untuk membandingkan dua nilai int, tetapi menggunakan apa pun tetapi yang lebih jelas adalah pilihan yang buruk, IMO.
Reed Copsey
3
@Sergio: Saya kira, kalau begitu, saya tidak melihat inti dari pertanyaan;)
Reed Copsey
6
@ Sergio: jika ifbukan "barok" jangan memperbaikinya.
StriplingWarrior

Jawaban:

154

Ada banyak opsi:

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

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

Juga, lihat pos SO ini untuk opsi regex.

Dustin Laine
sumber
334
Enumerable.Range harus membuat enumerable of integer terlebih dahulu, dan kemudian mengulang setiap item untuk menemukannya. Itu ide dan kinerja yang buruk dibandingkan dengan memeriksa nilai secara drastis berbeda. Saya pikir kita harus mengadopsi moto, hanya karena Ekstensi LINQ itu keren, tidak berarti mereka harus digunakan untuk semuanya.
Matthew Abbott
14
@Matthew: stackoverflow.com/questions/777400/…
Adam Robinson
15
Saya setuju ini adalah ide yang buruk untuk kinerja, tetapi OP menginginkan sesuatu yang lebih mewah daripada ifpernyataan. Ini tentu saja mencapai itu ...;)
Tim Coker
10
Perlu dicatat bahwa parameter kedua bukan "berhenti", tetapi "hitung". Jadi misalnya, Enumerable.Range (150, 300) .Contains (400) akan mengembalikan true.
Shathur
5
Tolong jangan gunakan jawaban ini . Ini akan memiliki kinerja yang mengerikan jika rentang Anda cukup besar. Silakan lihat jawabannya oleh @ olivier-jacot-descombes
Aaron Hudon
95

Maksudmu?

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

atau

bool TestRange (int numberToCheck, int bottom, int top)
{
  return (numberToCheck >= bottom && numberToCheck <= top);
}
kemiller2002
sumber
1
Anda tidak perlu "ada" di sana ... Ini tidak akan dikompilasi. (Kalau tidak, saya setuju 100%)
Reed Copsey
4
@Ben, tunggu saja sampai saya mencoba dan mematenkannya juga :)
kemiller2002
Saya pikir ini adalah solusi yang paling solid tetapi tidak elegan yang dicari si penanya, bukan?
Kevin Simple
Satu-satunya hal yang akan saya ubah adalah menambahkan kata kunci statis ke metode. ;-)
Robert S.
Membutuhkan flag batas, yaitu InRange (angka, lowerBound, LOWER_IS_INCLUSIVE, Upperbound, UPPER_IS_EXCLUSIVE) untuk memungkinkan <vs <=. Saya menulis ini bermaksud menjadi snarky tetapi sekarang saya berpikir tentang hal itu bendera benar-benar akan mendorong penelepon untuk mendapatkan spesifikasi mereka langsung.
William T. Mallard
56

Untuk menambah derau di sini, Anda dapat membuat metode ekstensi:

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

Yang akan membiarkan Anda melakukan sesuatu seperti ...

int val = 15;

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

Yang sedang berkata, ini sepertinya hal yang konyol untuk dilakukan ketika cek itu sendiri hanya satu baris.

Adam Robinson
sumber
1
@ Ben: Saya membahas masalah ini, yang mengatakan "dalam kisaran" (yang menurut saya tidak ambigu dalam hal itu), tetapi Anda benar bahwa badan pertanyaan mengatakan "antara 1 dan 100" (yang merupakan , tentu saja, ambigu).
Adam Robinson
48

Seperti yang dikatakan orang lain, gunakan if sederhana.

Anda harus memikirkan pemesanan.

misalnya

1 <= x && x <= 100

lebih mudah dibaca daripada

x >= 1 && x <= 100
Esben Skov Pedersen
sumber
19
"Lebih mudah" ada di mata yang melihatnya. Saya pribadi lebih suka memiliki variabel yang dipermasalahkan di sebelah kiri dan konstanta atau variabel tidak dipertanyakan di sebelah kanan.
Adam Robinson
15
Dalam Perl 6 , Anda akan menulis 1 <= x <= 100.
Jordão
2
Urutan nomor baris pada mulanya paling jelas - tetapi Anda dapat melatih mata / pikiran Anda untuk pesanan lainnya. Secara khusus - Saya suka trik menempatkan konstanta di sebelah kiri, selalu. Jika Anda melakukannya, kompiler akan memberi tahu Anda saat Anda mengetik =alih-alih ==. Ini tidak membantu operator relasional non-kesetaraan - tetapi mudah digunakan untuk menggunakannya secara konsisten.
davidbak
1
Saya hanya ingin menambahkan bahwa solusi ini tidak berguna dalam hal apa pun. Pertimbangkan xadalah pemanggilan fungsi yang kompleks atau ekspresi Linq yang menghabiskan waktu. Dalam hal ini Anda akan melakukan ini dua kali yang bukan hal yang baik. Tentu Anda harus menyimpan nilai ke dalam variabel lokal sementara tetapi ada beberapa kasus (misalnya di lain-jika-pernyataan) di mana Anda hanya ingin memanggil fungsi setelah yang lain jika atau jika-jika gagal. Dengan variabel sementara Anda harus tetap memanggilnya sebelumnya. Metode ekstensi (disebutkan dalam jawaban lain) adalah solusi imho terbaik dalam kasus tersebut.
Robert S.
4
Saya suka urutan nomor juga, dan juga untuk tes pelengkap, misalnya x <10 || 20 <x. Bagi saya itu berteriak "x ada di luar kisaran 10 - 20".
William T. Mallard
44

Dalam kode produksi saya hanya akan menulis

1 <= x && x <= 100

Ini mudah dimengerti dan sangat mudah dibaca.


Berikut ini adalah metode pintar yang mengurangi jumlah perbandingan dari dua menjadi satu dengan menggunakan beberapa matematika. Idenya adalah bahwa salah satu dari dua faktor menjadi negatif jika angka tersebut berada di luar rentang dan nol jika jumlahnya sama dengan salah satu batas:

Jika batasannya inklusif:

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

atau

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

Jika batasannya eksklusif:

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

atau

(x - min) * (max - x) > 0
Olivier Jacot-Descombes
sumber
3
Menurut standar saya, ini adalah solusi yang paling elegan, yang menarik adalah bagi saya itu juga tampaknya berjalan lebih cepat daripada memeriksa kedua ekspresi, yang mengatakan itu juga tampaknya lebih tidak konsisten (kecepatan tampaknya lebih bervariasi) akan menarik untuk dilihat jika ada penelitian yang dilakukan yang mana yang lebih cepat.
Thomas Lindvall
3
Menguji solusi Anda pada javascript dan akurat dengan angka floating-point hingga 14 desimal. Ini cuplikan kode yang sangat bagus. Itu akan membuatmu
marah
4
Meskipun, ada masalah kecil jika angka positif yang besar terlibat, itu bisa meluap! XD Anda mungkin ingin mengingatnya saat menulis kode Anda.
BrainStorm.exe
2
Pertanyaan itu meminta keanggunan dan karena itu lebih bersifat akademis daripada nilai praktis. Secara pribadi saya hanya akan menggunakan 1 < x && x < 100kode produktif yang sederhana . Lebih mudah dimengerti.
Olivier Jacot-Descombes
1
Bagi mereka yang peduli tentang kinerja, 1 < x & x < 100(tidak ada && hubung singkat) menginstruksikan kompiler bahwa ia selalu dapat mengevaluasi x < 100terlepas dari hasil 1 < x. Anehnya (karena prediksi cabang) lebih cepat untuk selalu melakukan operasi sederhana ini daripada kadang-kadang melewatkannya.
Tom Leys
23

Saya mengusulkan ini:

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

Contoh:

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

dan tentu saja dengan variabel:

myvalue.IsWithin(min, max)

Mudah dibaca (dekat dengan bahasa manusia) dan berfungsi dengan jenis apa pun yang sebanding (bilangan bulat, ganda, jenis khusus ...).

Memiliki kode yang mudah dibaca adalah penting karena pengembang tidak akan menyia-nyiakan "siklus otak" untuk memahaminya. Dalam sesi pengkodean yang panjang, siklus otak yang terbuang membuat pengembang lelah lebih awal dan rentan terhadap bug.

Anton M
sumber
3
saya akan lebih menyederhanakan dengan menggunakan kata di antara, dan memiliki bendera boolean untuk menentukan apakah inklusif atau tidak
Ben
Baik. Mudah dimengerti. Saya mengubah nama 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 akan berfungsi tetapi saya tidak suka kondisi negatif
Paulustrious
21

Dengan sedikit penyalahgunaan metode ekstensi, kita bisa mendapatkan solusi "elegan" berikut:

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!");
        }
    }
}
Ferruccio
sumber
Suka solusinya! Btw untuk mendukung inklusif, membuat enum Inclusivedengan nilai-nilai: Lower, Upper, All. Dan lulus untuk Infungsi satu parameter tambahan jenis enum Inclusivedengan nilai default Inclusive.All, memperbarui Tofungsi tubuh untuk menangani All, Lower, Uppernilai-nilai :)
Nikita
7

Jika ini insidentil, sederhanalah ifyang Anda butuhkan. Jika ini terjadi di banyak tempat, Anda mungkin ingin mempertimbangkan keduanya:

  • PostSharp . Hiasi metode dengan atribut yang 'menyuntikkan' kode ke dalam metode setelah kompilasi. Saya tidak tahu pasti, tapi saya bisa membayangkan itu bisa digunakan untuk ini.

Sesuatu seperti:

[Between("parameter", 0, 100)]
public void Foo(int parameter)
{
}
  • Kontrak kode . Memiliki keuntungan bahwa kendala dapat diperiksa pada waktu kompilasi, dengan verifikasi statis kode Anda dan tempat-tempat yang menggunakan kode Anda.
JulianR
sumber
+1 untuk kontrak kode; ini khusus untuk memvalidasi parameter, tetapi ini adalah kasus penggunaan yang sering dan verifikasi statis memiliki potensi untuk menjadi sangat berguna.
Dan Bryant
5
if (value > 1 && value < 100)
{
    // do work
}
else
{
    // handle outside of range logic
}
Nick Larsen
sumber
5

Menggunakan &&ekspresi untuk bergabung dengan dua perbandingan hanyalah cara paling elegan untuk melakukan ini. Jika Anda mencoba menggunakan metode ekstensi mewah dan semacamnya, Anda mengalami pertanyaan apakah akan menyertakan batas atas, batas bawah, atau keduanya. Setelah Anda mulai menambahkan variabel tambahan atau mengubah nama ekstensi untuk menunjukkan apa yang disertakan, kode Anda menjadi lebih lama dan lebih sulit dibaca (untuk sebagian besar programmer). Selain itu, alat seperti Resharper akan memperingatkan Anda jika perbandingan Anda tidak masuk akal ( number > 100 && number < 1), yang tidak akan mereka lakukan jika Anda menggunakan metode ('i.IsBetween (100, 1)').

Satu-satunya komentar lain yang akan saya buat adalah bahwa jika Anda memeriksa input dengan maksud untuk melemparkan pengecualian, Anda harus mempertimbangkan untuk menggunakan kontrak kode:

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

Ini lebih elegan daripada if(...) throw new Exception(...), dan Anda bahkan bisa mendapatkan peringatan waktu kompilasi jika seseorang mencoba memanggil metode Anda tanpa memastikan bahwa nomor tersebut berada dalam batas pertama.

StriplingWarrior
sumber
2
FYI, penganalisa statis kontrak lebih bahagia ketika batasan batas bawah dan batas atas dibagi menjadi pernyataan Membutuhkan terpisah.
Dan Bryant
Terima kasih Dan Bryant, itulah tepatnya yang saya cari di sini. Tidak dapat menemukan banyak bahan saran tentang gaya kondisi untuk Membutuhkan dan metode Kontrak Kode terkait lainnya.
jpierson
2

Jika Anda ingin menulis lebih banyak kode daripada kode sederhana, mungkin Anda dapat: Membuat Metode Ekstensi yang disebut 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());

Tambahan:perlu dicatat bahwa dalam praktiknya Anda sangat jarang "hanya memeriksa kesetaraan" (atau <,>) dalam basis kode. (Selain dari dalam situasi yang paling sepele.) Murni sebagai contoh, setiap programmer game akan menggunakan kategori seperti berikut ini di setiap proyek, sebagai masalah dasar. Perhatikan bahwa dalam contoh ini (kebetulan) menggunakan fungsi (Mathf. Mungkin) yang dibangun di lingkungan itu; dalam praktiknya Anda biasanya harus dengan hati-hati mengembangkan konsep Anda sendiri tentang apa artinya perbandingan untuk representasi komputer dari bilangan real, untuk jenis situasi yang Anda rekayasa. (Bahkan tidak menyebutkan bahwa jika Anda melakukan sesuatu seperti, mungkin pengontrol, pengontrol PID atau sejenisnya, seluruh masalah menjadi pusat dan sangat sulit, itu menjadi sifat proyek.

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;
    }
Tony
sumber
1
Ganti if and else denganreturn (value >= Min && value <= Max);
AeroX
cara elegan untuk menulis perbandingan adalah "dalam urutan logis ..." if (Min <= value && value <= Max). Itu jauh lebih cantik.
Fattie
2
Lebih lanjut tentang pertanyaan ini, sangat mengejutkan bahwa tidak ada yang menyebutkan masalah utama dalam proyek dunia nyata (terutama jika Anda seorang insinyur game) adalah bahwa Anda harus berurusan dengan masalah perkiraan . Dalam setiap perangkat lunak dunia nyata Anda pada dasarnya tidak pernah "hanya melakukan perbandingan" (apakah kesetaraan atau <,>) Anda harus mempertimbangkan dan menangani masalah kesalahan, tergantung pada situasi yang dihadapi. Saya telah mengedit dalam lampiran untuk jawaban ini (satu-satunya jawaban yang benar di sini!) Karena tidak ada lagi jawaban yang diizinkan.
Fattie
Terima kasih atas pengamatan dan adendum ini.
Tony
2

Karena semua jawaban lain tidak ditemukan oleh saya, di sini hanya implementasi saya:

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

Anda kemudian dapat menggunakannya seperti ini:

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

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

EDIT: Jawaban Baru disediakan. Saya baru mulai menggunakan C # ketika saya menulis jawaban pertama untuk pertanyaan ini, dan di belakang saya sekarang menyadari bahwa "solusi" saya adalah naif dan tidak efisien.

Jawaban asli saya: Saya akan menggunakan versi yang lebih sederhana:

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

A Better Way

Karena saya belum melihat solusi lain yang lebih efisien (menurut tes saya setidaknya), saya akan mencobanya lagi.

Cara baru dan lebih baik yang juga berfungsi dengan rentang negatif :

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

Ini dapat digunakan dengan rentang positif dan negatif dan default ke kisaran

1..100 (inklusif) dan digunakan xsebagai nomor untuk memeriksa diikuti oleh rentang opsional yang ditentukan oleh mindan max.

Menambahkan Contoh Untuk Ukuran Yang Baik

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

Pengembalian:

True
True
True
False
True

Contoh 2: Menggunakan daftar int acak antara 1 dan 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);

Pengembalian:

66660 ints found in range 1..100

Waktu Eksekusi: 0,016 detik

cseder
sumber
Yeay, saya mengomentari komentar untuk jawaban saya dari 2013 :) @RyanTheLeach: Bagaimana jawaban saya untuk pertanyaan ini berbeda dari jawaban yang sekarang “diterima”? Saya menyadari bahwa itu bukan traversal yang paling efektif, tetapi "mengerikan"? Seberapa buruk pengalokasian dan pengulangan melalui 100 int? Pada tahun 1950 itu mungkin tidak diterima secara sosial, tetapi ...
cseder
@RyanTheLeach Saya tidak menyalahkan Anda ... Saya telah memperbarui jawaban saya, jadi, jika Anda tahu tentang solusi yang bahkan lebih efisien, tolong jelaskan!
cseder
1
Saya telah menghapus komentar saya karena tidak lagi berlaku. Terima kasih atas perbaikannya, sepertinya baik-baik saja.
Ryan The Leach
1

Sentuhan baru pada favorit lama:

public bool IsWithinRange(int number, int topOfRange, int bottomOfRange, bool includeBoundaries) {
    if (includeBoundaries)
        return number <= topOfRange && number >= bottomOfRange;
    return number < topOfRange && number > bottomOfRange;
}
Ben Hoffstein
sumber
3
Sebenarnya ada empat kasus, inklusif / inklusif, inklusif / eksklusif, eksklusif / inklusif dan eksklusif / eksklusif.
William T. Mallard
1

Dalam C, jika efisiensi waktu sangat penting dan overflow bilangan bulat akan membungkus, orang bisa melakukannya if ((unsigned)(value-min) <= (max-min)) .... Jika 'max' dan 'min' adalah variabel independen, pengurangan ekstra untuk (max-min) akan membuang waktu, tetapi jika ekspresi itu dapat dikomputasi pada waktu kompilasi, atau jika itu dapat dihitung sekali pada saat run-time untuk menguji banyak angka terhadap rentang yang sama, ekspresi di atas dapat dihitung secara efisien bahkan dalam kasus di mana nilai berada dalam kisaran (jika sebagian besar nilai akan berada di bawah rentang yang valid, mungkin lebih cepat digunakan if ((value >= min) && (value <= max)) ...karena akan keluar lebih awal jika nilai kurang dari min).

Namun, sebelum menggunakan implementasi seperti itu, patok mesin target seseorang. Pada beberapa prosesor, ekspresi dua bagian mungkin lebih cepat dalam semua kasus karena dua perbandingan dapat dilakukan secara independen sedangkan dalam metode kurangi dan bandingkan pengurangan harus diselesaikan sebelum perbandingan dapat dieksekusi.

supercat
sumber
1

Bagaimana dengan sesuatu yang seperti ini?

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

dengan metode ekstensi sebagai berikut (diuji):

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;
    }
}
William T. Mallard
sumber
1

Saya akan melakukan objek Range, sesuatu seperti ini:

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

Maka Anda menggunakannya dengan cara ini:

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

Dengan begitu Anda dapat menggunakannya kembali untuk jenis lain.

IEatBagels
sumber
RangeObjek Anda perlu menggunakan CompareTometode untuk membandingkan item, bukan <operator.
Servy
Anda benar, meskipun jika menerapkan IComparable Anda juga harus mengesampingkan operator (setidaknya itulah yang dikatakan analisis kode VS saya), artinya <akan bekerja. Walaupun saya mungkin salah, saya tidak punya banyak pengalaman dan ini adalah jawaban pertama saya pada SO
IEatBagels
Tidak, kompiler Anda tidak akan mengatakan bahwa ini berfungsi. Ini tidak akan dikompilasi. Sangat masuk akal jika suatu objek menerapkan IComparabledan tidak membebani <operator.
Servy
1

Saat memeriksa apakah "Angka" dalam kisaran Anda harus jelas dalam apa yang Anda maksud, dan apa artinya dua angka sama? Secara umum Anda harus membungkus semua angka floating point dalam apa yang disebut 'bola epsilon' ini dilakukan dengan memilih beberapa nilai kecil dan mengatakan jika dua nilai sedekat ini mereka adalah hal yang sama.

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

Dengan dua pembantu ini di tempat dan dengan asumsi bahwa jika nomor apa pun dapat dilemparkan sebagai ganda tanpa akurasi yang diperlukan. Yang Anda butuhkan sekarang adalah enum dan metode lain

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

Metode lain berikut:

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

Sekarang ini mungkin jauh lebih dari apa yang Anda inginkan, tetapi itu membuat Anda tidak berurusan dengan pembulatan sepanjang waktu dan mencoba mengingat jika suatu nilai telah dibulatkan dan ke tempat apa. Jika perlu, Anda dapat dengan mudah memperpanjang ini untuk bekerja dengan epsilon apa pun dan untuk memungkinkan epsilon Anda berubah.

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

Pemakaian

double numberToBeChecked = 7;

var result = numberToBeChecked.IsBetween (100.122);

hasil var = 5.IsBetween (100.120);

var result = 8.0.IsBetween (1.2,9.6);

İBRAHİM GAZALOĞLU
sumber
1

Jika Anda khawatir dengan komentar oleh @Daap pada jawaban yang diterima dan hanya dapat memberikan nilai satu kali, Anda dapat mencoba salah satu dari yang berikut ini

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

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

atau

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

//var t = TestRangeMargin(10, somelist.Count(), 5);
Hugo Delsing
sumber
1

Mengenai keanggunan, hal terdekat dengan notasi matematika ( a <= x <= b ) sedikit meningkatkan keterbacaan:

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

Untuk ilustrasi lebih lanjut:

public static bool IsOutside(this int value, int min, int max)
{
    return value < min || max < value;
}
hector-j-rivas
sumber
0

Saya sedang mencari cara yang elegan untuk melakukannya di mana batas mungkin diaktifkan (mis. Tidak yakin urutan nilai-nilai yang ada di).

Ini hanya akan berfungsi pada versi C # yang lebih baru di mana?: Ada

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

Jelas Anda bisa mengubah tanda = di sana untuk tujuan Anda. Bisa juga menyukai tipe casting. Saya hanya perlu pengembalian mengambang dalam batas (atau sama dengan)

Kalikovision
sumber
0

Elegan karena tidak mengharuskan Anda menentukan mana dari dua nilai batas yang lebih dulu. Ini juga tidak mengandung cabang.

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 );
}
Tom Leys
sumber
& + | adalah operator bitwise
nelsontruran
0

Saya tidak tahu tetapi saya menggunakan metode ini:

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

Dan inilah cara saya dapat menggunakannya:

    [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);
    }
pengguna8790965
sumber
Tolong berikan contoh penggunaan di bawah blok kode, ini akan membantu OP tahu apakah itu sesuai dengan tujuannya
Gabriel Balsa Cantú
0

Ini adalah beberapa metode Ekstensi yang dapat membantu

  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;
    }
hanan
sumber
0

Jika itu untuk memvalidasi parameter metode, tidak ada solusi yang membuang ArgumentOutOfRangeException dan memungkinkan konfigurasi yang mudah / tepat untuk nilai inklusif / eksklusif min / maks.

Gunakan seperti ini

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

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

Saya baru saja menulis fungsi-fungsi yang indah ini. Ini juga memiliki keuntungan karena tidak memiliki percabangan (jika tunggal) untuk nilai yang valid. Bagian tersulit adalah menyusun pesan pengecualian yang tepat.

/// <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);
Etienne Charland
sumber
-2

Anda mencari in [1..100]? Itu hanya Pascal.

Polluks
sumber
2
Tidak benar, ini bukan hanya Pascal. Banyak bahasa modern memiliki fitur seperti ini. Di Kotlin, misalnya disebut "Pencocokan Pola". Contoh when (number) { in 0..9 -> println("1 digit") in 10..99 -> println("2 digits") in 100..999 -> println("3 digits") }
this.myself