順次コレクションは、インデックス0またはインデックス1で開始する必要がありますか?


25

複数のチャネルを持つデバイスのオブジェクトモデルを作成しています。クライアントと私の間で使用される名詞があるChannelChannelSet。(「セット」は順序付けられており、適切なセットがそうではないため、意味的に正確ではありません。しかし、それは別の時代の問題です。)

C#を使用しています。以下に使用例を示し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

すべてがダンディです。 ただし、クライアントはプログラマーではないため、ゼロインデックスによって完全に混乱します。最初のチャネルはチャネル1です。しかし、C#との一貫性を保つために、ChannelSetインデックスをゼロから維持したいと思います

これにより、開発チームとクライアントが相互作用するときに、いくつかの切断が確実に発生します。しかし、さらに悪いことに、これがコードベース内でどのように処理されるかに矛盾があると、潜在的な問題になります。たとえば、次のUI画面では、エンドユーザー(1インデックス作成の観点から考える)がチャネル13を編集しています。

チャンネル13または12?

そのSaveボタンは、最終的にいくつかのコードになります。ChannelSetインデックスが1の場合:

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

または、インデックスがゼロの場合:

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

私はこれをどのように扱うか本当によくわかりません。私は、順序付けられた整数インデックス付きのもの(ChannelSet)を、C#ユニバースの他のすべての配列およびリストインターフェイスと一貫性を保つこと(ゼロインデックス付けChannelSet)をお勧めします。しかし、その後、UIとバックエンドの間のすべてのコードには翻訳(1を引く)が必要になります。そして、誰もが陰湿で一般的なオフバイワンエラーが既にどの程度存在するかを知っています。

それで、このような決定はあなたをかみましたか?インデックスをゼロにするか、インデックスを1つにする必要がありますか?


60
「配列インデックスは0または1から始まるべきですか?0.5の私の妥協は、適切な考慮なしに拒否されました。」— スタンケリー
ブートル-gnat

2
@gnat私がオフィスにいるのは良いことです。コンピューター画面を見つめる笑い声は、通常、生産性の良い指標ではありません。
kdbanman

4
セット内での位置によってチャンネルを識別する必要がありますか?代わりに何か他のものでそれらを識別することはできませんか(例えば、それらがテレビチャンネルだった場合の頻度)
svick

@svick、それは素晴らしい点です。後で別の識別子によるチャネルアクセスを許可する場合がありますが、クライアントの主要な専門用語は、実際には1つのインデックスが付けられたチャネル番号のようです。
kdbanman

簡単に読んだところ、効率の問題としてインデックスを使用する必要がないように思えるので、配列について考える必要なく、何らかの種類のMapを使用してチャンネルIDとチャンネルを直接リンクできます。これがRobの狙いだと思います。また、インデックスの使用を選択した場合の彼のソリューションにも同意します。
マシュー

回答:


24

の識別子とChannel内の位置を混同しているように感じますChannelSet。以下は、コード/コメントが現時点でどのように見えるかを視覚化したものです。

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

a内のs は上限と下限を持つ数字で識別されるため、インデックスである必要があるため、C#であるため、0ベースであると判断したように感じます。各チャネルを自然に参照する方法が1からXの間の数字である場合、1からXの間の数字で参照します。それらを強制的にインデックスにしようとしないでください。ChannelChannelSet

あなたが本当に0ベースのインデックスでそれらにアクセスする方法を提供したい場合(これはあなたのエンドユーザー、またはコードを消費する開発者にどのような利点がありますか?)インデクサーを実装します:

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

2
これは本当に完全で簡潔な答えです。それはまさに私が他の答えから導き出したアプローチです。
kdbanman

7
通行人、私はこの答えを受け入れましたが、このスレッドは良い答えに満ちているので、先に読んでください。
kdbanman

とても素敵です、@ Rob。「インデクサーの使用方法を知っています。」-ネオ
ジェイソンPサリンジャー

35

インデックス1でUIを表示し、コードでインデックス0を使用します。

そうは言っても、私はこのようなオーディオデバイスを使用し、チャンネルにインデックス1を使用し、フラストレーションを避けるために「インデックス」またはインデクサーを使用しないようにコードを設計しました。一部のプログラマーはまだ文句を言っていたので、変更しました。その後、他のプログラマーが不満を言いました。

ひとつだけ選んでそれを使い続けてください。ソフトウェアを導入するという壮大な計画には、解決すべき大きな問題があります。


12
これの例外:1ベースの言語を使用している場合。コードは、コードの残りの部分および言語と一致する必要があるため、C#では0ベース、VBA(!)またはLuaでは1ベースです。UIは、人間が期待するもの(ほぼ常に1ベース)でなければなりません。
user253751

1
@immibis-良い点、私はそれを考えていませんでした。
テラスティン

3
昔のBASICでのプログラミングで見落としがちなことの1つは、「OPTION BASE」です...
Brian Knoblauch

1
@ BlueRaja-DannyPflughoeft:私はいつもOption Baseばかげていると思っていましたが、配列が任意の下限を個別に指定できるようにするいくつかの言語が提供する機能が好きでした。たとえば、年ごとにデータの配列がある場合、配列に次元を持たせることは、[firstYear..lastYear]常にelementにアクセスするよりも優れています[thisYear-firstYear]
supercat

1
@ BlueRaja-DannyPflughoeftある男のバグは別の男の機能です...
ブライアン

21

両方を使う。

UIとコアコードを混在させないでください。内部的に(ライブラリとして)、配列の各要素が最終ユーザーによってどのように呼び出されるかを「知らずに」コーディングする必要があります。「自然な」0インデックスの配列とコレクションを使用します。

データをUIに結合するプログラムの一部であるビューは、ユーザーのメンタルモデルと実際の作業を行う「ライブラリ」との間でデータを正しく変換するように注意する必要があります。

なぜこれが良いのですか?

  • コードはよりクリーンであり、インデックス作成を変換するハックはありません。これはまた、プログラマーが不自然な慣習に従うことを忘れてしまうことを期待しないため、プログラマーを助けます。
  • ユーザーは、期待するインデックスを使用します。あなたは彼らを困らせたくありません。

12

コレクションは2つの異なる角度から見ることができます。

(1)そもそも、配列やリストのような通常の順次コレクションです。0慣例に従い、インデックスfrom は明らかに正しいソリューションです。十分なエントリを割り当て、チャネル番号をインデックスにマップします。これは簡単です(単に減算します1)。

(2)あなたのコレクションは、本質的にチャンネル識別子とチャンネル情報オブジェクト間のマッピングです。チャネル識別子が整数の連続した範囲であることはたまたまあります。明日は何か[1, 2, 3, 3a, 4, 4.1, 6, 8, 13]。この順序付きセットをマッピングキーとして使用します。

アプローチの1つを選択し、文書化し、それに固執します。柔軟性の観点から、(2)に進みます。これは、チャネル番号の表示(少なくとも表示名)が将来変更される可能性が比較的高いためです。


あなたの答えのパート2に感謝します。そのようなものを採用すると思います。インデックスアクセサは、( channels[int])であろうゼロインデックス付き整数、およびgetアクセッサGetChannelByFrequencyGetChannelByNameGetChannelByNumber柔軟であろう。
kdbanman

1
2番目のアプローチは間違いなく最適です。私の地元の放送局は、1〜201の範囲のLCNで 32のチャンネルを提供しています。これには、スパース配列(84%の空)が必要です。概念的には、電話番号でインデックス付けされた配列に人々のコレクションを保存するようなものです。
ケリートーマス

3
さらに、それはUIドロップダウンで表されるということ小さいので、任意のコレクションは非常にデータ構造としてアレイの特定の性能特性から利益を得る可能性は低いです。そのため、ユーザーが「チャネル1」と呼ぶものは、コードでも「1」と呼ばれる可能性があり、Dictionaryor を使用しSortedDictionaryます。
スティーブジェソップ

1
最も驚きの原則は、それoperator [] (int index)が0ベースであるべきであることを意味しますが、operator [] (IOrdered index)そうではありません。(おおよその構文の謝罪、私のC#は非常に錆びています。)
9000

2
@kdbanman:個人的に、[]任意のキーによるルックアップにこのオーバーロードを使用する言語でオーバーロードしている場合、プログラマーはキーが最初から0連続していると想定してはならず、その習慣を鋭くするべきだと主張します。それらは、、0または1、または1000、または文字列から始まる場合があります。これは、任意のキーで"Channel1"演算子[]を使用することのポイントです。OTOH、これがCで、誰かが「0開始するために配列の要素を未使用のままにしておくべきだ」と言っていた場合、1「明らかに、はい」とは言わず、近い呼び出しになりますが、 「いいえ」。
スティーブジェソップ

3

皆と彼らの犬はゼロベースのインデックスを使用します。アプリケーション内で1ベースのインデックスを使用すると、メンテナンスの問題が永遠に発生します。

これで、ユーザーインターフェイスに表示するものは、完全にあなたとあなたのクライアント次第です。チャンネル番号としてi + 1を、チャンネル#iと一緒に表示するだけで、クライアントが満足する場合。

クラスを公開する場合、プログラマに公開します。ゼロベースのインデックスで混乱している人はプログラマーではないため、ここで切断されます。

UIとコード間の変換を心配しているようです。心配な場合は、チャネル番号のユーザーインターフェイス表現を保持するクラスを作成します。番号12をUI表現「チャンネル13」に変える1回の呼び出しと、「チャンネル13」を番号12に変える1回の呼び出しです。変更する。また、顧客がローマ数字、または文字A〜Zを要求した場合に機能します。


1
私の犬はどのタイプのインデックスを使用しているか教えてくれないので、あなたの答えを確認できません。
アルマーニ

3

インデックス作成とIDという2つの概念を混同しています。それらは同じものではなく、混同しないでください。

インデックス作成の目的は何ですか?高速ランダムアクセス。パフォーマンスに問題がなく、説明が与えられている場合は、インデックスを持つコレクションを使用する必要はなく、おそらく偶然です。

あなた(またはあなたのチーム)がインデックスとIDで混乱している場合、インデックスを持たないことでその問題を解決できます。辞書または列挙可能なものを使用し、値でチャネルを取得します。

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

1つ目はより明確で、2つ目はより短く、同じILに変換される場合と変換されない場合がありますが、いずれにせよ、これをドロップダウンに使用している場合、十分に高速です。


2

ChannelSetユーザーが対話することを期待するクラスを介してインターフェイスを公開しています。できる限り自然に使用するようにします。ユーザーが0ではなく1からカウントを開始することを期待している場合は、この期待を念頭に置いてこのクラスとその使用法を公開します。


1
そこに問題があります!エンドユーザーは1から開始することを期待しており、
開発

1
そのため、エンドユーザーのニーズに合わせてクラスを調整することをお勧めします。
バーナード

2

宣言はオブジェクトのサイズを示すため、0ベースのインデックスは人気があり、重要な人々によって保護されました。

最近のコンピューターでは、ユーザーが使用するコンピューターで、オブジェクトのサイズは重要ですか?

言語(C#)が配列の最初と最後の要素を宣言する明確な方法をサポートしていない場合は、より大きな配列を宣言し、宣言された定数(または動的変数)を使用して論理的な開始と終了を識別できます配列のアクティブ領域。

UIオブジェクトの場合、ほぼ確実にマッピングに辞書オブジェクトを使用する余裕があります(オブジェクトが1,000万個ある場合、UIの問題があります)、そして最高の辞書オブジェクトがリストであることが判明した場合どちらかの端に無駄なスペースがありますが、重要なものは何も失っていません。


実際、ゼロベースおよび1ベースのインデックスは、メモリ内での配列の構築方法に関係しています。ゼロベースのインデックスは外部メモリ(配列の外部)を使用してサイズを決定し、1ベースのインデックスは内部メモリ(インデックス0)を使用して配列のサイズを記憶します(通常はとにかく)。「オブジェクトのサイズ」では一般的ではありませんが、通常、メモリが配列に内部的に割り当てられる方法に関係しています。とにかく、あちこちでバイトをむやみにリークすることはお勧めしません。しかし、とにかく辞書は通常、より良いアイデアです。
phyrfox

@phyrfox:ゼロベースのインデックス作成で私が好む抽象化は、インデックスがオブジェクト間の位置識別すると言うことです。最初のオブジェクトはインデックス0と1の間にあり、2番目は1と2の間にあります。x<= y <= zの場合、範囲[x、y]と[y、z]は連続しますが重ならず、両方とも空で、特別な場合は必要ありません。ゼロベースのインデックス付けで「アイテム間にインデックスを配置」モデルを採用すると、6つのアイテムのリストが0から6の範囲に広がります。1から始まるインデックスでは、1から7になります。この抽象化は、「one-beyond」ポインターにうまく適合することに注意してください。
-supercat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.