Почему нулевые массивы являются нормой?


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». Большинство людей не понимают программистов, так что это заставляет вас думать ...
Ладья

Я не могу поверить, что этот вопрос был перенесен из StackOverflow в Программисты, а затем закрыт как неконструктивный. Это глупо.
paercebal

Ответы:


105

Я не думаю, что кто-либо из нас может привести более сильный аргумент, чем статья Эдсгера В. Дейкстры «Почему нумерация должна начинаться с нуля» .


Он поднял некоторые характеристики и использовал доказательство в математическом стиле. Но я уверен, что кто-то еще может поспорить. Хотя я одобряю, поэтому я бы не стал.

6
Статья Дейкстры о стиле, но его аргументы о простоте и простоте использования ... +1.
paercebal

80

Авторитетный аргумент

Ну ... По-видимому, большинство языков, в том числе и совсем недавние, основаны на нулях. Поскольку эти языки были написаны довольно опытными людьми, ваш друг, должно быть, ошибается ...

Почему один?

почему 1 будет лучшим начальным индексом, чем ноль? Почему не 2 или 10? Сам ответ интересен, потому что он многое показывает о том, как люди защищают идею.

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

Аргумент номер один в том, что последний индекс также является размером массива ...

Я все еще впечатлен "качеством" причин, которые я обычно слышу для такого рода аргументов ... И даже больше, когда мне напоминают, что ...

Почему не ноль?

... «Основанные на одном» нотации - это пережитки западной культуры, которые игнорировали существование нуля на протяжении веков, если не больше.

Хотите верьте, хотите нет, оригинальный григорианский календарь исходит из -3, -2, -1, 1, 2, 3 ... Попробуйте представить проблему, которую он внес в западную науку (например, сколько лет с 1 января -2 до 1 января 2, чтобы увидеть, чем оригинальный григорианский календарь конфликтует с чем-то простым, например, вычитанием ...).

Хранение в одноосновных массивах похоже на то, что (ну, я буду за это понижен ... ^ _ ^ ...), в милях и ярдах в 21 веке ...

Почему ноль? Потому что это математика!

Первый (Ой ... Извините ... Я попробую еще раз)

Ноль , Ноль - это ничто, один - это нечто. И некоторые религиозные тексты утверждают, что «в начале ничего не было». Некоторые компьютерные дискуссии могут быть такими же горячими, как и религиозные дебаты, так что этот вопрос не так уж и неуместен, чем кажется ... ^ _ ^

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

Во-вторых , давайте помнить, что при работе с числами высоки шансы, что вы будете иметь дело с математикой в ​​тот или иной момент, а когда вы имеете дело с математикой, шансы хорошие, у вас нет настроения для глупых хаков, чтобы обойти устаревшие соглашения. Однозначное обозначение преследует и математику, и даты на протяжении веков, и, учась на наших ошибках, мы должны стремиться избегать этого в науках, ориентированных на будущее (включая компьютерные языки).

В-третьих , что касается массивов компьютерных языков, привязанных к аппаратному обеспечению, выделите массив C из 21 целого числа и переместите указатель на 10 индексов вправо, и у вас будет естественный массив [от -10 до 10]. Это не естественно для оборудования. Но это для математики. Конечно, математика может быть устаревшей, но в последний раз, когда я проверял, большинство людей в мире считали, что это не так.

В-четвертых , как уже указывалось в другом месте, даже для дискретной позиции (или расстояний, сокращенных до дискретных значений), первый индекс будет нулевым, как этаж в здании (начиная с нуля), уменьшающийся обратный отсчет (3, 2, 1, НОЛЬ !), высота земли, первый пиксель изображения, температура (ноль Кельвин, для абсолютного ноля или ноль градусов Цельсия, при температуре замерзания воды 273 К). Фактически, единственное, что действительно начинается с одного, - это традиционный способ « первый , второй , третий и т. Д.». итерационная запись, которая естественным образом подводит меня к следующему пункту ...

Пять следующий пункт (который , естественно , следует за предыдущий ) является то , что контейнеры высокого уровня должны быть доступны, а не по индексу, а итераторы , если индексы сами не имеют непреходящую ценность. Я удивлен, что ваш адвокат «на более высоком уровне» не упомянул об этом. В случае, если важен сам индекс, вы можете поспорить, что половина времени у вас есть вопросы, связанные с математикой. И, таким образом, вы хотели бы, чтобы ваш контейнер был дружественным по отношению к математике, а не по математике, как «ваш старый григорианский календарь», начиная с 1, и нуждался в отрыжках, чтобы заставить его работать.

Заключение

Аргумент вашего коллеги-программиста - заблуждение, потому что он без необходимости привязывает привычки разговорного / письменного языка, которые по своей природе являются размытыми, к компьютерным языкам (где вы не хотите, чтобы ваши инструкции были размытыми), а также потому, что ошибочно присваиваете оборудование Причину этой проблемы он надеется убедить, так как языки становятся все выше и выше в абстракции, что массив с нулями остается в прошлом.

Основанные на нуле массивы основаны на нулях по математическим причинам. Не по аппаратным причинам.

Теперь, если это проблема для вашего коллеги-программиста, пусть он начнет программировать с настоящими высокоуровневыми конструкциями, такими как итераторы и циклы foreach.


ноль градусов по Цельсию - 273,15K;)

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: Предположим, вы эмулируете трехмерный массив с плоским. Тогда в 0 base элемент (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 as 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 эквивалентны:

  • а [2];
  • 2 [а]; а также
  • * (А + 2).

С массивами, основанными на одном, математика немного сложнее.

Так что причины в значительной степени исторические.


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)

* р совпадает с * (р + 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

И тогда у вас есть свойство, что я также размер подмассива для этого индекса (то есть индексы в диапазоне [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 ++, с большим опытом работы в 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 или «магических чисел» является довольно сложной задачей. Обратите внимание, что такие выражения, как Days [2] и Days [Jan + Dec] просто не будут компилироваться, что может показаться жестоким людям, которые все еще думают на C или Assembler.

Я должен сказать, что есть много аспектов языков Pascal / Delphi, которые я не пропускаю ни разу, но массивы, основанные на C, кажутся просто «тупыми» по сравнению.


Возможно, стоит отметить, что ваш алгоритм неверен для 2100 года. En.wikipedia.org/wiki/Leap_year#Algorithm

2
Я знаю ;-) Тем не менее, это было правильно для 2000 года. Я просто играю "Найди педанта" ...
Родди

Найди педанта! ЛОЛ.
jcollum

Да. Избегайте всей проблемы, основывайте массив так, как вы хотите.
Лорен Печтел

Я не удивлюсь, если ваш средний компилятор Pascal назначит Jan = 0, Dec = 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.
Деннис Манси

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, посыпанные здесь и там? Это то, о чем я думал. На самом деле, я бы сказал, что единственные случаи, когда стиль индексации не имеет значения, это когда вы должны были использовать что-то, что не было массивом, например, Set.


Это плохая аналогия, путающая целочисленную логику с плавающей точкой.

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-base без программирования большого количества кода на основе массива, такого как поиск строк и различные алгоритмы сортировки / слияния, или моделирования многомерных массивов в одномерном массиве. Fortran основан на 1, и вам нужно много кофе, чтобы сделать этот код правильно.

Но это выходит далеко за рамки этого. Это очень полезная умственная привычка, чтобы иметь возможность думать о длине чего-либо, а не о показателях его элементов. Например, при работе с пиксельной графикой гораздо яснее думать о координатах как о попадающих между пикселями, а не на них. Таким образом, прямоугольник 3х3 содержит 9 пикселей, а не 16.

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

Еще один пример - как использовать «goto» на языках, где у вас нет выбора, таких как командные файлы MS-DOS. Подход "здравого смысла" состоит в том, чтобы прикреплять метки к блокам кода, которые должны быть выполнены, и маркировать их как таковые. Часто лучшим подходом является размещение меток на концах блоков кода, чтобы пропустить их. Это делает его «структурированным» и намного легче модифицировать.


1

Это так и было на протяжении многих лет. Менять или даже обсуждать это так же бессмысленно, как менять или обсуждать меняющиеся светофоры. Давайте сделаем синий = стоп, красный = иди.

Посмотрите на изменения, внесенные с течением времени в Numeric Recipes for C ++. Они использовали макросы для фальсификации индексации, основанной на 1, но в выпуске 2001 года сдались и присоединились к стаду. Возможно, на их сайте www.nr.com есть материалы по причинам

Кстати, также раздражают варианты указания диапазона из массива. Пример: питон против IDL; [100: 200] против [100: 199], чтобы получить 100 элементов. Просто надо выучить причуды каждого языка. Изменение языка, который делает это одним способом, чтобы соответствовать другому, вызвало бы такое оскорбление и скрежет зубов, и не решило бы никакой реальной проблемы.


1

Я предпочитаю массивы на основе 0, потому что, как уже упоминалось, это облегчает математику. Например, если у нас есть одномерный массив из 100 элементов, эмулирующий сетку 10x10, то каков индекс массива 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 = ceil (i / 10)

Учитывая, что приведенная выше математика явно более сложна при использовании массивов на основе 1, кажется логичным выбрать массивы на основе 0 в качестве стандарта.

Тем не менее, я думаю, что кто-то может утверждать, что моя логика ошибочна, потому что я предполагаю, что была бы причина представлять двумерные данные в одномерном массиве. Я сталкивался с рядом таких ситуаций в C / C ++, но должен признать, что необходимость выполнения таких вычислений в некоторой степени зависит от языка. Если массивы действительно выполняли всю математическую работу с индексом для клиента все время, то компилятор мог бы просто преобразовать ваши обращения к массиву на основе М во время компиляции в 0 и скрыть все эти подробности реализации от пользователя. Фактически, любая константа времени компиляции может использоваться для выполнения того же набора операций, хотя такие конструкции, вероятно, просто приведут к непонятному коду.

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

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

0 на основе этажа: floor ((N - 1) * x / xRange)
1-й этаж с полом: этаж ((N - 1) * x / xRange) + 1
1 на основе ceil: 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 и программировать соответственно.

Что касается стандарта, у Бореалида есть отличный аргумент.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.