Pengecualian vs hasil kosong ditetapkan saat input secara teknis valid, tetapi tidak memuaskan

108

Saya sedang mengembangkan perpustakaan yang ditujukan untuk rilis publik. Ini berisi berbagai metode untuk beroperasi pada set objek - menghasilkan, memeriksa, mempartisi dan memproyeksikan set ke dalam bentuk baru. Dalam hal ini relevan, ini adalah perpustakaan kelas C # yang berisi ekstensi gaya LINQ aktif IEnumerable, yang akan dirilis sebagai paket NuGet.

Beberapa metode di pustaka ini dapat diberikan parameter input yang tidak memuaskan. Sebagai contoh, dalam metode kombinatorik, ada metode untuk menghasilkan semua set n item yang dapat dibangun dari set sumber item m . Misalnya, diberikan set:

1, 2, 3, 4, 5

dan meminta kombinasi 2 akan menghasilkan:

1, 2
1, 3
1, 4
dll ...
5, 3
5, 4

Sekarang, sangat mungkin untuk meminta sesuatu yang tidak dapat dilakukan, seperti memberikannya satu set 3 item dan kemudian meminta kombinasi 4 item sambil mengatur opsi yang mengatakan itu hanya dapat menggunakan setiap item sekali.

Dalam skenario ini, setiap parameter valid secara individual :

  • Pengumpulan sumber tidak nol, dan memang mengandung item
  • Ukuran kombinasi yang diminta adalah bilangan nol bukan positif
  • Mode yang diminta (gunakan setiap item hanya sekali) adalah pilihan yang valid

Namun, keadaan parameter saat diambil bersama-sama menyebabkan masalah.

Dalam skenario ini, apakah Anda mengharapkan metode untuk melempar pengecualian (mis. InvalidOperationException), Atau mengembalikan koleksi kosong? Tampaknya valid bagi saya:

  • Anda tidak dapat menghasilkan kombinasi n item dari set item m di mana n> m jika Anda hanya diperbolehkan menggunakan setiap item sekali, sehingga operasi ini dapat dianggap mustahil, karenanya InvalidOperationException.
  • Himpunan kombinasi ukuran n yang dapat diproduksi dari item m ketika n> m adalah himpunan kosong; tidak ada kombinasi yang dapat diproduksi.

Argumen untuk set kosong

Kekhawatiran pertama saya adalah bahwa pengecualian mencegah chaining metode LINQ gaya idiomatik ketika Anda berurusan dengan dataset yang mungkin memiliki ukuran yang tidak diketahui. Dengan kata lain, Anda mungkin ingin melakukan sesuatu seperti ini:

var result = someInputSet
    .CombinationsOf(4, CombinationsGenerationMode.Distinct)
    .Select(combo => /* do some operation to a combination */)
    .ToList();

Jika set input Anda berukuran variabel, perilaku kode ini tidak dapat diprediksi. Jika .CombinationsOf()melempar pengecualian ketika someInputSetmemiliki kurang dari 4 elemen, maka kode ini kadang - kadang akan gagal saat runtime tanpa beberapa pemeriksaan awal. Dalam contoh di atas pengecekan ini sepele, tetapi jika Anda menyebutnya setengah dari rantai yang lebih panjang dari LINQ maka ini mungkin membosankan. Jika mengembalikan set kosong, maka resultakan kosong, yang mungkin Anda senangi.

Argumen untuk pengecualian

Kekhawatiran kedua saya adalah bahwa mengembalikan set kosong mungkin menyembunyikan masalah - jika Anda memanggil metode ini di tengah-tengah rantai LINQ dan secara diam-diam mengembalikan set kosong, maka Anda dapat mengalami masalah beberapa langkah kemudian, atau menemukan diri Anda dengan yang kosong set hasil, dan mungkin tidak jelas bagaimana itu terjadi mengingat Anda pasti memiliki sesuatu di set input.

Apa yang Anda harapkan, dan apa argumen Anda untuk itu?

anaximander
sumber
66
Karena set kosong secara matematis benar, kemungkinannya adalah ketika Anda mendapatkannya sebenarnya adalah apa yang Anda inginkan. Definisi dan konvensi matematika pada umumnya dipilih untuk konsistensi dan kenyamanan sehingga semuanya berjalan sesuai dengannya.
penanggung jawab
5
@asmeurer Mereka dipilih agar teorema konsisten dan nyaman. Mereka tidak dipilih untuk mempermudah pemrograman. (Itu kadang-kadang menguntungkan sisi, tetapi kadang-kadang mereka membuat pemrograman lebih sulit juga.)
jpmc26
7
@ jpmc26 "Mereka dipilih agar teorema konsisten dan nyaman" - memastikan bahwa program Anda selalu berfungsi seperti yang diharapkan pada dasarnya setara dengan membuktikan teorema.
artem
2
@ jpmc26 Saya tidak mengerti mengapa Anda menyebutkan pemrograman fungsional. Membuktikan kebenaran untuk program imperatif sangat mungkin, selalu menguntungkan, dan dapat dilakukan secara informal juga - hanya berpikir sedikit dan menggunakan akal sehat matematika ketika Anda menulis program Anda, dan Anda akan menghabiskan lebih sedikit waktu pengujian. Secara statistik terbukti pada sampel satu ;-)
artem
5
@DmitryGrigoryev 1/0 sebagai tidak terdefinisi jauh lebih benar secara matematis daripada 1/0 sebagai tak terhingga.
penanggung jawab

Jawaban:

145

Kembalikan Set Kosong

Saya mengharapkan set kosong karena:

Ada 0 kombinasi 4 angka dari himpunan 3 ketika saya hanya dapat menggunakan setiap angka satu kali

Ewan
sumber
5
Secara matematis, ya, tapi itu juga sangat mungkin menjadi sumber kesalahan. Jika jenis input ini diharapkan, mungkin lebih baik untuk mengharuskan pengguna menangkap pengecualian di perpustakaan tujuan umum.
Casey Kuball
56
Saya tidak setuju bahwa itu adalah "kemungkinan besar" penyebab kesalahan. Misalkan, misalnya, Anda menerapkan algoritme "matchmaknig" yang naif dari sekumpulan input largish. Anda dapat meminta semua kombinasi dari dua item, menemukan kecocokan "terbaik" di antara mereka, dan kemudian menghapus kedua elemen dan memulai kembali dengan set baru yang lebih kecil. Akhirnya set Anda akan kosong, dan tidak akan ada pasangan yang tersisa untuk dipertimbangkan: pengecualian pada titik ini adalah obstruktif. Saya pikir ada banyak cara untuk berakhir dalam situasi yang sama.
amalloy
11
Sebenarnya Anda tidak tahu apakah ini merupakan kesalahan di mata pengguna. Set kosong adalah sesuatu yang harus diperiksa pengguna, jika perlu. if (result.Any ()) DoSomething (result.First ()); lain DoSomethingElse (); jauh lebih baik daripada mencoba {result.first (). dosomething ();} catch {DoSomethingElse ();}
Guran
4
@ TripeHound Itulah yang membuat keputusan pada akhirnya: mengharuskan pengembang menggunakan metode ini untuk memeriksa, dan kemudian melempar, memiliki dampak yang jauh lebih kecil daripada melemparkan pengecualian yang tidak mereka inginkan, dalam hal upaya pengembangan, kinerja program, dan kesederhanaan alur program.
anaximander
6
@Darthfett Cobalah membandingkan ini dengan metode ekstensi LINQ yang ada: Where (). Jika saya memiliki klausa: Di mana (x => 1 == 2) saya tidak mendapatkan pengecualian, saya mendapatkan set kosong.
Necoras
79

Jika ragu, tanyakan orang lain.

Fungsi contoh Anda memiliki salah satu yang sangat mirip dengan Python: itertools.combinations. Mari kita lihat cara kerjanya:

>>> import itertools
>>> input = [1, 2, 3, 4, 5]
>>> list(itertools.combinations(input, 2))
[(1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]
>>> list(itertools.combinations(input, 5))
[(1, 2, 3, 4, 5)]
>>> list(itertools.combinations(input, 6))
[]

Dan itu terasa sangat baik bagi saya. Saya mengharapkan hasil yang saya bisa beralih dan saya mendapatkannya.

Tapi, jelas, jika Anda bertanya sesuatu yang bodoh:

>>> list(itertools.combinations(input, -1))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: r must be non-negative

Jadi saya akan mengatakan, jika semua parameter Anda memvalidasi tetapi hasilnya adalah set kosong mengembalikan set kosong, Anda bukan satu-satunya yang melakukannya.


Seperti yang dikatakan oleh @Bakuriu di komentar, ini juga sama untuk SQLpermintaan seperti SELECT <columns> FROM <table> WHERE <conditions>. Selama <columns>, <table>, <conditions>baik terbentuk terbentuk dan mengacu pada nama-nama yang ada, Anda dapat membangun serangkaian kondisi yang mengecualikan satu sama lain. Kueri yang dihasilkan hanya akan menghasilkan tanpa baris alih-alih melemparkan InvalidConditionsError.

Mathias Ettinger
sumber
4
Diturunkan karena tidak idiomatis dalam ruang bahasa C # / Linq (lihat jawaban sbecker untuk mengetahui bagaimana masalah yang sama di luar batas ditangani dalam bahasa tersebut). Jika ini adalah pertanyaan Python, itu akan menjadi +1 dari saya.
James Snell
32
@ JamesSnell Saya hampir tidak melihat bagaimana hubungannya dengan masalah di luar batas. Kami tidak memilih elemen berdasarkan indeks untuk menyusun ulang elemen, kami memilih nelemen dalam koleksi untuk menghitung jumlah cara kami dapat mengambilnya. Jika pada titik tertentu tidak ada elemen yang tersisa saat mengambilnya, maka tidak ada (0) cara memilih nelemen dari koleksi tersebut.
Mathias Ettinger
1
Namun, Python bukan ramalan yang baik untuk pertanyaan C #: misalnya C # akan mengizinkan 1.0 / 0, Python tidak mau.
Dmitry Grigoryev
2
@MathiasEttinger Pedoman Python untuk bagaimana dan kapan menggunakan pengecualian sangat berbeda dengan C #, jadi menggunakan perilaku dari modul Python sebagai pedoman bukanlah metrik yang baik untuk C #.
Ant P
2
Alih-alih memilih Python kita bisa memilih SQL. Apakah kueri yang terbentuk dengan baik diSELECT atas tabel menghasilkan pengecualian saat kumpulan hasil kosong? (spoiler: tidak!). "Well-formedness" dari kueri hanya bergantung pada kueri itu sendiri dan beberapa metadata (misalnya apakah tabel ada dan memiliki bidang ini (dengan tipe ini)?) Setelah kueri terbentuk dengan baik dan Anda menjalankannya, Anda mengharapkan salah satu (mungkin kosong) hasil yang valid atau pengecualian karena "server" memiliki masalah (mis. server db turun atau sesuatu).
Bakuriu
72

Dalam istilah awam:

  • Jika ada kesalahan, Anda harus mengajukan pengecualian. Itu mungkin melibatkan melakukan hal-hal dalam langkah-langkah alih-alih dalam satu panggilan berantai untuk mengetahui persis di mana kesalahan terjadi.
  • Jika tidak ada kesalahan tapi set yang dihasilkan kosong, jangan naikkan pengecualian, kembalikan set kosong. Set kosong adalah set yang valid.
Tulains Córdova
sumber
23
+1, 0 adalah angka yang benar-benar valid, dan memang sangat penting. Ada begitu banyak kasus di mana kueri mungkin secara sah menghasilkan nol klik sehingga meningkatkan pengecualian akan sangat mengganggu.
Kilian Foth
19
saran yang bagus, tetapi apakah jawaban Anda jelas?
Ewan
54
Saya masih belum bijaksana jika jawaban ini menyarankan OP harus throwatau mengembalikan set kosong ...
James Snell
7
Jawaban Anda mengatakan saya bisa melakukan keduanya, tergantung pada apakah ada kesalahan, tetapi Anda tidak menyebutkan apakah Anda akan menganggap skenario contoh sebagai kesalahan. Dalam komentar di atas Anda mengatakan saya harus mengembalikan set kosong "jika tidak ada masalah", tetapi sekali lagi, kondisi yang tidak memuaskan dapat dianggap sebagai masalah, dan Anda melanjutkan dengan mengatakan bahwa saya tidak mengajukan pengecualian ketika seharusnya. Jadi yang harus saya lakukan?
anaximander
3
Ah, saya mengerti - Anda mungkin salah paham; Saya tidak merantai apa pun, saya menulis metode yang saya harapkan dapat digunakan dalam sebuah rantai. Saya bertanya-tanya apakah pengembang masa depan hipotetis yang menggunakan metode ini ingin itu melempar dalam skenario di atas.
anaximander
53

Saya setuju dengan jawaban Ewan tetapi ingin menambahkan alasan tertentu.

Anda berhadapan dengan operasi matematika, jadi mungkin saran yang bagus untuk tetap menggunakan definisi matematika yang sama. Dari sudut pandang matematika, jumlah r -set dari n -set (yaitu nCr ) didefinisikan dengan baik untuk semua r> n> = 0. Ini adalah nol. Oleh karena itu mengembalikan set kosong akan menjadi kasus yang diharapkan dari sudut pandang matematika.

sigy
sumber
9
Ini poin yang bagus. Jika perpustakaan berada pada level yang lebih tinggi, seperti memilih kombinasi warna untuk membuat palet - maka masuk akal untuk melempar kesalahan. Karena Anda tahu palet tanpa warna bukanlah palet. Tapi satu set tanpa entri masih set dan matematika tidak mendefinisikannya sama dengan set kosong.
Kapten Man
1
Jauh lebih baik daripada jawaban Ewan karena mengutip praktik matematika. +1
user949300
24

Saya menemukan cara yang baik untuk menentukan apakah akan menggunakan pengecualian, adalah membayangkan orang terlibat dalam transaksi.

Mengambil mengambil konten file sebagai contoh:

  1. Tolong ambilkan saya isi file, "not exist.txt"

    Sebuah. "Ini isinya: koleksi karakter kosong"

    b. "Erm, ada masalah, file itu tidak ada. Aku tidak tahu harus berbuat apa!"

  2. Tolong ambilkan saya isi file, "ada tetapi kosong.txt"

    Sebuah. "Ini isinya: koleksi karakter kosong"

    b. "Erm, ada masalah, tidak ada dalam file ini. Aku tidak tahu harus berbuat apa!"

Tidak diragukan lagi beberapa akan tidak setuju, tetapi bagi kebanyakan orang, "Erm, ada masalah" masuk akal ketika file tidak ada dan mengembalikan "koleksi karakter kosong" ketika file kosong.

Jadi terapkan pendekatan yang sama pada contoh Anda:

  1. Tolong beri saya semua kombinasi 4 item untuk {1, 2, 3}

    Sebuah. Tidak ada, ini satu set kosong.

    b. Ada masalah, saya tidak tahu harus berbuat apa.

Sekali lagi, "Ada masalah" akan masuk akal jika misalnya nullditawarkan sebagai set item, tetapi "ini set kosong" tampaknya respons yang masuk akal untuk permintaan di atas.

Jika mengembalikan nilai kosong menutupi masalah (misalnya file yang hilang, a null), maka pengecualian yang biasanya digunakan sebagai gantinya (kecuali bahasa yang Anda pilih mendukung option/maybejenis, maka terkadang hal itu lebih masuk akal). Kalau tidak, mengembalikan nilai kosong kemungkinan akan menyederhanakan biaya dan lebih sesuai dengan prinsip paling tidak heran.

David Arno
sumber
4
Nasihat ini bagus, tetapi tidak berlaku untuk kasus ini. Contoh yang lebih baik: Berapa hari setelah tanggal 30 Januari ?: 1 Berapa hari setelah tanggal 30: februari ?: 0 Berapa hari setelah tanggal 30: dibuat bulan Januari ?: Pengecualian Berapa jam kerja pada tanggal 30? : th february: Exception
Guran
9

Karena itu untuk perpustakaan tujuan umum, insting saya adalah Biarkan pengguna akhir memilih.

Sama seperti yang kita miliki Parse()dan TryParse()tersedia bagi kita, kita dapat memiliki opsi yang kita gunakan tergantung pada output apa yang kita butuhkan dari fungsi. Anda akan menghabiskan lebih sedikit waktu untuk menulis dan mengelola pembungkus fungsi untuk membuang pengecualian daripada berdebat memilih satu versi fungsi.

James Snell
sumber
3
+1 karena saya selalu menyukai gagasan pilihan dan karena pola ini memiliki kecenderungan untuk mendorong programmer untuk memvalidasi input mereka sendiri. Keberadaan fungsi TryFoo hanya memperjelas bahwa kombinasi input tertentu dapat menyebabkan masalah dan jika dokumentasi menjelaskan apa masalah potensial tersebut, pembuat kode dapat memeriksanya dan menangani input yang tidak valid dengan cara yang lebih bersih daripada hanya dengan menangkap pengecualian atau menanggapi set kosong. Atau mereka bisa malas. Either way, keputusan ada di tangan mereka.
aleppke
19
Tidak, set kosong adalah jawaban yang benar secara matematis. Melakukan sesuatu yang lain adalah mendefinisikan kembali matematika yang disepakati dengan tegas, yang tidak boleh dilakukan atas kemauan. Sementara kita berada di sana, haruskah kita mendefinisikan kembali fungsi eksponensial untuk melempar kesalahan jika eksponen sama dengan nol, karena kita memutuskan kita tidak menyukai konvensi bahwa angka apa pun yang dinaikkan ke daya "zeroth" sama dengan 1?
Wildcard
1
Saya akan menjawab sesuatu yang serupa. Metode ekstensi Linq Single()dan SingleOrDefault()segera muncul dalam pikiran. Single melempar pengecualian jika hasil nol atau> 1, sedangkan SingleOrDefault tidak akan melempar, dan sebagai gantinya kembali default(T). Mungkin OP bisa menggunakan CombinationsOf()dan CombinationsOfOrThrow().
RubberDuck
1
@ Kartu Memori - Anda membicarakan dua hasil berbeda untuk input yang sama yang bukan hal yang sama. OP hanya tertarik untuk memilih a) hasil atau b) melemparkan pengecualian yang bukan merupakan hasil.
James Snell
4
Singledan SingleOrDefault(atau Firstdan FirstOrDefault) cerita yang sama sekali berbeda, @RubberDuck. Mengambil contoh saya dari komentar saya sebelumnya. Ini baik-baik saja untuk anser tidak untuk query "Apa yang bahkan bilangan prima yang lebih besar dari dua ada?" , karena ini adalah jawaban yang logis dan masuk akal secara matematis. Lagi pula, jika Anda bertanya, "Apa yang pertama bahkan lebih besar dari dua?" tidak ada jawaban alami. Anda tidak dapat menjawab pertanyaan itu, karena Anda meminta satu nomor. Ini bukan tidak ada (itu bukan angka tunggal, tetapi satu set), bukan 0. Oleh karena itu kami melemparkan pengecualian.
Paul Kertscher
4

Anda perlu memvalidasi argumen yang disediakan saat fungsi Anda dipanggil. Dan faktanya, Anda ingin tahu cara menangani argumen yang tidak valid. Fakta bahwa banyak argumen saling bergantung, tidak menebus fakta bahwa Anda memvalidasi argumen.

Jadi saya akan memilih ArgumentException yang menyediakan informasi yang diperlukan bagi pengguna untuk memahami apa yang salah.

Sebagai contoh, periksa public static TSource ElementAt<TSource>(this IEnumerable<TSource>, Int32)fungsi di Linq. Yang melempar ArgumentOutOfRangeException jika indeks kurang dari 0 atau lebih besar dari atau sama dengan jumlah elemen dalam sumber. Dengan demikian indeks divalidasi sehubungan dengan enumerable yang disediakan oleh penelepon.

sbecker
sumber
3
+1 untuk mengutip contoh situasi serupa dalam bahasa.
James Snell
13
Anehnya, teladan Anda membuat saya berpikir, dan menuntun saya ke sesuatu yang saya pikir membuat contoh tandingan yang kuat. Seperti yang saya catat, metode ini dirancang untuk menjadi seperti LINQ, sehingga pengguna dapat menghubungkannya dengan metode LINQ lainnya. Jika Anda melakukannya, new[] { 1, 2, 3 }.Skip(4).ToList();Anda mendapatkan set kosong, yang membuat saya berpikir bahwa mengembalikan set kosong mungkin adalah pilihan yang lebih baik di sini.
anaximander
14
Ini bukan masalah semantik bahasa, semantik domain masalah sudah memberikan jawaban yang benar, yaitu mengembalikan set kosong. Melakukan hal lain melanggar prinsip ketakjuban seseorang yang bekerja di domain masalah kombinatorik.
Ukko
1
Saya mulai memahami argumen untuk mengembalikan set kosong. Mengapa itu masuk akal. Setidaknya untuk contoh khusus ini.
sbecker
2
@sbecker, untuk membawa titik rumah: Jika pertanyaan itu "Berapa banyak kombinasi yang ada dari ..." kemudian "nol" akan menjadi jawaban yang benar-benar valid. Dengan cara yang sama, "Apa yang kombinasi seperti itu ..." memiliki himpunan kosong sebagai jawaban benar-benar valid. Jika pertanyaannya adalah, "Apa kombinasi pertama sehingga ...", maka dan hanya dengan demikian pengecualian akan sesuai, karena pertanyaannya tidak dapat dijawab. Lihat juga komentar Paul K .
Wildcard
3

Anda harus melakukan salah satu dari yang berikut (meskipun terus secara konsisten menimbulkan masalah mendasar seperti jumlah kombinasi negatif):

  1. Berikan dua implementasi, satu yang mengembalikan set kosong ketika input bersama tidak masuk akal, dan satu yang melempar. Coba panggil mereka CombinationsOfdan CombinationsOfWithInputCheck. Atau apa pun yang Anda suka. Anda dapat membalikkan ini sehingga yang memeriksa input adalah nama yang lebih pendek dan daftar adalah CombinationsOfAllowInconsistentParameters.

  2. Untuk metode Linq, kembalikan yang kosong IEnumerablepada premis yang telah Anda jelaskan. Kemudian, tambahkan metode Linq ini ke perpustakaan Anda:

    public static class EnumerableExtensions {
       public static IEnumerable<T> ThrowIfEmpty<T>(this IEnumerable<T> input) {
          return input.IfEmpty<T>(() => {
             throw new InvalidOperationException("An enumerable was unexpectedly empty");
          });
       }
    
       public static IEnumerable<T> IfEmpty<T>(
          this IEnumerable<T> input,
          Action callbackIfEmpty
       ) {
          var enumerator = input.GetEnumerator();
          if (!enumerator.MoveNext()) {
             // Safe because if this throws, we'll never run the return statement below
             callbackIfEmpty();
          }
          return EnumeratePrimedEnumerator(enumerator);
       }
    
       private static IEnumerable<T> EnumeratePrimedEnumerator<T>(
          IEnumerator<T> primedEnumerator
       ) {
          yield return primedEnumerator.Current;
          while (primedEnumerator.MoveNext()) {
             yield return primedEnumerator.Current;
          }
       }
    }
    

    Akhirnya, gunakan seperti itu:

    var result = someInputSet
       .CombinationsOf(4, CombinationsGenerationMode.Distinct)
       .ThrowIfEmpty()
       .Select(combo => /* do some operation to a combination */)
       .ToList();

    atau seperti ini:

    var result = someInputSet
       .CombinationsOf(4, CombinationsGenerationMode.Distinct)
       .IfEmpty(() => _log.Warning(
          $@"Unexpectedly received no results when creating combinations for {
             nameof(someInputSet)}"
       ))
       .Select(combo => /* do some operation to a combination */)
       .ToList();

    Harap dicatat bahwa metode pribadi yang berbeda dari yang umum diperlukan agar perilaku melempar atau tindakan terjadi ketika rantai linq dibuat alih-alih beberapa waktu kemudian ketika dihitung. Anda ingin membuangnya segera.

    Namun, perlu diketahui bahwa setidaknya harus menyebutkan item pertama untuk menentukan apakah ada item. Ini adalah kelemahan potensial yang saya pikir sebagian besar dikurangi oleh fakta bahwa setiap pemirsa masa depan dapat dengan mudah alasan bahwa ThrowIfEmptymetode memiliki menghitung setidaknya satu item, jadi harus tidak terkejut dengan hal itu melakukannya. Tapi kamu tidak pernah tahu. Anda bisa menjadikan ini lebih eksplisit ThrowIfEmptyByEnumeratingAndReEmittingFirstItem. Tapi itu sepertinya berlebihan berlebihan.

Saya pikir # 2 cukup, baik, luar biasa! Sekarang kekuatannya ada dalam kode panggilan, dan pembaca kode selanjutnya akan mengerti apa yang dilakukannya, dan tidak perlu berurusan dengan pengecualian yang tidak terduga.

ErikE
sumber
Tolong jelaskan apa yang Anda maksud. Bagaimana sesuatu di posting saya "tidak berperilaku seperti set" dan mengapa itu hal yang buruk?
ErikE
Pada dasarnya Anda melakukan hal yang sama dengan jawaban saya, alih-alih membungkus kueri yang ingin Anda ajukan pada Kondisi Jika, hasilnya tidak berubah uu
GameDeveloper
Saya tidak dapat melihat bagaimana jawaban saya "pada dasarnya sama dengan" jawaban Anda. Mereka tampak sangat berbeda, bagi saya.
ErikE
Pada dasarnya Anda hanya menegakkan kondisi yang sama dari "kelas buruk saya". Tapi Anda tidak mengatakan itu ^^. Apakah Anda setuju, bahwa Anda menegakkan keberadaan 1 elemen dalam hasil kueri?
GameDeveloper
Anda harus berbicara lebih jelas untuk programmer yang jelas-jelas bodoh yang masih tidak mengerti apa yang Anda bicarakan. Kelas apa yang buruk? Kenapa itu buruk? Saya tidak menegakkan apa pun. Saya mengizinkan PENGGUNA kelas untuk memutuskan perilaku akhir dan bukan PENCIPTA kelas. Hal ini memungkinkan seseorang untuk menggunakan paradigma pengkodean yang konsisten di mana metode Linq tidak melempar secara acak karena aspek komputasi yang tidak benar-benar melanggar aturan normal seperti jika Anda meminta -1 item sekaligus.
ErikE
2

Saya bisa melihat argumen untuk kedua kasus penggunaan - pengecualian sangat bagus jika kode hilir mengharapkan set yang berisi data. Di sisi lain, hanya satu set kosong sangat bagus jika jika ini diharapkan.

Saya pikir itu tergantung pada harapan penelepon jika ini adalah kesalahan, atau hasil yang dapat diterima - jadi saya akan mentransfer pilihan ke penelepon. Mungkin memperkenalkan opsi?

.CombinationsOf(4, CombinationsGenerationMode.Distinct, Options.AllowEmptySets)

Sauer Kristen
sumber
10
Sementara saya mencapai tujuan Anda dengan ini, saya mencoba untuk menghindari metode yang memiliki banyak opsi yang disahkan yang mengubah perilaku mereka. Saya masih belum 100% senang dengan mode enum yang sudah ada di sana; Saya benar-benar tidak menyukai gagasan tentang parameter opsi yang mengubah perilaku pengecualian metode. Saya merasa seperti jika suatu metode perlu melempar pengecualian, itu perlu melempar; jika Anda ingin menyembunyikan pengecualian itu adalah keputusan Anda sebagai kode panggilan, dan bukan sesuatu yang dapat Anda lakukan dengan kode saya dengan opsi input yang tepat.
anaximander
Jika pemanggil mengharapkan set yang tidak kosong, maka itu tergantung pada pemanggil untuk menangani set kosong atau melemparkan pengecualian. Sejauh menyangkut callee, satu set kosong adalah jawaban yang sangat baik. Dan Anda dapat memiliki lebih dari satu penelepon, dengan pendapat berbeda tentang apakah set kosong baik-baik saja atau tidak.
gnasher729
2

Ada dua pendekatan untuk memutuskan apakah tidak ada jawaban yang jelas:

  • Tulis kode dengan asumsi opsi pertama, lalu opsi lainnya. Pertimbangkan mana yang akan bekerja paling baik dalam praktik.

  • Tambahkan parameter boolean "ketat" untuk menunjukkan apakah Anda ingin parameter diverifikasi secara ketat atau tidak. Misalnya, Java SimpleDateFormatmemiliki setLenientmetode untuk mencoba mengurai input yang tidak sepenuhnya cocok dengan format. Tentu saja, Anda harus memutuskan apa defaultnya.

JollyJoker
sumber
2

Berdasarkan analisis Anda sendiri, mengembalikan set kosong tampaknya jelas benar - Anda bahkan telah mengidentifikasinya sebagai sesuatu yang mungkin diinginkan beberapa pengguna dan belum jatuh ke dalam perangkap melarang penggunaan karena Anda tidak dapat membayangkan pengguna pernah ingin menggunakannya seperti itu.

Jika Anda benar-benar merasa bahwa beberapa pengguna mungkin ingin memaksakan pengembalian yang tidak kosong, maka beri mereka cara untuk meminta perilaku itu daripada memaksanya pada semua orang. Misalnya, Anda dapat:

  • Jadikan opsi konfigurasi pada objek apa pun yang melakukan tindakan untuk pengguna.
  • Jadikan sebagai bendera pengguna dapat secara opsional masuk ke fungsi.
  • Berikan AssertNonemptycek yang bisa mereka masukkan ke rantai mereka.
  • Buat dua fungsi, satu yang menegaskan nonempty dan satu yang tidak.

sumber
ini tampaknya tidak menawarkan sesuatu yang substansial atas poin yang dibuat dan dijelaskan dalam 12 jawaban sebelumnya
agas
1

Ini benar-benar tergantung pada apa yang diharapkan pengguna Anda dapatkan. Untuk contoh (yang agak tidak terkait) jika kode Anda melakukan pembagian, Anda bisa melempar pengecualian atau kembali Infatau NaNketika Anda membagi dengan nol. Namun, tidak ada yang benar atau salah:

  • jika Anda kembali Infdi pustaka Python, orang akan menyerang Anda karena menyembunyikan kesalahan
  • jika Anda meningkatkan kesalahan di perpustakaan Matlab, orang akan menyerang Anda karena gagal memproses data dengan nilai yang hilang

Dalam kasus Anda, saya akan memilih solusi yang paling mencengangkan bagi pengguna akhir. Karena Anda sedang mengembangkan perpustakaan yang berurusan dengan set, set kosong sepertinya sesuatu yang diharapkan pengguna Anda untuk berurusan dengan, jadi mengembalikannya terdengar seperti hal yang masuk akal untuk dilakukan. Tapi saya mungkin keliru: Anda memiliki pemahaman yang jauh lebih baik tentang konteks daripada orang lain di sini, jadi jika Anda mengharapkan pengguna Anda mengandalkan set yang selalu kosong, Anda harus segera melemparkan pengecualian.

Solusi yang memungkinkan pengguna memilih (seperti menambahkan parameter "ketat") tidak definitif, karena mereka mengganti pertanyaan asli dengan yang setara baru: "Nilai mana yang strictseharusnya menjadi default?"

Dmitry Grigoryev
sumber
2
Saya berpikir tentang menggunakan argumen NaN dalam jawaban saya dan saya membagikan pendapat Anda. Kami tidak dapat memberi tahu lebih banyak tanpa mengetahui domain OP
GameDeveloper
0

Ini adalah konsepsi umum (dalam matematika) bahwa ketika Anda memilih elemen dari set Anda tidak dapat menemukan elemen dan karenanya Anda mendapatkan set kosong . Tentu saja Anda harus konsisten dengan matematika jika menggunakan cara ini:

Aturan umum yang ditetapkan:

  • Set.Foreach (predikat); // selalu mengembalikan true untuk set kosong
  • Set.Exists (predikat); // selalu mengembalikan false untuk set kosong

Pertanyaan Anda sangat halus:

  • Bisa jadi input fungsi Anda harus menghormati kontrak : Dalam hal input yang tidak valid harus menimbulkan pengecualian, itu saja, fungsi tersebut tidak berfungsi di bawah parameter reguler.

  • Bisa jadi input fungsi Anda harus berperilaku persis seperti set , dan karenanya harus dapat mengembalikan set kosong.

Sekarang jika saya berada di Anda, saya akan pergi dengan cara "Set", tetapi dengan "TAPI" besar.

Asumsikan Anda memiliki koleksi yang "dengan hipotesis" hanya boleh dimiliki siswa perempuan:

class FemaleClass{

    FemaleStudent GetAnyFemale(){
        var femaleSet= mySet.Select( x=> x.IsFemale());
        if(femaleSet.IsEmpty())
            throw new Exception("No female students");
        else
            return femaleSet.Any();
    }
}

Sekarang koleksi Anda tidak lagi merupakan "set murni", karena Anda memiliki kontrak di atasnya, dan karenanya Anda harus menegakkan kontrak Anda dengan pengecualian.

Ketika Anda menggunakan fungsi "set" Anda dengan cara yang murni, Anda tidak boleh melempar pengecualian jika set kosong, tetapi jika Anda memiliki koleksi yang tidak lebih "set murni" maka Anda harus membuang pengecualian di mana yang tepat .

Anda harus selalu melakukan apa yang terasa lebih alami dan konsisten: bagi saya satu set harus mematuhi aturan, sementara hal-hal yang tidak ditetapkan harus memiliki aturan yang dipikirkan dengan baik.

Dalam kasus Anda, sepertinya ide yang baik untuk dilakukan:

List SomeMethod( Set someInputSet){
    var result = someInputSet
        .CombinationsOf(4, CombinationsGenerationMode.Distinct)
        .Select(combo => /* do some operation to a combination */)
        .ToList();

    // the only information here is that set is empty => there are no combinations

    // BEWARE! if 0 here it may be invalid input, but also a empty set
    if(result.Count == 0)  //Add: "&&someInputSet.NotEmpty()"

    // we go a step further, our API require combinations, so
    // this method cannot satisfy the API request, then we throw.
         throw new Exception("you requsted impossible combinations");

    return result;
}

Tapi itu bukan ide yang baik, kita sekarang memiliki keadaan tidak valid yang dapat terjadi saat runtime pada saat-saat acak, namun itu tersirat dalam masalah sehingga kita tidak bisa menghapusnya, yakin kita bisa memindahkan pengecualian di dalam beberapa metode utilitas (tepatnya kode yang sama, pindah di tempat yang berbeda), tetapi itu salah dan pada dasarnya hal terbaik yang dapat Anda lakukan adalah tetap berpegang pada aturan yang ditetapkan .

Infact menambahkan kompleksitas baru hanya untuk menunjukkan Anda dapat menulis metode kueri linq tampaknya tidak layak untuk masalah Anda , saya cukup yakin jika OP dapat memberi tahu kami lebih banyak tentang domainnya, mungkin kami bisa menemukan tempat di mana pengecualian benar-benar diperlukan (jika sama sekali, ada kemungkinan masalahnya tidak memerlukan pengecualian sama sekali).

GameDeveloper
sumber
Masalahnya adalah bahwa Anda menggunakan objek yang didefinisikan secara matematis untuk menyelesaikan masalah, tetapi masalahnya sendiri mungkin tidak memerlukan objek yang didefinisikan secara matematis sebagai jawaban (infact di sini mengembalikan daftar). Itu semua tergantung pada tingkat masalah yang lebih tinggi, terlepas dari bagaimana Anda menyelesaikannya, itu disebut enkapsulasi / penyembunyian informasi.
GameDeveloper
Alasan mengapa "SomeMethod" Anda seharusnya tidak lagi berperilaku seperti set adalah bahwa Anda meminta sepotong informasi "out of Sets", secara khusus lihat bagian yang berkomentar "&& someInputSet.NotEmpty ()"
GameDeveloper
1
TIDAK! Anda tidak harus melemparkan pengecualian di kelas wanita Anda. Anda harus menggunakan pola Builder yang menegaskan bahwa Anda bahkan tidak dapat MENDAPATKAN instance dari FemaleClasskecuali jika hanya memiliki betina (itu tidak bisa dibuat secara publik, hanya kelas Builder yang bisa membagikan contoh), dan mungkin memiliki setidaknya satu betina di dalamnya . Kemudian kode Anda di semua tempat yang FemaleClassdianggap sebagai argumen tidak harus menangani pengecualian karena objeknya dalam kondisi yang salah.
ErikE
Anda berasumsi bahwa kelas sangat sederhana sehingga Anda dapat menegakkan prasyaratnya hanya dengan konstruktor, pada kenyataannya. Argumen Anda salah. Jika Anda melarang untuk tidak memiliki wanita lagi, maka atau Anda tidak dapat memiliki metode untuk menghapus studends dari kelas atau metode Anda harus melempar pengecualian ketika ada 1 siswa yang tersisa. Anda baru saja memindahkan masalah saya ke tempat lain. Intinya adalah bahwa akan selalu ada beberapa kondisi / kondisi fungsi yang tidak dapat dipertahankan "oleh desain" dan bahwa pengguna akan rusak: itulah mengapa Pengecualian ada! Ini hal yang cukup teoretis, saya harap downvote bukan milikmu.
GameDeveloper
Downvote bukan milik saya, tetapi jika itu, saya akan bangga karenanya. Saya downvote hati-hati tetapi dengan keyakinan. Jika aturan kelas Anda adalah bahwa HARUS memiliki item di dalamnya, dan semua item harus memenuhi kondisi tertentu, maka membiarkan kelas berada dalam keadaan tidak konsisten adalah ide yang buruk. Memindahkan masalah ke tempat lain adalah niat saya! Saya ingin masalah terjadi pada waktu membangun, bukan pada waktu penggunaan. Inti dari menggunakan kelas Builder alih-alih melemparkan konstruktor adalah bahwa Anda dapat membangun keadaan yang sangat kompleks dalam banyak bagian dan bagian melalui inkonsistensi.
ErikE