なぜゼロベースの配列が標準なのですか?


112

ここで尋ねられ質問は、仲間のプログラマーとの話し合いを思い出させてくれました。彼は、ゼロベースの配列は配列とポインターおよびコンピューターハードウェアの動作方法に由来する実装の詳細であるため、ゼロベースの配列を1ベースの配列に置き換える必要があると主張しましたが、これらの種類はより高いレベルに反映されるべきではありません言語。

今、私は議論するのがあまり得意ではないので、ゼロベースの配列に固執する正当な理由を提供することはできませんでした。配列の一般的な出発点なぜゼロなのですか?


n要素の配列では、要素 'n'は存在しません。n要素の配列には、0からn-1までの番号のメンバーのみがあります。したがって、1から始まる配列があり、n要素の配列が実際に配列内に存在するn個の要素を表す場合、それは良くありません。

バイトの最初のビットが2 ^ 1ではなく2 ^ 0であるため、ゼロベースの配列が好きです。これは時々私を助けます:)
e-MEE

たとえばen.wikipedia.org/wiki/…などのこのリストを見ると、ほとんどのドメイン固有の言語は1でインデックス付けを開始し、csの考え方の学校のほとんどの言語は0で開始します。 、彼はこのテーマについて非常に多くの愚かな議論が行われたことに気付くでしょう。「1」を守るために、世界中のほとんどの人がそれを使用しています。ほとんどのプログラマは「0」を使用します。ほとんどの人はプログラマーを理解していないので、それはあなたに思わせます
ルーク

この質問がStackOverflowからProgrammersに移行された後、非建設的なものとしてクローズされたとは信じられません。これは馬鹿げています。
パーセバル

回答:


105

Edsger W. Dijkstraの記事「なぜ番号付けはゼロから始めるべきか」よりも強力な議論を提供できる人はいないと思います。


彼はいくつかの統計を表示し、数学スタイルの証明を使用しました。しかし、私は誰かがまだ議論できると確信しています。私は承認しますが、私はそうしません。

6
ダイクストラの記事はスタイルに関するものですが、彼の主張はシンプルさと使いやすさに関するものです... +1。
paercebal 08

80

権威論

まあ...どうやら、非常に最近の言語を含むほとんどの言語はゼロベースです。これらの言語は非常に熟練した人々によって書かれたので、あなたの友人は間違っているに違いありません...

なんで?

ゼロよりも1の方が良い開始インデックスになるのはなぜですか?なぜ2または10ではないのですか?回答自体は興味深いものです。アイデアを擁護する人々のプロセスについて多くを示しているからです。

最初のための引数は、それがより自然だということである第一は、通常、1人の大多数のため、少なくとも、他のすべての前に...

number- 1つの引数は、最後のインデックスはまた、配列の大きさであるということです...

私は今でも、この種の議論でよく耳にする理由の「品質」に感銘を受けています...そして、それを思い出すとさらに…

なぜゼロではないのですか?

...「1から始まる」表記法は、西洋文化からの残り物であり、数世紀にわたってゼロの存在を無視していました。

信じられないかもしれませんが、元のグレゴリオ暦は-3、-2、-1、1、2、3から始まります...それが西洋科学に貢献した問題を想像してみてください元のグレゴリオ暦が減算と同じくらい単純なものと競合するよりも見るために1月2日まで...)。

1ベースの配列を維持することは(まあ、そのために改造されます... ^ _ ^ ...)、21世紀のマイルとヤードを維持するようなものです...

なぜゼロなのか?数学だから!

最初(OOops ...すみません...もう一度やります)

ゼロ、ゼロは何もない、1つは何かです。そして、いくつかの宗教的なテキストは、「最初は何もなかった」としています。いくつかのコンピューター関連の議論は宗教的な議論と同じくらい燃えている場合があるので、この点は見た目ほどトピックから外れていません... ^ _ ^

最初に、1から始まる配列を操作してそのゼロ番目の値を見つけるためにハックするよりも、ゼロから始まる配列を操作してそのゼロ番目の値を無視する方が簡単です。この理由は、以前とほぼ同じくらい愚かですが、1ベースの配列を支持する最初の議論もかなり誤りでした。

第二に、数字を扱うときはチャンスが高いので、いつか数学を扱うことを覚えておきましょう。そして、数学を扱うときはチャンスが良いことを思い出してください。Oneベースの表記法も数世紀にわたって数学と日付を悩ませていました。私たちの間違いから学ぶことで、私たちは未来志向の科学(コンピューター言語を含む)でそれを避けるよう努力すべきです。

第三に、ハードウェアに関連付けられているコンピューター言語配列については、21個の整数のC配列を割り当て、ポインターを10インデックス右に移動すると、自然な[-10から10]配列になります。これはハードウェアにとって自然ではありません。しかし、それは数学のためです。もちろん、数学は時代遅れかもしれませんが、私が最後にチェックしたとき、世界中のほとんどの人はそうではないと信じていました。

4、すでに別の場所で指摘したように、離散位置(または離散値に減少した距離)であっても、最初のインデックスはゼロであり、建物の床(ゼロから始まる)、減少するカウントダウン(3、2、1、ゼロ) !)、地上高度、画像の最初のピクセル、温度(絶対零度の場合はゼロケルビン、273 Kの水凍結温度としては摂氏0度)。実際、実際に1で始まるのは、従来の「最初2番目3番目など」の方法だけです。自然に次のポイントにつながる反復表記法...

(天然に以下の点以前は)高レベルのコンテナでないインデックスではなくによって、アクセスされるべきであるということであるイテレータインデックス自体が本質的な価値を持っていない限り、。あなたの「高レベル言語」の支持者がそれについて言及しなかったことに驚いています。インデックス自体が重要な場合、数学関連の質問を念頭に置いて半分の時間をかけることができます。したがって、1から始まる「あなたの古いグレゴリオ暦」のように数学が無効にされており、それを機能させるには逆流ハックが必要なコンテナを数学に優しいものにしたいのです。

結論

あなたの仲間のプログラマーによって与えられた議論は、それが不自然に話し言葉/書き言葉の習慣を不必要に結び付けるため、誤解です。それは、コンピューター言語(あなたの命令をぼやけさせたくない)と、この問題の理由として、彼は、言語の抽象化がますます進むにつれて、ゼロベースの配列は過去のものであることを納得させたいと考えています。

ゼロベースの配列は、数学関連の理由によりゼロベースです。ハードウェア関連の理由ではありません。

さて、これがあなたの仲間のプログラマーにとって問題であれば、イテレーターやforeachループのような本当の高レベルの構造でプログラミングを始めさせてください。


ゼロ摂氏273,15Kである)

2
私は... ^ _ ^ ...と私の引数に色をしようとしたユーモラスな側面よりもポイント少なかった私は(私は物理学上のマスターの卒業証書を持っている)知っているが、私は小数で遊んで感じた
paercebal

20
段落には、「ゼロ、1、2、3、4、5」というラベルが付いています。一貫性を保つために、基数(「ゼロ、1、2、3、4、5」)または序数(「ゼロス、1番目、2番目、3番目、4番目、5番目」)を使用する必要があります。:-)
ShreevatsaR

10
同様に、私たちの生活の最初の年のために、私たちは1歳ではありませんが、ゼロ歳

3
@Nikita Rybak:驚くべきことは、あなたの前にすべてのコメンテーターが見たものを見逃したということです:もちろん、Bill the Lizardの答えは正しいものです。これが私が彼に+1を投票した理由であり、これが質問の最良の答えとして選ばれた理由です。私の答えは、1ベースのアレイの背後にある誤った理由をからかうことと、1ベースのアレイが迷惑になる具体的なケースを提供することです。それでも、私は...あなたもの理由が皮肉と混合されて考えると、「ない単一説得力のあるものを」見つけ驚い
paercebal

47

半開間隔はうまく構成されます。 処理し0 <= i < limているときにn要素ごとに拡張したい場合、新しい要素にはの範囲のインデックスがありますlim <= i < lim + n。ゼロベースの配列を使用すると、配列分割または連結するとき、または要素をカウントするときに算術が簡単になります。単純な算術演算がフェンスポストエラーの減少につながることを願っています。


半開間隔の+1-すべてを簡単にします。
Eclipse

29

特定のタイプの配列操作は、1ベースの配列では非常に複雑になりますが、0ベースの配列では単純なままです。

ある時点でいくつかの数値解析プログラミングを行いました。FORTRANとC ++の両方で記述された圧縮されたスパース行列を操作するアルゴリズムを使用していました。

FORTRANアルゴリズムには多くのa[i + j + k - 2]がありましたが、C ++にはあっa[i + j + k]たのは、FORTRAN配列が1ベースで、C ++配列が0ベースだったためです。


同意する。1ベースの配列が便利だと思うのは、nullアイテムインデックス用のスペースを作りたいときだけです。たとえば、オブジェクトの配列があり、そのインデックスをハンドルとして使用し、nullハンドルが必要な場合。
ファビオセコネロ08

私は、1ベースの配列の不必要な複雑さにぶつかりました。0ベースの配列は、限られた経験の中で、まれな例外を除いて、常により明確な配列インデックスのコードを生成しました。

FORTRANインデックスとC ++インデックスは、それぞれのインデックスが1だけオフセットされている場合、どのように2異なりますか?また、なぜマイナス 2ですか?FORTRANが1ベースの場合、2(または1)を追加しませんか?
RexE 08

@RexE:それが機能する方法であり、1ベースの配列では非常に複雑です。
ジェイバズージ08

@RexE:3D配列をフラット配列でエミュレートするとします。次に、0ベースでは、要素(0 0 0)はフラット配列の要素0に対応します。OTOHが1から始まる場合、要素(1 1 1)はフラット配列の要素1に対応します:1 + 1 + 1-2。

22

配列内のインデックスは、実際にはインデックスではありません。これは、配列の先頭からの距離である単純なオフセットです。最初の要素は配列の先頭にあるため、距離はありません。したがって、オフセットは0です。


3
現在設計されているほとんどの言語では、これは実際には実装の詳細であり、言語に表示されるべきではありません(そうするより適切な理由がある場合を除く)
Jens Schauder

17

理由は単なる歴史的なものではありません。CとC ++は現在も広く使用されており、ポインター演算は配列がインデックス0から始まる非常に有効な理由です。

ポインター演算を持たない他の言語の場合、最初の要素がインデックス0または1にあるかどうかは、他の何かというよりもむしろ慣習です。
問題は、最初の要素としてインデックス1を使用する言語が真空に存在せず、通常-で書かれたライブラリと対話する必要があることです-あなたはそれを推測-CまたはC ++ ...

VBとその派生フレーバーは、配列が0または1から始まることに悩まされており、長い間問題の原因でした。

結論としては、最初から最後まで一貫している限り、最初の要素のインデックスを言語がどのように考えるかは問題ではありません。問題は、1を最初のインデックスとして考慮すると、実際に作業することが難しくなることです。


同意した。一貫性が重要であり、低レベルのコード(C / C ++を含む)を完全に回避する余裕がない限り、1ベースの配列を操作するのは問題を引き起こすだけです。
Shog9

私たちがそれに取り組んでいる間に、質問:プラットフォーム固有ではない方法で低レベルのコードを使用したことがありますか?言い換えれば、あなたは常に何らかのプラットフォーム上にいて、あなたはどちらを知っている必要がありますか?
ダンローゼンスターク08

1
VB .NETが通常不当に悪いと考える人として、配列でのVB .NETの実践はひどいことだと言わざるを得ません。それらは違いを分割し、さらに混乱させました。配列は0から始まりますが、Dim a as Integer(5)は6つの位置を持つ配列を作成します。理論的な理由は、配列の長さを超えてアドレス指定することでバグが発生するよりも、余分な位置を確保する方が優れているように思われます。残念なことに(およびAndやOrのような他の問題については)、彼らは結局VB .NETを使用しなくなった多くのVB6プログラマーからの要求に屈しました。
キラレッサ2009

1
@Kyralessa:いいえ。その理由は、表記法が直感に反し、エラーが発生しやすいことをよく知っていたとしても、VB6(自動アップグレードアシスタント…)との後方互換性を持つことでした。一方、AndおよびOrビット単位はVB6とは何の関係も持っていないこと、それはVB型言語のための唯一の論理的なソリューションです。あなたあなたの論理操作のために持っAndAlsoOrElseいます。
コンラッドルドルフ

AndそしてOr、彼らはVB6でビット単位であったためビット単位であることは、VB6で行うためにすべてを持っています。醜い事業者AndAlsoOrElseビット演算、論理のものよりもはるかに少ない共通していることから、ビット単位の行われてきたはずです。ByValがデフォルトであるにもかかわらず、あちこちに塗りつぶされているという事実のように、「後方互換性」のために言語に残されているようなlikeいイボがたくさんあります。
キラレッサ

10

ゼロベースの配列は、Cおよびアセンブラーにもそのルーツがあります。Cでは、ポインター計算は基本的に次のように機能します。

  • 配列の各要素は、特定のバイト数を占有します。32ビット整数は(明らかに)4バイトです。
  • 配列のアドレスは、配列の最初の要素と、その後の同じサイズの連続したブロックの後続の要素によって占有されます。

例として、int a[4]0xFF00にあると仮定します。アドレスは次のとおりです。

  • a [0]-> 0xFF00;
  • a [1]-> 0xFF04;
  • a [2]-> 0xFF08;
  • a [3]-> 0xFF0C。

したがって、ゼロベースのインデックスでは、addresの数学は簡単です。

要素のアドレス=配列のアドレス+インデックス* sizeof(type)

実際、Cの式はすべて同等です。

  • a [2];
  • 2 [a]; そして
  • *(a + 2)。

1ベースの配列では、数学は(それでも)少し複雑です。

その理由は大部分が歴史的です。


1
元の質問はすでに「ゼロベースの配列は、配列とポインターおよびコンピューターハードウェアの動作方法に由来する実装の詳細ですが、これらの種類のものは高級言語に反映すべきではありません」と述べています。

Nベースの配列を許可する言語は、通常、ゼロのランタイムコストで自動的に計算される配列「オフセット」を含むコードを生成することに言及する価値があります。
ロディ

8

ゼロベースの配列を使用する場合、配列の長さは有効なインデックスのセットです。少なくとも、それはペアノ​​算術が言うことです:

0 = {}
1 = 0 U {0} = {0}
2 = 1 U {1} = {0,1}
3 = 2 U {2} = {0,1,2}
...
n = n-1 U {n-1} = {0,1,2...n-1}

ある意味で、これは最も自然な表記法です。


7

Cの配列とポインターの間には強い相関関係があるため

char* p = "hello";
char q[] = "hello";

assert(p[1] == q[1]);

assert(*p == *q)

* pは*(p + 0)と同じです

開始インデックスが1であれば、後で頭痛がします


5

ヒープは、1ベースの配列の利点の一例です。インデックスiを指定すると、iの親と左の子のインデックスは

PARENT[ i ] = i ÷2

LCHILD[ i ] = i ×2

ただし、1ベースのアレイのみ。0ベースの配列の場合

PARENT[ i ] =(i + 1)÷2-1

LCHILD[ i ] =(i + 1)×2-1

そしてその後、プロパティ有しiは([1の範囲内、すなわちインデックスもそのインデックスにサブアレイの大きさをI ])。

しかし、最終的には重要ではありません。通常よりも1つ多くの要素を割り当て、0番目を無視することで、0ベースの配列を1ベースの配列にできるからです。したがって、必要に応じて1ベースの配列の利点を得るためにオプトインし、他のほとんどすべての状況でよりクリーンな算術演算のために0ベースの配列を保持できます。


4

私の感覚では、それは完全にarbitrary意的です。ゼロベースまたは1ベースの配列には特別なものはありません。Visual Basic(ほとんどの場合、Excelで小さなことをします)から解放されて以来、1ベースの配列を扱ったことはありません。それは同じです。実際、配列の3番目の要素が必要な場合は、3または2と呼ばれる実装の詳細にすぎませんただし、配列で行う作業の99%は、2つの絶対ポイントにのみ関係します。最初の要素とカウントまたは長さ。繰り返しますが、最初の要素は1ではなくゼロと呼ばれるか、最後の要素はcount-1または代わりにcountと呼ばれるという実装の詳細です。

編集:一部の回答者は、1ベースの配列はフェンスポストエラーを起こしやすいと述べています。私の経験では、今考えてみると、これは本当です。VBでは、「これはうまくいくか、1つずれているので爆発する」と思ったことを覚えています。Javaでは決して起こりません。私は良くなったと思っていましたが、回答者の中には、0ベースの配列がより良い算術をもたらすケースを指摘しています。


PHPでは、文字列関数内のほとんどの検索は、見つからない場合にFALSEを返します。-1ではありません。
jmucchiello 08

カウントを最後の要素のインデックスと混同しています。空の配列のカウントは、0ベースの配列を使用するか1ベースの配列を使用するかに関係なく、常に0です。1ベースの配列の利点は、カウントが最後の要素の位置であることです(ただし、それが唯一の利点です)。

これらの点の両方に関して真です:ゼロベースまたは1ベースの配列と同じであるため、最後の半分が削除されました:要素がゼロの場合、カウントは0です。
ダンローゼンスターク08

私は...私の答えの最後の半分を意味
ダンRosenstark

4

10年以上のC / C ++プログラマとして、PascalとDelphiで非常に強力なバックグラウンドを持っているので、Pascalの強力な配列バインドとインデックスタイプチェック、およびそれに伴う柔軟性と安全性を今でも見逃しています。これの明白な例は、各月の値を保持する配列データです。

パスカル:

 Type Month = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec);

  Var Days[Month] of integer;

  ... 
  if Year mod 4 = 0 then // yes this is vastly simplified for leap years and yes i don't know what the comment marker is in pascal and no i won't go look it up
    Days[Feb] := 29
  else
    Days[Feb] := 28;

+/- 1や「マジックナンバー」を使用せずにC言語で同様のコードを記述することは非常に困難です。Days [2]やDays [Jan + Dec]のような式はコンパイルされないことに注意してください。これは、Cやアセンブラーでまだ考えている人にとっては残忍なように見えます。

Pascal / Delphi言語には多くの側面がありますが、私は少し見逃していませんが、Cのゼロベースの配列は比較すると「馬鹿げている」ように見えます。


あなたのアルゴリズムが2100年に対して正しくないことは注目に値するかもしれません。en.wikipedia.org/ wiki

2
私は...しかし、それは私がちょうど「PEDANTをスポット」遊んだ2000年のために正しかった;-)知っている
ロディ

物思いにふける!笑。
jcollum

はい。すべての問題を回避し、必要に応じて配列をベースにします。
ローレンペクテル

あなたの平均的なPascalコンパイラがマシンコードを生成するときにJan = 0、Dec = 11を割り当てても驚かないでしょう:-)

4

1ではなく0から始まる理由は、この要素が配列のメモリの先頭からどれだけ離れているかをオフセットと考えることができるためです。それは私に0番目の要素を与えると言っているのではなく、最初から0要素である要素を私に与えているということです。

別の見方をすれば、これらは(ほとんどの場合)同等であるということです。

array[n]

*(array + n)

標準が変更されない理由は、Cが約40年前から存在しているためです。変更する説得力のある理由はありません。変更した場合、配列の先頭が0であることに依存する既存のコードはすべて破損します。


実際には、あなたは書き換えることができますarray[n]ようn[array]C.でそれはそれを行うにはお勧めできません、それは混乱です!しかし、上記のアイデンティティと追加が可換であるという事実のため、それは合法です(少なくともC89まで)。
ドナルドフェローズ

それはそれを書くためのクレイジーな方法です-あなたが維持しなければならないコードを見た場合、それは大きな警告サインになるでしょう。ありがたいことに、私はそのことに出くわしていません...まだ:)
デニスマンシー

4

いくつかの元の位置/相対位置情報を含むコードは、0から始まる配列を使用するとはるかにきれいになります。

たとえば、より大きなベクトルの定義された位置にベクトルをコピーするコードは、1から始まる配列では苦痛です:

function copyAtPos (dest, vect, i):
    for i from 1 -> vect.length do
        dest[pos+i-1] = vect[i]

0から始まる配列との対立により:

function copyAtPos (dest, vect, i):
    for i from 0 -> vect.length-1 do
        dest[pos+i] = vect[i]

大きな畳み込み式を書き始めるなら、それは必須になります。


3

なぜ2または3または20ではないのですか?1ベースの配列を使用することは、ゼロベースの配列を使用するよりも簡単または簡単に理解できるようではありません。1ベースの配列に切り替えるには、そこにいるすべてのプログラマーが配列の操作方法を再学習する必要があります。

さらに、既存の配列へのオフセットを処理している場合は、さらに意味があります。配列から115バイトを読み込んだ場合、次のチャンクは115から始まることがわかります。など、次のバイトは常に読み込んだバイトのサイズです。1ベースでは、常に1を追加する必要があります。

また、「真の」ポインター演算のない言語であっても、配列内のデータのチャンクを処理する必要がある場合があります。Javaでは、メモリにマップされたファイル、またはバッファにデータを含めることができます。その場合、ブロックiのサイズは* iです。1から始まるインデックスでは、block * i + 1になります。

1から始まるインデックス付けでは、多くの手法であらゆる場所で+1が必要になります。


なぜ2、3、20ではないのですか?0は付加的アイデンティティであり、1は乗法的アイデンティティであるためです。彼らは最も理にかなっています。

3

1ベースの配列を使用して、単一次元配列を多次元配列に変換します。

int w = 5, h = 5, d = 5;

int[] a1 = new int[w * h * d], new a2 = int[w,h,d];

for (int z = 1; z <= d; z++)

  for (int y = 1; y <= h; y++)

    for (int x = 1; x <= w; x++)

      a1[x + (y - 1) * w + (z - 1) * h] = a2[x,y,z];

配列が1ベースであっても、yおよびzインデックスは0ベース(y-1、z-1)であることに注意してください。状況によっては、0ベースのインデックスを避けることができません。一貫性を保つために、なぜ0ベースのインデックスを常に使用しないのですか?


3

なぜ配列を1から始めたいのですか?

あなたが言うときa[x][y]、コンパイラはこれを:に翻訳しますa+(x*num_cols+y)。配列が1から始まる場合、これはになりa+(x*num_cols+y-1)ます。これは、配列要素にアクセスするたびに追加の算術演算になります。なぜプログラムを遅くしたいのですか?


1
+ Y -実際には、+(1)* NUM_COLS(X) -になることだろう1) - XおよびYの両方が1から開始する
デニスMunsie

2

ここで手足を踏み出し、整数の「キー付き」配列とは異なる何かを提案します。

あなたの同僚は、常に1でカウントを開始する物理世界の「セット」の1対1のマッピングを作成しようとしていると思います。私はこれを理解できます。ソフトウェアと物理的な世界との間で1対1でマッピングされている場合。

私のおすすめ

格納するものに整数ベースの配列を使用するのではなく、他の種類の辞書またはキーと値のペアを使用します。あなたは任意の整数に縛られないので、これらは実際の生活により良く写ります。これには場所があり、ソフトウェアと物理世界の間で概念1対1をマッピングする利点があるため、できる限り使用することをお勧めします。

すなわちkvp['Name Server'] = "ns1.example.com"; (これは100万の可能な例のうちの1つにすぎません)。

ディサイマー

これは、数学がコンピューターの実際の実装に近いため、数学に基づいた概念を使用している場合はほとんど確実に機能しません。ここでkvpセットを使用しても何の助けにもなりませんが、実際には混乱を招き、問題が大きくなります。私は、何かがkvpまたは配列としてより良く機能する可能性があるすべてのコーナーケースを考えたことがありません。

最終的なアイデアは、ゼロベースの配列またはキーと値のペアを使用することです。ハンマーしか持っていない場合、すべての問題は爪のように見えます。


2

個人的には、1つの引数は、配列インデックスをオフセットとして見るときです。理にかなっています。

最初の要素と言えますが、配列の原点に対する最初の要素のオフセットはゼロです。そのため、配列の原点を取得してゼロを追加すると、最初の要素が生成されます。

したがって、計算では、1を追加してから1を削除するよりも、ゼロを追加して最初の要素を見つける方が簡単です。

低レベルのものを作った人はだれでも、基本はゼロだと思います。そして、多くの場合、非アルゴリズムプログラミングを始めたり、より高いレベルに慣れている人々は、ベース1システムを希望するかもしれません。または、過去の経験に偏っているだけかもしれません。


正確に-それは基本的に、低レベルの言語に由来する慣習です。

2

1ベースのインデックスの代わりに0ベースのインデックスを使用する2つの(非常に)深刻な理由は、多くのプログラマー後方互換性のための再教育を回避するようです。

あなたが受け取ったすべての回答には、1ベースのインデックスに対する他の深刻な議論はありませんでした。

実際、インデックスは自然に1ベースです。これが理由です。

まず、私たちは尋ねなければなりません:配列はどこから来たのですか?彼らは実世界で同等のものを持っていますか?答えはイエスです。コンピューターサイエンスでベクターとマトリックスをモデル化する方法です。ただし、ベクトルとマトリックスは、コンピューター時代よりも前に1ベースのインデックスを使用していた数学の概念です(現在でもほとんど1ベースのインデックスを使用しています)。

現実の世界では、インデックスは1ベースです。

トーマスが上記で述べたように、0ベースのインデックスを使用した言語は、実際にはインデックスではなくオフセットを使用しています。そして、これらの言語を使用している開発者は、インデックスではなくオフセットについて考えます。物事が明確に述べられていれば、これは問題になりませんが、そうではありません。オフセットを使用する多くの開発者は、まだインデックスについて話します。そして、インデックスを使用する多くの開発者は、C、C ++、C#などがオフセットを使用していることをまだ知りません。

これは言葉遣いの問題です。

(Diskstraの論文について注意してください- それは私が上で言っている、まさに言う:数学者1ベースのインデックスを使用しない。しかしDiskstraがmatematiciansがあると思います。べきではありません例えば:1 <= N <= 0(一部の式は、その後醜いになるので、それらを使用)。まあ、彼がその1つに正しいかどうかはわかりません-それらの例外的な空のシーケンスを避けるためにそのようなパラダイムシフトを行うことは、少しの結果のために多くの問題のようです...)


2
数学者は常に1ベースのインデックスを使用するとは限りません。シーケンスの初期値にx0が何度も使用されるのを見てきました。どちらがより便利であるかに依存します。

2

実際に1900年代を指す「20世紀」に悩まされたことはありますか?それは、1ベースのアレイを使用するときにいつも面倒なことを処理するための良い例えです。

.net IO.stream読み取りメソッドのような一般的な配列タスクを検討してください。

int Read(byte[] buffer, int offset, int length)

0ベースの配列の方が優れていると納得させるために私が提案することは次のとおりです。

各インデックススタイルで、読み取りをサポートするBufferedStreamクラスを記述します。1ベースの配列の読み取り関数の定義を変更できます(たとえば、オフセットの代わりに下限を使用します)。派手なものは何も必要ありません。単純にしてください。

さて、これらの実装のどれがよりシンプルですか?あちこちに+1と-1のオフセットが散らばっているのはどれですか?私もそう思っていました。実際、インデックス付けのスタイルが問題にならないのは、Setのような配列ではないものを使用すべきだった場合だけだと主張します。


整数ロジックと浮動小数点を混同する悪い類推です。

2

これは、配列がどのように構築されるかによるものです。1つから始めるのはあまり意味がありません。配列は、メモリ内のベースアドレス、サイズ、およびインデックスです。n番目の要素にアクセスするには、次のようにします。

base + n * element_size

したがって、0は明らかに最初のオフセットです。


1

これを実装するには、実際にはいくつかの異なる方法があります。

  • 0ベースの配列インデックス
  • 1ベースの配列インデックス
  • 0または1ベースの配列(VB 6.0など...これは本当に恐ろしいです)

最終的に、言語が0ベースの配列を使用するか1ベースの配列を使用するかは問題ではないと思います。しかし、ほとんどのプログラマーがその慣習に慣れているという単純な理由で、0ベースの配列を使用することが最良の選択だと思います。

ただし、本当にうまくいかない唯一の方法は、Visual Basicのように一貫性を保つことです。私が現在管理しているコードベースは、0〜1ベースの配列に分割されています。そして、どちらがどれであるかを把握することは非常に困難です。これは迷惑なほど冗長なforループにつながります。

dim i as integer, lb as integer, ub as integer
lb = LBound(array)
ub = UBound(array)
for i = lb to ub
       '...
next

笑私は、ことを吸い込ま男...覚えて
ダンRosenstark

負の数で始まる配列を持つことさえ覚えていると思います。私がVBから離れている多くの理由の1つにすぎません。

1

線形コレクション内のアイテムの場所について話すとき、ゼロは自然です。

本でいっぱいの棚を考えてください-最初の本は棚の側壁と同じ高さにあります-それは位置ゼロです。

したがって、配列インデックスを物事の発見または物事の参照の手段とみなすかどうかにかかっていると思います。


1

モジュロ(およびモジュロに使用される場合はAND演算子)がいくつかの値に対して常に0を返すため、0ベースのインデックスが好ましいです。

私はこのような配列を使用していることがよくあります。

int blah = array[i & 0xff];

1ベースのインデックスを使用すると、この種のコードが間違っていることがよくあります。


1

文字列検索やさまざまな並べ替え/マージアルゴリズムなどの配列ベースのコードを大量にプログラミングしたり、1次元配列の多次元配列をシミュレートしたりせずに、0ベースを守ることは困難です。Fortranは1ベースであり、この種のコードを正しく実行するには多くのコーヒーが必要です。

しかし、それはそれをはるかに超えています。要素のインデックスではなく、何かの長さについて考えることができるのは非常に有用な精神習慣です。たとえば、ピクセルベースのグラフィックスを実行する場合、座標をピクセル上ではなくピクセル間に入ると考える方がはるかに明確です。そのようにして、3x3の長方形には16ではなく9ピクセルが含まれます。

もう少し先取りされた例は、解析またはテーブルの小計の印刷における先読みのアイデアです。「常識的な」アプローチでは、1)次の文字、トークン、またはテーブル行を取得し、2)それをどう処理するかを決定します。先読みアプローチでは、1)表示できると仮定して、必要かどうかを判断し、2)必要な場合は「受け入れ」ます(次のものを表示できます)。その後、擬似コードを書き出すと、はるかに簡単になります。

さらに別の例は、MS-DOSバッチファイルなど、選択の余地のない言語で「goto」を使用する方法です。「常識的な」アプローチは、実行するコードのブロックにラベルを添付し、そのようにラベルを付けることです。多くの場合、コードブロックをスキップする目的で、コードブロックの最後にラベルを付けるのがより良いアプローチです。これにより、「構造化」され、変更がはるかに簡単になります。


1

それはまさにそうであり、長年にわたって行われてきました。変更したり、議論することは、信号機の変更や議論と同じくらい無意味です。blue = stop、red = goにしましょう。

C ++の数値レシピで時間の経過とともに加えられた変更を調べます。マクロを使用して1ベースのインデックス付けを偽造していましたが、2001年版ではあきらめて群れに加わりました。彼らのサイトwww.nr.comで、この背後にある理由に関する啓発資料があるかもしれません。

ところで、また迷惑なのは、配列の範囲を指定する変形です。例:python vs. IDL; a [100:200]対a [100:199]で100個の要素を取得します。各言語の癖を学ばなければなりません。他の言語と一致するように1つの方法で言語を変更すると、そのような歯の噛み合わせや歯ぎしりが発生し、実際の問題は解決されません。


1

他の人が述べたように、数学が簡単になるため、0ベースの配列が好きです。たとえば、10x10グリッドをエミュレートする1​​00個の要素の1次元配列がある場合、行r、列cの要素の配列インデックスiは次のようになります。

0ベース:i = 10 * r + c
1ベース:i = 10 *(r-1)+ c

そして、インデックスiが与えられ、行と列に戻ると:

0ベース:c = i%10
         r = floor(i / 10)
1ベース:c =(i-1)%10 + 1
         r = ceil(i / 10)

1ベースの配列を使用する場合、上記の計算が明らかに複雑になることを考えると、0ベースの配列を標準として選択することは論理的に思えます。

ただし、1D配列で2Dデータを表す理由があると想定しているため、誰かが私のロジックに欠陥があると主張できると思います。私はC / C ++で多くのそのような状況に遭遇しましたが、そのような計算を実行する必要があることは言語にある程度依存することを認めなければなりません。配列が常にクライアントに対してすべてのインデックス計算を本当に実行した場合、コンパイラはコンパイル時にMベースの配列アクセスを0ベースに変換し、これらの実装の詳細をすべてユーザーから隠すことができます。実際、コンパイル時定数を使用して同じ操作セットを実行できますが、そのような構成体はおそらく理解できないコードにつながる可能性があります。

おそらく、より良い議論は、1ベースの配列を持つ言語で配列インデックス操作の数を最小化するには、天井関数を使用して整数除算を実行する必要があるということでしょう。ただし、数学的な観点から、整数除算はd剰余rを返す必要があります。dとrは両方とも正です。したがって、数学を簡素化するために、0ベースの配列を使用する必要があります。

たとえば、N個の要素を含むルックアップテーブルを生成する場合、値xの配列への現在の値の前の最も近いインデックスは(概算、結果が丸め前の整数である値を無視します):

0から始まる床:floor((N-1)* x / xRange)
1から床まで:floor((N-1)* x / xRange)+ 1
ceilを使用した1ベース:ceil((N-1)* x / xRange)

切り捨ての標準規則が使用される場合、1ベースの配列には追加の操作が必要になることに注意してください。これは望ましくありません。この種の数学は、舞台裏で何が起こっているかについての低レベルの知識を必要とするため、コンパイラーによって隠すことはできません。


多次元配列をサポートする高レベルの言語ができるまで、それは正当な理由です。

1

プログラマーは、日々の思考で0ベースの配列の直感に反することに悩まされ、配列を記述するより直感的な手段を主張していたに違いない。皮肉なことに、人間として「クラス」を考え出すのに多くの時間を費やし、コードでより人間的な方法で物事を記述できるようになりましたが、0対1の配列を見るとハングアップしているように見えますそれの論理だけ。

コンピュータに関する限り、数学的には0のほうがおそらく良いでしょうが、ここでポイントを見逃していると感じています。物事をより人間的な方法(クラスなど)で記述したい場合、言語の他の部分でも同じようにしたくないのでしょうか?言語を人間にとってより理解しやすく使いやすいものにするために、それは等しく論理的でも有効でもありません(または優先順位を高くします...)使用可能な作成物をより迅速に作成します。PHPの例:

array(1 => 'January', 'February', 'March');

リクエストごとに1ベースの配列を提供します。

なぜ規範がないのか:

array('January', 'February', 'March');

そして例外は:

array(0 => 'Value for scenario where 0 *has* to be used as the key',
      'value2', 'value3');

PHPの場合、私の賭けは80%であり、デフォルトの構文ベースの1ベースの配列は、現実世界のユースケースで論理バグを減らすか、少なくとも平均して多くを引き起こさない使用可能なコードをより迅速に生成するためにコーダーで簡単に。覚えておいて、必要なときにarray(0 => 'value')のオプションがまだあると仮定しているが、ほとんどの場合、現実世界の記述により近いものが実用的であると仮定している。

これは、その観点からそれを見るとき、実際に要求を取得しすぎるとは思えません。インターフェースに近づくとき、OSであれプログラマーの言語であれ、人間の思考や習慣に近づけるほど、ほとんどの場合幸せになり、人間とコンピューターの間の誤解が少なくなります(人間の論理-バグ)、およびより高速な生産などがあります。現実世界の80%の時間でリストを作成したり数えたりするときに1で物事を説明する場合、コンピューターは、できるだけ少ない情報で、または可能な限り何かを説明する通常の方法から変更して、理解する方法に私の意味を理想的に解釈する必要があります。要するに、現実の世界をより密接にモデル化できるほど、抽象化の品質が向上します。だから彼が望んでいるのは、決して愚かではない。それが究極の目標であり、より抽象化が必要な証拠だからだ。コンピュータは最終的に、0ベースの配列の特別な使用として最終的にそれを見ることができます。時間が経つにつれてバグが少なくなり、自分が望むものをより簡単で直感的に説明できる方法である限り、コンピューターがどのように解釈するかを気にする必要がなくなります。

だから、それは私の2セントです。彼が何を言っていたのか、あるいは何が解釈されたのかは、彼が何を意味していたのか、真剣に疑っています。彼がおそらく意味していたのは、「コンピューターに自分が欲しいものを伝える直感的でない方法が嫌いだ」ということでした。:)私たち全員ですか?笑。


1

「自分の」コードを書く際に注意を払えば可能です。すべてのn> = 0について、インデックスがnから始まると想定し、それに応じてプログラムできます。

標準に関しては、Borealidには大きな議論があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.