Чому масиви на основі нуля є нормою?


112

Питання , поставлене тут нагадав мені про дискусії я мав з товаришем програмістом. Він стверджував, що масиви на основі нуля повинні бути замінені на масиви, що базуються на нулі, оскільки масиви, засновані на нулі, - це деталь реалізації, що бере початок від того, як працюють масиви та покажчики та апаратне забезпечення комп'ютера, але такі речі не повинні відображатися на більш високому рівні мови.

Зараз я не дуже хороший в дискусіях, тому я не міг запропонувати жодних вагомих причин дотримуватися масивів на основі нуля, окрім того, що вони вважають себе більш доречним. Чому це нульове загальна відправна точка для масивів?


У масиві n-елементів елемент 'n' відсутній. У масиві n-елементів є члени, які налічують лише від 0 до n-1. Отже, чи не краще, якщо у нас є масив, починаючи з 1, і тому масив n-елементів насправді представляє n елементів, наявних у масиві.

Мені подобаються масиви на основі нуля, оскільки перший біт у байті дорівнює 2 ^ 0, а не 2 ^ 1. Це мені іноді допомагає :)
e-MEE

Якщо дивитися на цей список, наприклад, en.wikipedia.org/wiki/… , ви помітите, що більшість мов, що задаються доменом, починають індексуватись з 1, а більшість мов із школи мислення cs з 0. Якщо шукати трохи довше , він помітить, що на цю тему було зроблено стільки дурних дискусій, що, мабуть, безглуздо намагатися поговорити будь-якого з двох, щоб змінити свій шлях. Захищаючи "1", більшість людей у ​​світі використовують саме цю. Більшість програмістів використовують "0". Більшість людей не розуміють програмістів, тож змушує задуматися ...
Rook

Я не можу повірити, що це питання було перенесено із StackOverflow на програмістів, а потім закрите як неконструктивне. Це німий.
paercebal

Відповіді:


105

Я не думаю, що хтось із нас може навести більш вагомий аргумент, ніж стаття Едсгера У. Дейкстри "Чому нумерація повинна починатися з нуля" .


Він зібрав деякі статистичні дані та використав доказ математичного стилю. Але я впевнений, що хтось все-таки міг би сперечатися. Хоча я схвалюю, так і не став би.

6
Стаття Dijkstra стосується стилю, але потім, його аргументи - про простоту та простоту використання ... +1.
paercebal

80

Аргумент влади

Ну ... Мабуть, більшість мов, включаючи дуже недавні, покладені на нуль. Оскільки ці мови були написані досить кваліфікованими людьми, ваш друг повинен помилитися ...

Чому один?

чому 1 буде кращим початковим індексом, ніж нульовим? Чому б не 2, або 10? Сама відповідь цікава, бо багато в чому свідчить про процес хоч людей, які захищають цю ідею.

Перший аргумент в тому , що це більш природно, тому що перший , як правило, один перед усіма іншими, по крайней мере, для більшості людей ...

Аргумент номер один полягає в тому, що останній індекс - це також розмір масиву ...

Мене все ще вражає "якість" причин, які я зазвичай чую для такого роду аргументів ... І навіть більше, коли мені це нагадують ...

Чому б не нуль?

... Однозначні позначення - це залишені у західній культурі, які ігнорували існування нуля протягом століть, якщо не більше.

Вірите чи ні, оригінальний григоріанський календар походить від -3, -2, -1, 1, 2, 3 ... Спробуйте уявити проблему, яку він сприяв західній науці (наприклад, скільки років з 1 січня -2 до 1 січня, щоб побачити, ніж оригінальний григоріанський календар суперечить чомусь такому простому, як субстракція ...).

Дотримуватися масивів, що базуються на одній основі, це як (ну, я буду перешкоджати цьому ... ^ _ ^ ...), зберігаючи милі та ярди в 21 столітті ...

Чому нуль? Бо це математика!

Спочатку (OOops ... Вибачте ... я спробую ще раз)

Нуль , нуль - це ніщо, одне - це щось. І деякі релігійні тексти стверджують, що "На початку нічого не було". Деякі дискусії, пов'язані з комп'ютером, можуть бути настільки ж пекучими, як і релігійні дебати, тому ця тема не настільки поза темою, як здається ... ^ _ ^

По-перше , простіше працювати з масивом на основі нуля і ігнорувати його нульове значення, ніж працювати з одноосновним масивом і хакнути навколо, щоб знайти його нульове значення. Ця причина настільки ж нерозумна, як і попередня, але тоді оригінальний аргумент на користь масивів, що базуються на одній основі, теж був помилковим.

По-друге , згадаймо, що, маючи справу з числами, велика ймовірність, що ти будеш займатися математикою в той чи інший момент, а коли ти займаєшся математикою, швидше за все, ти не в настрої для дурних хакерів обійти застарілі умовності. Нотація, що базується на одній, також століттями мучила математику та дати, і, вивчаючи свої помилки, ми повинні прагнути уникати цього в майбутніх орієнтованих науках (включаючи комп'ютерні мови).

По-третє , що стосується масивів комп'ютерної мови, прив'язаних до обладнання, виділіть масив C з 21 цілим числом і перемістіть вказівник 10 індексів праворуч, і у вас буде природний масив [-10 до 10]. Це не природно для апаратних засобів. Але це для математики. Звичайно, математика могла бути застарілою, але востаннє, коли я перевіряв, більшість людей у ​​світі вважали, що це не так.

По-четверте , Як уже зазначалося в іншому місці, навіть для дискретного положення (або відстані, зменшеного до дискретних значень), перший показник буде нульовим, як і підлога в будівлі (починаючи з нуля), зменшуючи зворотний відлік (3, 2, 1, ZERO !), висота землі, перший піксель зображення, температура (нуль Кельвіна, за абсолютний нуль або нуль градусів Цельсія, як температура замерзання води 273 К). Насправді, єдине, що насправді починається з одного, - це традиційний спосіб " першого , другого , третього тощо". нотація ітерації , яка природно призводить мене до наступного пункту ...

П'ять наступний пункт (який , природно , слід за попередній ) є те , що контейнери високого рівня повинні бути доступні, а не по індексу, а ітератори , якщо індекси самі не мають неминущу цінність. Я здивований, що твій захисник "мови вищого рівня" про це не згадав. У випадку, якщо сам індекс важливий, ви можете зробити ставку вдвічі, коли маєте на увазі питання, пов'язані з математикою. І таким чином, ви хочете, щоб ваш контейнер був зручним для математики, а не з обмеженими фізичними можливостями, як "календар вашого старого григоріанця", починаючи з 1, і вам потрібні перероблені хаки, щоб він працював.

Висновок

Аргумент, поданий вашим колегою-програмістом, є помилкою, оскільки він непотрібно пов’язує розмовні / письмові мовні звички, які, за своєю природою, розмиті, до комп'ютерних мов (там, де ви не хочете, щоб ваша інструкція була розмитою), і тому, що неправильно привласнювати обладнання Причиною цієї проблеми він сподівається переконати вас, як мови йдуть все вище і вище в абстракції, що масив на основі нуля - це минуле.

Нульові масиви базуються на нулі через причини, пов'язані з математикою. Не з апаратних причин.

Тепер, якщо це проблема для вашого колеги-програміста, запропонуйте йому почати програмувати реальні конструкції високого рівня, як ітератори та цикли передбачення.


нульовий градус градуса - 273,15К;)

2
Я знаю (у мене є магістерський диплом з фізики), але я відчував, що грати з десятковою коробкою не менше, ніж жартівлива сторона, з якою я намагався розфарбувати свої аргументи ... ^ _ ^ ...
paercebal

20
Ваші абзаци позначені "Нуль, перший, другий, третій, чотири, п'ять". Для послідовності вам слід використовувати або порядкові числа ("Нуль, Один, Два, Три, Чотири, П'ять") або порядкові числа ("Зерот, Перший, Другий, Третій, Четвертий, П'ятий"). :-)
ShreevatsaR

10
Аналогічно, перший рік нашого життя нам не один рік, а нуль років

3
@Nikita Rybak: Дивно, що ти пропустив те, що бачили всі коментатори перед тобою: Звичайно, відповідь Білла Ящера - правильна. Ось чому я проголосував за нього +1, і саме тому він був обраний як найкраща відповідь на питання. Моя відповідь більше стосується того, щоб висміяти помилкові причини, що стоять за масивами на основі 1, і запропонувати конкретні випадки, коли масив на основі 1 буде неприємністю. І все-таки я здивований, що ти знайшов "жодного не переконливого", навіть вважаючи, що причини змішуються з іронією ...
paercebal

47

Напіввідкриті інтервали добре складаються. Якщо ви маєте справу з 0 <= i < limі хочете розширити на nелементи, нові елементи мають індекси в діапазоні lim <= i < lim + n. Робота з масивами на основі нуля робить арифметику простішою при розбитті або об'єднанні масивів або під час підрахунку елементів . Можна сподіватися, що простіша арифметика призводить до меншої кількості помилок на огорожі .


+1 для напіввідкритих інтервалів - це просто полегшує все.
Затемнення

29

Деякі типи маніпуляцій з масивами стають божевільними, ускладнюючись масивами на основі 1, але залишаються більш простими з масивами на основі 0.

Я зробив декілька програм чисельного аналізу в один момент. Я працював з алгоритмами для маніпулювання стислими, розрідженими матрицями, записаними як у FORTRAN, так і в C ++.

Алгоритмів FORTRAN було багато a[i + j + k - 2], тоді як C ++ a[i + j + k], тому що масив FORTRAN був заснований на 1, а масив C ++ - на 0.


Я згоден. Єдиний момент, коли я вважаю масив на основі 1 корисним - це коли я хочу звільнити індекс з нульовим елементом. Наприклад, якщо я маю масив об'єктів і використовую їх індекси як ручки, і хочу мати нульову ручку.
Фабіо Чеконелло

Я також потрапив у зайве ускладнення масивів на основі 1, масиви на основі 0, за моїм обмеженим досвідом, завжди створювали більш чіткий код для індексації масивів за рідкісним винятком.

Як би індекси FORTRAN та C ++ відрізнялися на 2, якщо їхні відповідні індекси зміщені лише на 1? Також, чому мінус 2? Якщо FORTRAN базується на 1, то чи не додали б ви 2 (або 1)?
RexE

@RexE: Ось так це працює, і тому це так складно з масивами на основі 1.
Джей Базузі

@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

Поки ми працюємо над цим питанням: чи використовуєте ви коли-небудь код низького рівня неплатформовим способом? Іншими словами, ви завжди знаходитесь на тій чи іншій платформі, і ви повинні знати, яка, правда?
Дан Розенстарк

1
Оскільки хтось, хто думає, що VB .NET зазвичай несправедливо зловживається, я мушу сказати, що практика VB .NET на масивах є жахливою. Вони розділили різницю і зробили її ще більш заплутаною: масиви починаються з 0, але Dim a як Integer (5) створює масив з 6 позицій. Обґрунтуванням здавалося, що мати додаткову позицію було б краще, ніж помилки з адресацією повз довжину масиву. На жаль, з цього приводу (та інших питань, таких як "І" або "Побіт") вони підтягнулися до вимог багатьох програмістів VB6, які все-таки не використовували VB .NET.
Kyralessa

1
@Kyralessa: Ні, обгрунтування полягало в тому, щоб мати зворотну сумісність з VB6 (помічник автоматичного оновлення…), хоча вони добре розуміли, що позначення є контрінтуїтивними та схильними до помилок. З іншого боку, Andі Orпобітне значення не має нічого спільного з VB6, це єдине логічне рішення для мови типу VB. Ви дійсно є AndAlsoі OrElseдля логічних операцій.
Конрад Рудольф

Andі Orбути побитним має все спільне з VB6, тому що вони були побитними в VB6. Потворні оператори AndAlsoі OrElseповинні бути зроблені побіжно, оскільки бітові операції набагато рідше, ніж логічні. На мові залишилось багато таких некрасивих бородавок, завдяки "зворотній сумісності", наприклад, тому, що ByVal склеюється скрізь, хоча це за замовчуванням.
Kyralessa

10

Масиви на нульовій основі мають коріння в C і навіть асемблері. З C математика вказівника в основному працює так:

  • Кожен елемент масиву займає певну кількість байт. 32-бітове ціле число (очевидно) 4 байти;
  • Адресу масиву займає перший елемент масиву з подальшими елементами в суміжних блоках однакового розміру після цього.

Для ілюстрації, припустимо, що int a[4]це 0xFF00, адреси:

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

Отже, з нульовими індексами математика адресів проста:

Адреса елемента = Адреса масиву + індекс * sizeof (тип)

Насправді вирази в C всі еквівалентні:

  • a [2];
  • 2 [а]; і
  • * (a + 2).

З масивами, що базуються на одній основі, математика (все одно) трохи складніше.

Тож причини багато в чому історичні.


1
У початковому запитанні вже зазначено, що "масиви, засновані на нулі, - це детальна інформація про реалізацію, яка бере свій початок від того, як працюють масиви та покажчики та апаратне забезпечення комп'ютера, але такі речі не повинні відображатися на мовах вищого рівня".

Варто згадати, що мови, які дозволяють N-масиви, як правило, генерують код із масивом 'offsets' автоматично обчислюється при нульовій вартості виконання.
Родді

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. З урахуванням індексу я , індекс я «батько і дитина знаходяться зліва

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 ]).

Зрештою, це не має значення, адже ви можете зробити масив на основі 0 в масив на основі 1, виділивши ще один елемент, ніж звичайний, і ігноруючи нуль. Таким чином, ви можете увімкнути, щоб отримати переваги масивів на основі 1, коли це доречно, і зберегти масиви на основі 0 для більш чистої арифметики майже у всіх інших ситуаціях.


4

Моє відчуття, що це абсолютно довільно. Немає нічого особливого в масивах, що базуються на нулі або на одній основі. З моменту звільнення себе від Visual Basic (в основному іноді я роблю крихітні речі в Excel), я не працював з масивами на основі 1, і ... це те саме. Справа в тому, що якщо вам потрібен третій елемент масиву, це лише деталь реалізації, яку він називає 3 або 2. Однак 99% роботи, яку ви виконуєте з масивами, цікавлять лише дві абсолютні точки: перший елемент і кількість або довжина. Знову ж таки, це просто деталь реалізації, що перший елемент називається нулем замість одного, або що останній елемент називається count-1 або, замість цього, count.

Редагувати: Деякі з відповідей згадували, що масиви на основі 1 більш схильні до помилок на огорожі. З мого досвіду, думаючи про це зараз, це правда. Я пам’ятаю, що в VB думав, що "це буде або працювати, або вибухне, тому що я одній." На Java цього ніколи не буває. Хоча я думав, що я стає кращим, деякі відповіді вказують на випадки, коли масиви на основі 0 приводять до кращої арифметики, навіть якщо вам не доведеться мати справу з язиком нижчого рівня.


У PHP більшість пошуку всередині рядкової функції повертають FALSE на не знайдено. Не -1.
jmucchiello

Ви плутаєте рахунок з індексом останнього елемента. Кількість порожнього масиву завжди дорівнює 0, незалежно від того, використовуєте ви масив на основі нуля або на основі одного. Перевага масивів, що базуються на одній основі, полягає в тому, що підрахунок - це позиція останнього елемента (але це приблизно єдина перевага).

Що стосується обох цих точок: остання половина видалена, оскільки вона однакова для масивів на основі нуля або на основі нуля: підрахунок дорівнює 0, якщо у вас є нульові елементи.
Дан Розенстарк

Я мав на увазі останню половину своєї відповіді ...
Ден Розенштарк

4

Як 10 + рік C / C ++ програміст, маючи дуже сильний досвід роботи у Паскалі та Дельфах, я все ще сумую за сильним переглядом масиву Паскаля та індексом, а також гнучкістю та безпекою, що поставляється із ним. Очевидним прикладом цього є значення масиву даних, що містять дані для кожного місяця.

Паскаль:

 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;

Написання подібного коду на мовах C без використання +/- 1 або "магічних чисел" є досить складним завданням. Зауважте, що такі вирази, як Days [2] та Days [Jan + Dec], просто не складатимуться, що може здатися жорстоким людям, які все ще думають на C або Assembler.

Я мушу сказати, що існує багато аспектів мов Pascal / Delphi, які я трохи не пропускаю, але масиви на основі нуля на основі C здаються просто "німими" порівняно.


Може бути , варто відзначити , що ваш алгоритм не є правильним для 2100. року en.wikipedia.org/wiki/Leap_year#Algorithm

2
Я знаю ;-) Однак це було правильним для 2000 року. Я просто граю в "spot the pedant" ...
Roddy

Знайдіть педант! ЛОЛ.
jcollum

Так. Уникайте всього питання, базуйте масив, як би ви хочете.
Лорен Печтел

Я не здивуюсь, якщо ваш середній компілятор Pascal призначає січень = 0, грудень = 11 при генерації машинного коду :-)

4

Причина, що починається з 0, а не 1, полягає в тому, що ви можете подумати про зміщення як про те, наскільки далеко від початку пам’яті масиву знаходиться цей елемент. Це не означає дати мені 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, вам потрібно буде постійно додавати його.

І вам іноді потрібно обробляти шматки даних у масивах, навіть мовою без "справжньої" арифметики вказівника. У Java ви можете мати дані у файлах, відображених у пам'яті, або в буферах. У такому випадку ви знаєте, що блок i знаходиться в розмірі * i. З індексом на основі 1 це було б у блоці * 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];

Зауважте, що ваші y та z індекси базуються на 0 (y - 1, z - 1), навіть якщо ваш масив заснований на 1. За певних обставин не можна уникнути індексів на основі 0. Для послідовності, чому б не завжди використовувати індекси на основі 0?


3

Чому ви хочете, щоб масиви починалися з одного?

Коли ви говорите a[x][y], компілятор переводить це в: a+(x*num_cols+y). Якби масиви починалися одразу, це стане a+(x*num_cols+y-1). Це була б додаткова арифметична операція кожного разу, коли ви хочете отримати доступ до елемента масиву. Чому ви хочете уповільнити програми?


1
насправді це повинно стати + ((x - 1) * num_cols) + y - 1) - і x, і y почнеться з 1.
Dennis Munsie

2

Я збираюся вийти на кінцівку тут і запропонувати щось інше, ніж цілий масив "набору ключів".

Я думаю, що ваш колега задумається створити відображення «набору» у фізичному світі, де ми завжди починаємо рахувати з 1. Я можу це зрозуміти, коли ви нічого не фантазуєте, легко зрозуміти якийсь код коли вам відображено 1 до 1 між програмним забезпеченням та фізичним світом.

Моя пропозиція

Не використовуйте масиви на основі цілого числа для того, що ви зберігаєте, але використовуйте інший тип словника чи пару ключових значень. Ці карти краще в реальному житті, оскільки ви не пов'язані довільним цілим числом. Це має своє місце, і я б рекомендував використовувати його якомога більше, оскільки ви можете спричинити відображення понять 1 до 1 між програмним забезпеченням та фізичним світом.

тобто kvp['Name Server'] = "ns1.example.com"; (це лише один із мільйона можливих прикладів).

Discaimer

Це, безумовно, не працює, коли ви працюєте з концепціями, заснованими на математиці, в основному тому, що математика ближче до реальної реалізації комп’ютера. Використання наборів kvp тут нічого не допоможе, але насправді зіпсує речі та зробить це більш проблематичним. Я не продумав усіх кутових випадків, коли щось може працювати краще як kvp або як масив.

Кінцева ідея полягає у використанні нульових масивів або пар ключових значень там, де це має сенс, пам’ятайте, що коли у вас є лише молоток, кожна проблема починає виглядати як цвях ...


2

Особисто один аргумент - це коли індексні масиви розглядаються як компенсації. Це просто має сенс.

Можна сказати, що це перший елемент, але зміщення першого елемента відносно початку масиву дорівнює нулю. Таким чином, беручи початок масиву і додаючи нуль, вийде перший елемент.

Тож при обчисленні простіше додати нуль, щоб знайти перший елемент, ніж додати його, а потім видалити його.

Я думаю, що кожен, хто робив якісь речі нижчого рівня, завжди думає, що базовий нульовий шлях. І люди, які починають або звикли до вищого рівня, часто не алгоритмічне програмування, можуть побажати базової системи. А може, ми просто упереджені минулим досвідом.


Саме так - це в основному умова, що лежить навколо мов нижчого рівня.

2

Єдині дві (дуже) серйозні причини використання індексів на основі 0 замість індексів на основі 1, схоже, уникають повторного зменшення кількості програмістів І для зворотної сумісності .

В усіх відповідях, які ви отримали, я не бачив жодних серйозних аргументів проти індексів на основі 1.

Насправді індекси, природно, базуються на 1 , і ось чому.

По-перше, ми повинні запитати: чи беруться масиви? Чи мають у них реальні еквіваленти? Відповідь так: саме вони моделюють вектори та матрицю в інформатиці. Однак Вектори і матриця - це поняття математики, які використовували індекси на основі 1 до епохи комп'ютера (і до цих пір здебільшого використовують індекси на основі 1).

У реальному світі індекси - це 1-бази.

Як сказав Томас вище, мови, які використовували 0-базисні індекси, насправді використовують зсуви , а не індекси. І розробники, які використовують ці мови, думають про компенсації , а не про індекси. Це не було б проблемою, якби речі були чітко прописані, але це не так. Дуже багато розробників, які використовують компенсації, все ще говорять про індекси. І багато розробників, які використовують індекси, досі не знають, що C, C ++, C #, ... використовують компенсації.

Це проблема формулювання .

(Примітка до статті Diskstra. У ній сказано саме те, про що я говорив вище : математики використовують індекси на основі 1. Але Diskstra вважає, що математики не повинні використовувати їх, оскільки якийсь вираз буде тоді некрасивим (наприклад: 1 <= n <= 0 ). Ну, не впевнений, що він правий на цьому - робити таку зміну парадигми, щоб уникнути цих виняткових порожніх послідовностей, здається, багато клопоту для невеликого результату ...)


2
Математики не завжди використовують індекси на основі 1. Я бачив x0 багато разів використовувався для початкового значення послідовності. Це залежить від того, що зручніше.

2

Вас коли-небудь дратувало "20 століття", яке насправді стосується 1900-х років? Що ж, це хороша аналогія для виснажливих речей, з якими ви постійно працюєте під час використання масивів на основі 1.

Розглянемо загальну задачу масиву, як метод читання .net IO.stream:

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

Ось що я пропоную вам зробити, щоб переконати себе, що масиви на основі 0 краще:

У кожному стилі індексації напишіть клас BufferedStream, який підтримує читання. Ви можете змінити визначення функції Read (наприклад, використовувати нижню межу замість зміщення) для масивів на основі 1. Не потрібно нічого фантазії, просто зробіть це просто.

Тепер, яка з цих реалізацій є простішою? У якому з них тут і там посипаються +1 та -1 компенсації? Це те, що я думав. Насправді я б стверджував, що єдиний випадок, коли стиль індексації не має значення, це коли ви мали використовувати щось, що не було масивом, як набір.


Це погана аналогія, що плутає цілу логіку з плаваючою точкою.

2

Це тому, як будуються масиви. Починати з одного не має великого сенсу. Масив - це базова адреса в пам'яті, розмірі та індексі. Для доступу до n-го елемента:

base + n * element_size

Тож 0 очевидно є першим зміщенням.


1

Насправді існує кілька різних способів цього здійснити:

  • Індекси масиву на основі 0
  • Індекси масиву на основі 1
  • або масиви на основі 0, або 1 (наприклад, VB 6.0 ... це справді жахливо)

Зрештою, я не думаю, що має значення, чи використовує мова масиви на основі 0 або 1. Але я думаю, що найкращим вибором є використання масивів на основі 0, з тієї простої причини, що більшість програмістів звикли до цієї конвенції, і це відповідає переважній більшості вже написаних кодів.

Однак єдиний спосіб, коли ви можете дійсно помилитися, - це бути непослідовним, як Visual Basic. База коду, яку я зараз підтримую, розділена на масиви на основі 0 та 1; і надзвичайно важко зрозуміти, що це таке. Це призводить до дратівливого багатослівного циклу:

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

ха-ха, я пам’ятаю це, людина, яка смоктала ...
Ден Розенстарк,

Я думаю, що пам’ятаю навіть наявність масивів, починаючи з від’ємних чисел. Просто одна з багатьох причин я тримаюсь подалі від VB.

1

Нуль природно, коли йдеться про розташування предмета в лінійній колекції.

Подумайте про полицю, наповнену книгами - перша книга розташована нарівні з бічною стінкою полиці - це місце нульове.

Тож я думаю, це залежить від того, чи вважаєте ви індекси масиву засобом пошуку речей або посиланням на речі.


1

Я віддаю перевагу індексу на основі 0, оскільки оскільки модуль (і оператор AND, коли використовується для модуля) завжди повертає 0 для деяких значень.

Я часто опиняюся таким чином, використовуючи такі масиви:

int blah = array[i & 0xff];

Я часто помиляюся з таким кодом, коли використовую 1 індекс на основі.


1

Важко захистити 0-базу без програмування великого коду на основі масиву, такого як пошук рядків та різних алгоритмів сортування / злиття або моделювання багатовимірних масивів в одновимірному масиві. Fortran має 1 основу, і вам потрібно багато кави, щоб правильно зробити такий код.

Але це виходить за рамки цього. Дуже корисна психічна звичка вміти думати про довжину чогось, а не про показники його елементів. Наприклад, роблячи графіку на основі пікселів, набагато чіткіше мислити координати як попадання між пікселями, а не на них. Таким чином, прямокутник 3x3 містить 9 пікселів, а не 16.

Трохи більш надуманим прикладом є ідея перспективного розбору або роздрукування підсумків у таблиці. Підхід "здорового глузду" говорить: 1) отримати наступний символ, маркер або рядок таблиці та 2) вирішити, що з ним робити. Підхід, що дивиться вперед, говорить: 1) припустимо, що ви можете його бачити, і вирішіть, чи хочете ви його, і 2) якщо ви цього хочете, "прийміть" його (що дозволяє бачити наступний). Тоді якщо ви випишете псевдо-код, це набагато простіше.

Ще один приклад - як використовувати "goto" мовами, де у вас немає вибору, наприклад, пакетні файли MS-DOS. Підхід "здорового глузду" полягає в прикріпленні міток до блоків коду, які необхідно виконати, і позначення їх як таких. Часто кращим підходом є нанесення міток на кінці блоків коду з метою пропускання їх. Це робить його "структурованим" і набагато легше змінювати.


1

Просто так і є вже багато років. Змінювати його або навіть обговорювати його так само безглуздо, як і змінювати або обговорювати зміни світлофорів. Зробимо синій = стоп, червоний = підемо.

Подивіться на зміни, внесені з часом у числові рецепти для C ++. Вони використовували макроси для підробленої індексації на основі 1, але в випуску 2001 року відмовилися і приєдналися до стада. На їхньому веб-сайті www.nr.com може бути ознайомлений матеріал з причин цього

До речі, також дратують варіанти вказівки діапазону поза масивом. Приклад: python vs. IDL; a [100: 200] проти a [100: 199], щоб отримати 100 елементів. Просто треба вивчити химерність кожної мови. Зміна мови, яка робить один спосіб узгодження з іншим, спричинить такі зубри і скрегіт зубів, а не вирішить жодної реальної проблеми.


1

Я віддаю перевагу масивам на основі 0, тому що, як зазначають інші, це полегшує математику. Наприклад, якщо у нас є одновимірний масив із 100 елементів, що емулюють сітку 10х10, то що таке індекс масиву i елемента в рядку r, col c:

На основі 0: i = 10 * r + c
На основі 1: i = 10 * (r - 1) + c

І, враховуючи індекс i, повернення до рядка та стовпця:

На основі 0: c = i% 10
         r = підлога (i / 10)
На основі 1: c = (i - 1)% 10 + 1
         r = стеля (i / 10)

Зважаючи на те, що математика, наведена вище, явно складніша при використанні масивів на основі 1, здається, логічно вибрати масиви на основі 0 як стандарт.

Однак я думаю, що хтось міг би стверджувати, що моя логіка є помилкою, оскільки я припускаю, що було б підстави представляти 2D дані в 1D масиві. Я зіткнувся з низкою таких ситуацій в C / C ++, але мушу визнати, що необхідність виконувати такі обчислення дещо залежить від мови. Якщо масиви справді виконували всю математику індексу для клієнта, весь час, компілятор може просто перетворити ваш доступ до масиву на основі М на час 0 компіляції та приховати всі ці відомості про реалізацію від користувача. Насправді, будь-яка константа часу компіляції може бути використана для виконання одного і того ж набору операцій, хоча такі конструкції, ймовірно, призводять до незрозумілого коду.

Можливо, кращим аргументом буде те, що мінімізація кількості операцій з індексом масиву на мові з масивами на основі 1 вимагатиме, щоб ціле поділ виконувались за допомогою функції стелі. Однак з математичної точки зору ціле ділення повинно повернути d залишок r, де d і r позитивні. Тому для спрощення математики слід використовувати масиви на основі 0.

Наприклад, якщо ви генеруєте таблицю пошуку з N елементами, найближчим індексом до поточного значення в масиві буде значення x (приблизно, ігноруючи значення, де результат є цілим числом до округлення):

0 на основі підлоги: підлога ((N - 1) * x / xRange)
1-основа з підлогою: підлога ((N - 1) * x / xRange) + 1
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'), коли це потрібно, але також припускаючи, що більшість часу це практично, щоб щось було ближче до реального опису світу.

Це дійсно не здається занадто далеко отриманим запитом, якщо дивитися на нього з тієї точки зору. Коли ми підходимо до інтерфейсу, будь то ОС або мова для програміста, чим ближче до мислення людини і звичок ми його розробляємо, тим щасливішими ми будемо в більшості випадків і тим менше непорозумінь між людиною та комп'ютером (людська логіка- помилки), і швидше виробництво тощо у нас буде. Якщо 80% часу в реальному світі я описую речі з 1 при складанні списків або підрахунку, то комп'ютер в ідеалі повинен інтерпретувати мій сенс таким чином, як він розуміє, якомога менше інформації або зміниться від мого звичайного способу описати щось як можна. Коротше кажучи, чим ближче ми зможемо моделювати реальний світ, тим краще якість абстракції. Тож те, що він хоче, аж ніяк не дурне, оскільки це є кінцевою метою і є свідченням необхідності більшої абстракції. Комп'ютер все ще може сприймати це як особливе використання масиву на основі 0. Мені було б менше байдужесь, як комп'ютер інтерпретує це, поки це простіший та інтуїтивніший спосіб описати, що я хочу йому, з меншими помилками з часом.

Отже, це мої два центи. Я серйозно сумніваюся в тому, що він говорив, або те, що його тлумачили, це те, що він мав на увазі. Напевно, він мав на увазі: «Я ненавиджу менш інтуїтивний спосіб сказати комп’ютеру, що я хочу». :) Чи не всі ми? Лол.


1

Це можливо, якщо ви подбаєте під час написання "власного" коду. Ви можете припустити, що ваш індекс починається від n для всіх n> = 0 і програмувати відповідно.

Щодо стандарту, у Borealid є великий аргумент.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.