Haruskah koleksi sekuensial saya mulai dengan indeks 0 atau indeks 1?

25

Saya membuat model objek untuk perangkat yang memiliki banyak saluran. Kata benda yang digunakan antara klien dan saya adalah Channeldan ChannelSet. ("Set" tidak akurat secara semantik, karena itu diatur dan set yang tepat tidak. Tapi itu masalah untuk waktu yang berbeda.)

Saya menggunakan C #. Berikut adalah contoh penggunaan ChannelSet:

// load a 5-channel ChannelSet
ChannelSet channels = ChannelSetFactory.FromFile("some_5_channel_set.json");

Console.Write(channels.Count);
// -> 5

foreach (Channel channel in channels) {
    Console.Write(channel.Average);
    Console.Write(",  ");
}
// -> 0.3,  0.3,  0.9,  0.1,  0.2

Semuanya keren. Namun, klien bukan pemrogram dan mereka benar - benar akan bingung dengan pengindeksan nol - saluran pertama adalah saluran 1 untuk mereka. Tapi, demi konsistensi dengan C #, saya ingin menjaga ChannelSetindeks dari nol .

Ini pasti akan menyebabkan beberapa terputusnya antara tim dev saya dan klien ketika mereka berinteraksi. Tetapi lebih buruk lagi, setiap inkonsistensi dalam bagaimana hal ini ditangani dalam basis kode adalah masalah potensial. Misalnya, inilah layar UI tempat pengguna akhir ( yang berpikir dalam 1 indeks ) mengedit saluran 13:

Saluran 13 atau 12?

Itu Savetombol pada akhirnya akan menghasilkan beberapa kode. Jika ChannelSet1 diindeks:

channels.GetChannel(13).SomeProperty = newValue;  // notice: 13

atau ini jika nol diindeks:

channels.GetChannel(12).SomeProperty = newValue;  // notice: 12

Saya tidak begitu yakin bagaimana menangani ini. Saya merasa ini adalah praktik yang baik untuk menjaga daftar hal-hal yang teratur (integer-indexed) dari hal-hal (yang ChannelSet) konsisten dengan semua array dan daftar antarmuka lainnya di alam semesta C # (dengan pengindeksan nol ChannelSet). Tapi kemudian setiap bagian kode antara UI dan backend akan membutuhkan terjemahan (kurangi dengan 1), dan kita semua tahu betapa berbahaya dan umum kesalahan yang sudah ada.

Jadi, pernahkah keputusan seperti ini menggigit Anda? Haruskah saya nol indeks atau satu indeks?

kdbanman
sumber
60
"Haruskah indeks array dimulai pada 0 atau 1? Kompromi 0,5 saya ditolak tanpa, saya pikir, pertimbangan yang tepat." - Stan Kelly-Bootle
nyamuk
2
@gnat Untung aku sendirian di kantor - semburan tawa menatap layar komputer biasanya bukan indikator produktivitas yang bagus!
kdbanman
4
Apakah saluran harus diidentifikasi oleh posisi mereka di set? Tidak bisakah Anda mengidentifikasi mereka dengan sesuatu yang lain (misalnya frekuensi jika itu adalah saluran TV)?
svick
@vick, itu poin yang bagus. Saya mungkin mengizinkan akses saluran oleh pengidentifikasi yang berbeda nanti, tetapi istilah utama klien tampaknya adalah nomor saluran yang diindeks.
kdbanman
Dari pembacaan cepat sepertinya tidak perlu menggunakan indeks sebagai masalah efisiensi, jadi Anda bisa menggunakan semacam Peta untuk langsung menautkan ID saluran dan saluran tanpa harus memikirkan array. Saya pikir inilah maksud Rob, dan saya juga setuju dengan solusinya jika Anda memilih untuk menggunakan indeks.
Matius Baca

Jawaban:

24

Rasanya seperti Anda sedang menyatukan Identifier Channeldengan posisinya dalam a ChannelSet. Berikut ini adalah visualisasi saya tentang bagaimana kode / komentar Anda akan terlihat saat ini:

public sealed class ChannelSet
{
    private Channel[] channels;
    /// <summary>Retrieves the specified channel</summary>
    /// <param name="channelId">The id of the channel to return</param>
    Channel GetChannel(int channelId)
    {
        return channels[channelId-1];
    }
}

Rasanya seperti Anda telah memutuskan bahwa karena Channels di dalam ChannelSetdiidentifikasi dengan angka yang memiliki batas atas dan bawah mereka harus indeks dan oleh karena itu berdasarkan C #, 0. Jika cara alami untuk merujuk ke masing-masing saluran adalah dengan angka antara 1 dan X, rujuklah dengan angka antara 1 dan X. Jangan mencoba dan memaksa mereka menjadi indeks.

Jika Anda benar-benar ingin memberikan cara untuk mengaksesnya dengan indeks berbasis 0 (manfaat apa yang diberikan pengguna akhir Anda, atau pengembang yang menggunakan kode?) Kemudian mengimplementasikan Indexer :

public sealed class ChannelSet
{
    private Channel[] channels;
    /// <summary>Retrieves the specified channel</summary>
    /// <param name="channelId">The id of the channel to return</param>
    public Channel GetChannel(int channelId)
    {
        return channels[channelId-1];
    }

    /// <summary>Return the channel at the specified index</summary>
    public Channel this[int index]
    {
        return channels[index];
    }
}
rampok
sumber
2
Ini adalah jawaban yang sangat lengkap dan ringkas. Justru pendekatan yang saya dapatkan dari jawaban lain.
kdbanman
7
Orang yang lewat, saya menerima jawaban ini, tetapi utas ini penuh dengan jawaban yang baik, jadi baca terus.
kdbanman
Sangat bagus, @Rob. "Aku tahu cara menggunakan Pengindeks." - Neo
Jason P Sallinger
35

Tampilkan UI dengan indeks 1, gunakan indeks 0 dalam kode.

Yang mengatakan, saya bekerja dengan perangkat audio seperti ini dan menggunakan indeks 1 untuk saluran dan merancang kode untuk tidak pernah menggunakan "Indeks" atau pengindeks untuk membantu menghindari frustrasi. Beberapa programmer masih mengeluh, jadi kami mengubahnya. Kemudian programmer lain mengeluh.

Pilih saja dan tetap dengan itu. Ada masalah yang lebih besar untuk dipecahkan dalam skema besar untuk mengeluarkan perangkat lunak.

Telastyn
sumber
12
Pengecualian untuk ini: jika Anda menggunakan bahasa berbasis 1. Kode Anda harus konsisten dengan sisa kode Anda dan bahasa Anda, jadi berbasis 0 di C #, 1 berbasis di VBA (!) Atau Lua. UI Anda harus sesuai dengan yang diharapkan manusia (yang hampir selalu berbasis 1).
user253751
1
@immibis - poin bagus, saya tidak memikirkan itu.
Telastyn
3
Satu hal yang kadang-kadang saya lewatkan tentang pemrograman masa lalu di BASIC adalah "OPTION BASE" ...
Brian Knoblauch
1
@ BlueRaja-DannyPflughoeft: Walaupun saya selalu berpikir Option Baseitu konyol, saya memang suka fitur beberapa bahasa yang ditawarkan memungkinkan array untuk secara individual menentukan batas bawah sewenang-wenang. Jika seseorang memiliki array data per tahun, misalnya, melampaui dimensi array [firstYear..lastYear]bisa lebih baik daripada harus selalu mengakses elemen [thisYear-firstYear].
supercat
1
@ BlueRaja-DannyPflughoeft Bug satu orang adalah fitur orang lain ...
Brian Knoblauch
21

Gunakan keduanya.

Jangan campur UI dengan kode inti Anda. Secara internal (sebagai perpustakaan) Anda harus kode "tanpa mengetahui" bagaimana setiap elemen pada array akan dipanggil oleh pengguna akhir. Gunakan array dan koleksi 0-indeks "alami".

Bagian dari program yang menggabungkan data dengan UI, tampilan, harus berhati-hati untuk menerjemahkan data dengan benar antara Model Mental Pengguna dan 'Perpustakaan' yang melakukan pekerjaan yang sebenarnya.

Kenapa ini lebih baik?

  • Kode bisa lebih bersih, tidak ada retasan untuk menerjemahkan pengindeksan. Ini juga membantu para programmer dengan tidak mengharapkan mereka untuk mengikuti dan ingat untuk menggunakan konvensi yang tidak wajar.
  • Pengguna akan menggunakan pengindeksan yang mereka harapkan. Anda tidak ingin mengganggu mereka.
Dietr1ch
sumber
12

Anda dapat melihat koleksi dari dua sudut yang berbeda.

(1) Ini, pada awalnya, koleksi berurutan biasa , seperti array atau daftar. Indeks dari 0jelas merupakan solusi yang tepat, setelah konvensi. Anda mengalokasikan cukup entri dan nomor peta saluran untuk indeks, yang sepele (kurangi saja 1).

(2) Koleksi Anda pada dasarnya adalah pemetaan antara pengidentifikasi saluran dan objek info saluran. Kebetulan pengidentifikasi saluran adalah kisaran integer berurutan; besok mungkin seperti itu [1, 2, 3, 3a, 4, 4.1, 6, 8, 13]. Anda menggunakan set yang dipesan ini sebagai kunci pemetaan.

Pilih salah satu pendekatan, dokumentasikan, dan pertahankan. Dari sudut pandang fleksibilitas, saya akan menggunakan (2), karena representasi nomor saluran (setidaknya nama yang ditampilkan) relatif cenderung berubah di masa depan.

9000
sumber
Saya sangat menghargai bagian 2 dari jawaban Anda. Saya pikir saya akan mengadopsi sesuatu seperti itu. Indeks accessor ( channels[int]) akan nol diindeks bilangan bulat, dan accesor Dapatkan GetChannelByFrequency, GetChannelByName, GetChannelByNumberakan fleksibel.
kdbanman
1
Pendekatan kedua jelas yang terbaik. Penyiar lokal saya menawarkan 32 saluran dengan LCN mulai dari 1 hingga 201. Ini akan membutuhkan array jarang (84% kosong). Secara konseptual seperti menyimpan kumpulan orang dalam array yang diindeks oleh nomor telepon.
Kelly Thomas
3
Selain itu, setiap koleksi yang sangat kecil yang diwakili oleh dropdown UI sangat tidak mungkin untuk mendapatkan manfaat dari karakteristik kinerja spesifik dari array sebagai struktur data. Jadi hal yang disebut "saluran 1" oleh pengguna mungkin juga disebut "1" dalam kode juga, dan gunakan a Dictionaryatau SortedDictionary.
Steve Jessop
1
Prinsip kejutan paling tidak akan menyiratkan bahwa operator [] (int index)harus berbasis 0, sementara operator [] (IOrdered index)tidak. (Permintaan maaf untuk sintaksis perkiraan, C # saya sangat berkarat.)
9000
2
@kdbanman: secara pribadi, saya berpendapat bahwa segera setelah Anda kelebihan []dalam bahasa yang menggunakan kelebihan ini untuk pencarian dengan kunci sewenang-wenang, maka programmer tidak boleh mengasumsikan kunci mulai dari 0dan berdekatan, dan harus menendang kebiasaan itu tajam. Mereka mungkin mulai dari 0, atau 1, atau 1000, string "Channel1", itulah inti dari menggunakan operator []dengan kunci sewenang-wenang. OTOH, jika ini adalah C dan seseorang berkata "haruskah saya meninggalkan elemen 0dalam array yang tidak digunakan untuk memulai dari 1" maka saya tidak akan mengatakan "jelas, ya", itu akan menjadi panggilan dekat tapi saya akan cenderung untuk "tidak".
Steve Jessop
3

Semua orang dan anjing mereka menggunakan indeks berbasis nol. Menggunakan indeks berbasis satu di dalam aplikasi Anda akan menyebabkan Anda masalah pemeliharaan selamanya.

Sekarang apa yang Anda tampilkan di antarmuka pengguna, itu sepenuhnya terserah Anda dan klien Anda. Cukup tampilkan i +1 sebagai nomor saluran, bersama dengan saluran #i, jika itu membuat klien Anda senang.

Jika Anda mengekspos kelas, maka Anda mengeksposnya ke programmer. Orang yang Anda bingung dengan indeks berbasis nol bukanlah pemrogram, jadi ada pemutusan di sini.

Anda sepertinya khawatir tentang konversi antara UI dan kode. Jika itu membuat Anda khawatir, buat kelas yang membawa representasi antarmuka pengguna dari nomor saluran. Satu panggilan mengubah angka 12 menjadi representasi UI "Channel 13", dan satu panggilan mengubah "Channel 13" menjadi angka 12. Anda harus tetap melakukannya, jika pelanggan berubah pikiran, jadi hanya ada dua baris kode Untuk mengganti. Dan itu berfungsi jika pelanggan meminta angka romawi, atau huruf A hingga Z.

gnasher729
sumber
1
Saya tidak dapat memverifikasi jawaban Anda karena anjing saya tidak akan memberi tahu saya jenis indeks apa yang ia gunakan.
armani
3

Anda mencampurkan dua konsep: pengindeksan dan identitas. Mereka bukan hal yang sama dan tidak boleh bingung.

Apa tujuan dari pengindeksan? Akses acak cepat. Jika kinerjanya tidak menjadi masalah, dan mengingat deskripsi Anda itu bukan masalah, maka menggunakan koleksi yang memiliki indeks tidak perlu dan mungkin tidak disengaja.

Jika Anda (atau tim Anda) dibingungkan oleh indeks vs identitas, maka Anda dapat menyelesaikan masalah itu dengan tidak memiliki indeks. Gunakan kamus atau enumerable dan dapatkan saluran berdasarkan nilainya.

channels.First(c=> c.Number == identity).SomeProperty = newValue;
channels[identity].SomeProperty = newValue;

Yang pertama lebih jelas, yang kedua lebih pendek - mereka mungkin atau mungkin tidak diterjemahkan ke dalam IL yang sama, tetapi bagaimanapun, mengingat Anda menggunakan ini untuk dropdown, itu harus cukup cepat.

jmoreno
sumber
2

Anda mengekspos antarmuka melalui ChannelSetkelas yang Anda harapkan berinteraksi dengan pengguna Anda. Saya akan membuatnya sealami mungkin bagi mereka untuk menggunakannya. Jika Anda mengharapkan pengguna Anda akan mulai menghitung dari 1 bukannya 0, maka ekspos kelas ini dan penggunaannya dengan harapan itu dalam pikiran.

Bernard
sumber
1
Di situlah letak masalahnya! Pengguna akhir saya diharapkan mulai dari 1, dan pengguna dev saya (termasuk saya) mengharapkan mulai dari 0.
kdbanman
1
Jadi saya sarankan mengadaptasi kelas Anda untuk memenuhi kebutuhan pengguna akhir Anda.
Bernard
2

Pengindeksan berbasis 0 populer, dan dipertahankan oleh orang-orang penting, karena deklarasi tersebut menunjukkan ukuran objek.

Dengan komputer modern, dengan komputer yang akan digunakan pengguna Anda, apakah ukuran objek itu penting?

Jika bahasa Anda (C #) tidak mendukung cara yang bersih untuk mendeklarasikan elemen pertama dan terakhir dari sebuah array, Anda bisa mendeklarasikan array yang lebih besar, dan menggunakan konstanta yang dideklarasikan (atau variabel dinamis) untuk mengidentifikasi awal dan akhir logis dari area aktif array.

Untuk objek UI, Anda hampir pasti mampu menggunakan objek kamus untuk pemetaan Anda, (jika Anda punya 10 juta objek, Anda punya masalah UI), dan jika objek kamus terbaik ternyata menjadi daftar dengan Membuang banyak ruang di kedua ujungnya, Anda tidak kehilangan sesuatu yang penting.

David
sumber
Sebenarnya indeks berbasis nol dan berbasis satu harus dilakukan dengan bagaimana array dibangun dalam memori. Indeks berbasis nol menggunakan memori eksternal (eksternal ke array) untuk menentukan ukuran, sedangkan indeks berbasis satu menggunakan memori internal (indeks 0) untuk mengingat ukuran array ( biasanya, bagaimanapun ). Ini tidak populer untuk "ukuran objek" tetapi biasanya ada hubungannya dengan bagaimana memori dialokasikan secara internal untuk array. Apapun, saya tidak akan merekomendasikan byte sembarangan bocor di sana-sini "hanya karena." Tetapi kamus biasanya merupakan ide yang lebih baik.
phyrfox
@ phyrfox: Abstraksi yang saya sukai untuk pengindeksan berbasis nol adalah untuk mengatakan bahwa indeks mengidentifikasi posisi antara objek; objek pertama terletak di antara indeks 0 dan 1, yang kedua antara 1 dan 2, dll. Diberikan x <= y <= z, rentang [x, y] dan [y, z] akan berturut-turut tetapi tidak tumpang tindih, dan salah satu atau keduanya mungkin kosong tanpa memerlukan sudut khusus. Ketika mengadopsi model "indeks duduk di antara item" dengan pengindeksan berbasis nol, daftar enam item mencakup rentang dari 0 hingga 6; dengan pengindeksan satu berbasis akan duduk dari 1 sampai 7. Perhatikan bahwa abstraksi ini cocok dengan pointer "satu-melampaui".
supercat