У меня было это, ну, кто-то назвал это пламенной войной. Я бы не назвал это точным. Я не очень люблю конфронтацию, мне это доставляет дискомфорт. Но если кто-то со мной не согласен, я решительно отстаиваю свою позицию. Я никогда не выхожу из себя и всегда крут, как огурец. Иногда я защищаю свою позицию даже после того, как смягчаю свою точку зрения или иным образом обновляю ее, но я защищаю только свою обновленную позицию, а не неправильную. Эй, это не / r / changemyview, мне не нужно сообщать вам дельту, когда я меняю свое мнение.
Но перейдем к нашей теме. Есть идея, что всякий раз, когда вы упоминаете, это, кажется, сводит программистов с ума. Вы, наверное, догадались, что это: индексирование начинается с 1, а не с 0. Каждый раз, когда я публикую об этом, я неизменно ввязываюсь в войну пламени. И я многому научился из этих огненных войн, и я решил резюмировать и уточнить моменты, которые я обычно упоминаю. Итак, без лишних слов, вот 2 причины, по которым массивы должны начинаться с 1:
Причина 1: итерация в обратном направлении более естественна
Когда вы перебираете список в обратном направлении, вам нужно сделать for (int i = count — 1; i >= 0; i — ), если вы использовали систему на основе 0, тогда как если вы выполняете итерацию в обратном направлении в системе на основе 1, вам нужно сделать for (int i = count; i >= 1; i — ), что намного лучше. У вас нет этого случайного -1 в середине кода.
Так зачем вам это делать? Что ж, оказывается, что если вы удаляете что-то из списка, это намного проще сделать, повторяя итерацию в обратном направлении. Вы можете понять почему: когда вызывается Remove (), удаляются только те элементы, которые вы уже просматривали. По общему признанию, я уже не делаю это так часто после того, как переключил свой проект Unity на новую систему ECS. ECS обеспечивает большую гибкость. Но это инструмент в моем наборе инструментов. И я иногда им пользуюсь.
Причина 2: это делает остальной мир
В остальном мы начинаем отсчет с 1. Почему в программировании я должен начинать отсчет с 0? В этом нет смысла и кажется неправильным. Когда я вижу, что кто-то что-то считает, например, видео TedX на YouTube или что-то в этом роде, они всегда начинают с 1.
Но вы можете возразить, что во многих языках низкого уровня нам нужно начинать отсчет с 0, потому что для доступа к элементу в массиве требуется что-то вроде (arrayPosition + i * sizeOf(thing)) или что-то в этом роде. Некоторое время не программировал на Си. Но это не одно и то же. Это получение позиции элемента в памяти, или, говоря более кратко, вы получаете относительные позиционные данные относительно начала блока. памяти. Когда вы идете for (int i = …), вы получаете абсолютные позиционные данные. Они разные, и гораздо легче удерживать это разделение в голове.
Когда вы перечисляете, вы начинаете с 1, когда вы занимаетесь позиционированием, вы начинаете с 0. Как можно проще. И есть веская причина, по которой так работает. При перечислении индекс последнего элемента также является счетчиком. Компьютерам все равно, а людям - нет. С ним легче работать.
Некоторые жалобы
Поэтому всякий раз, когда я публикую это, я всегда получаю отпор. Давай пройдемся по этому поводу.
Обновление: жалоба 1: я могу найти пример, где мы считаем от 0, поэтому весь ваш аргумент - ерунда
Раньше это была причина, но теперь имеет смысл подать жалобу.
Вы обнаружили относительную позиционную систему подсчета (…, -2, -1, 0, 1, 2,…). Это используется, когда вы сравниваете какое-то значение с другим эталонным значением, равным 0. Как и автомобиль, если он остановился, вы бы сказали, что он едет со скоростью 0 километров в час. Примеры этого можно найти повсюду.
Я не говорю об относительных позициях. Я говорю об абсолютных позициях, как в списках. В нулевом элементе списка нет ничего особенного, поэтому использовать систему относительного позиционного счета нецелесообразно.
Жалоба 2: так утверждает Дейкстра
Теперь Дейкстра утверждает, что индексация с 0 более естественна, потому что диапазон описывается как
1 Есть наименьшее натуральное число. Исключение нижней границы - как в b) и d) - вынуждает подпоследовательность, начинающуюся с наименьшего натурального числа, выполнять нижнюю границу, как упомянуто, в область неестественных чисел. Это некрасиво, поэтому для нижней границы мы предпочитаем ≤, как в а) и в).
Я имею в виду, что это вообще значит?
Хорошо, вот что я думаю. Давайте рассмотрим это очень медленно. Сначала первая часть.
Есть наименьшее натуральное число. Исключение нижней границы - как в б) и г)
B - это 1 < i < 12, а D - это 1 < i < 13.
Наименьшее натуральное число в обоих из них - 2.
вынуждает подпоследовательность, начинающуюся с наименьшего натурального числа, нижнюю границу, как упоминалось, в область неестественных чисел.
Итак, я подразумеваю, что существует подпоследовательность [a, b], где a - начало последовательности, а b - наименьшее возможное натуральное число. Итак, у нас есть [2, 1] в обоих. И я предполагаю, что часть «неестественного числа» связана с тем, что размер списка отрицательный.
Так что я думаю, что ошибся. Потому что у этого аргумента так много проблем. Первое «исключение нижней границы». Почему? Это просто подтасовка цифр, чтобы заставить это работать? Второй «начиная с наименьшего натурального числа». Почему 1? Почему бы, скажем, не 0? Или -1. Или 20? Почему натуральные числа? И, наконец, «неестественное». Я думал, что это означает отрицательный результат. Но, думаю, сейчас это означает отрицательный. Я бы хотел, чтобы Дейкстра был жив, чтобы я мог просто спросить его. В любом случае это еще одно магическое число. Почему отрицательный? Почему не отрицательно, как я изначально предполагал. Почему не положительное число. Это «неестественное» прозвище тоже. Похоже, кто-то пытался вызвать эмоции… и потерпел неудачу. И, наконец, я считаю, что идея размера негативного набора немного подозрительна. Наверняка в наборе не может быть отрицательного размера. Но, возможно, гипотетически это имело бы смысл при каком-то математическом подходе.
Итак, вторая часть:
Теперь рассмотрим подпоследовательности, начинающиеся с наименьшего натурального числа: включение верхней границы заставило бы последнее быть неестественным к тому времени, когда последовательность сжалась до пустой. Это некрасиво, поэтому для верхней границы мы предпочитаем ›диапазон нижних индексов 1 ≤ i. Я знаю студента, который чуть не провалил экзамен из-за молчаливого предположения, что вопросы заканчиваются внизу первой страницы.) Я думаю, Энтони Джей прав, когда он заявляет: «В корпоративных религиях, как и в других, еретик должен быть изгнан не из-за вероятности того, что он неправ, а из-за возможности, что он прав».
Так что я просто повторю то, что я сказал по этому поводу в ветке: я даже не знаю, что это значит.
Итак, в заключение я потерял всякое уважение к Дейкстре. Ну, может, не все мое уважение, но отчасти. Он по-прежнему создает отличные алгоритмы поиска путей.
Итак, теперь я встретил несколько альтернативных встречных претензий (в основном от StackExchange):
Жалоба 3: 0 полезен
Да, но не всегда. Конечно, не здесь. Здесь это скорее сбивает с толку, чем помогает.
Жалоба 4: но будущее - за 0. Это прогресс.
Смотрите: самолет более продвинутый, чем ходьба. Но ходьба не устарела.
Жалоба 5: Но тогда мне придется переходить между ними
Да, преобразование всегда будет необходимо при преобразовании между данными позиции и данными индексации. Но если бы мы использовали индексирование на основе 0, нам бы тоже пришлось это делать. Имейте в виду, что, поскольку ваш мозг работает с индексированием на основе 1, преобразование происходит не строго в коде. Кому нужно больше конверсии? Понятия не имею. Но на основе 1 у меня меньше головной боли. Какая разница, если мой код на 0,00000001% медленнее? Я чувствую себя лучше, и это все, что имеет значение.
Жалоба 6: но в позиционных данных используется 0. Мы также должны использовать 0 для индексов!
Хорошо, начни отсчет с 0 для всего. Скажи мне, как долго ты продержишься. Более естественно начинать индексы с 1. Компьютер может использовать и то, и другое взаимозаменяемо. Но человеческий разум гораздо более ограничен. Не стоит недооценивать силу последнего индекса, который также является количеством элементов.
Жалоба 7: при копировании элементов из A [] в B [] с индексированием на основе 1 вам необходимо добавить дополнительный 1, как в B [start + i -1] = A [i], чтобы не перезаписывать значения (и связанные Копирование аргументов массива)
Это, без сомнения, самый убедительный аргумент, и я действительно нашел его очень тревожным. Но чем больше я думал об этом, тем больше думал, что это не так уж и важно. Потому что B[start + i] и A[i] на самом деле не должны быть одним и тем же. Один использует позиционную систему, а другой - систему счисления. Так что, естественно, потребуется преобразование. Это проблематично? да. Это большое дело? Нет, потому что это очевидно, когда возникает такая проблема.
Жалоба 8: поскольку данные базовой позиции основаны на 0, преобразование индекса на основе 1 в индекс на основе 0 не происходит медленно
Сколько бы раз я ни слышал этот аргумент, я никогда не смогу принять его всерьез. Я вспоминаю, как в университетских ЦП есть специальные регистры для увеличения и уменьшения единицы. Но даже если бы это было не так, увеличение и уменьшение единицы должно быть последней из ваших забот, учитывая все остальное, что нужно сделать ЦП.
Кроме того, в современных языках есть множество полезных функций, которые можно считать «медленными». Ничего страшного. И в любом случае компилятор выполняет большую оптимизацию, поэтому я никогда не могу быть уверен, как та или иная функция влияет на производительность.
Жалоба 9. Но если у меня есть язык, поддерживающий индексирование на основе 1 и 0, переключение между ними может сбивать с толку
Ага. Выберите один и придерживайтесь его.
Жалоба 10: но я работаю на нескольких языках, использующих разные условные обозначения, и переключение между ними вызывает затруднения
Вот незадача. Это цена прогресса.
Обновление: жалоба 11: подождите, вы, ребята, используете индексы для перебора списков?
На самом деле это не жалоба. Я просто хочу объяснить, почему я не люблю использовать итераторы. В первую очередь они создают мусор. Что на самом деле не имеет значения для большинства приложений, но в игре, где вы запускаете код 60+ раз в секунду, постоянно создавать мусор - не лучший вариант. Я слышал, что в Unity есть способ не создавать мусор с помощью итератора, но я не совсем уверен, как это работает, и я не пишу код исключительно на Unity, поэтому я все еще не использую итераторы.
Во-вторых, вы можете удалить элементы из списка, если вы используете индексы для их перебора. Вот почему я так сильно забочусь о том, чтобы синтаксис выглядел чистым, когда вы просматриваете список в обратном порядке.
Заключение
Я хотел бы закончить отсюда комментарием, который мне очень понравился:
Мне кажется смешным, что мы, люди, потратили так много времени на то, чтобы придумать «классы», чтобы мы могли описывать вещи более человечным образом в нашем коде, но затем, глядя на массивы 0 против 1, мы, кажется, зацикливаемся на одна только логика.
Что касается компьютера, то математически 0, вероятно, будет лучше, но я чувствую, что здесь упускается один момент. Если бы мы хотели описывать вещи более человечным образом (например, классы), почему бы нам не сделать то же самое для других частей языка? Разве это не является столь же логичным или действительным (или имеет более высокий приоритет в этом отношении ...), чтобы сделать язык более понятным и удобным для людей и, таким образом, в более широком смысле, менее подверженным сценариям, которые имеют тенденцию создавать логические ошибки, и более склонным к более быстрому изготовление полезного творения.
[…]
В случае, скажем, PHP, моя ставка составляет 80% времени, когда массив на основе 1, являющийся синтаксисом по умолчанию, уменьшит логические ошибки в реальных случаях использования или, по крайней мере, не вызовет больше в среднем, при этом делая его много кодировщику проще создавать полезный код быстрее. Помните, я предполагаю, что все еще будет возможность array (0 = ›‘ value ’), когда это необходимо, но также предполагаю, что в большинстве случаев целесообразно иметь что-то более близкое к описанию реального мира.
Это действительно не звучит слишком надуманным запросом, если смотреть на него с этой точки зрения. Когда мы подходим к интерфейсу, будь то ОС или язык для программиста, чем ближе к человеческому мышлению и привычкам мы его создаем, тем в большинстве случаев мы будем счастливее и тем меньше будет недопонимания между человеком и компьютером (человеческая логика- ошибки), и более быстрое производство и т. д., которые у нас будут. Если в 80% случаев в реальном мире я описываю вещи с помощью 1 при составлении списков или подсчете, тогда компьютер должен в идеале интерпретировать мое значение так, как он понимает, с минимальным количеством информации или отличаться от моего обычного способа описания чего-либо, насколько это возможно. Короче говоря, чем ближе мы можем моделировать реальный мир, тем лучше качество абстракции. Так что то, что он хочет, отнюдь не глупо, поскольку это конечная цель и свидетельство необходимости большей абстракции. Компьютер все еще может в конечном итоге рассматривать это как специальное использование массива на основе 0. Меня не заботит, как компьютер интерпретирует это, если это более простой и интуитивно понятный способ описать ему то, что я хочу, с меньшим количеством ошибок со временем.
Знаете ли вы, что за статью можно хлопать больше одного раза? Просто удерживайте кнопку хлопка. Да, я знаю, это похоже на обман, но это не так. Вот сколько я обычно хлопаю в ладоши:
0 - ›Не согласен / Не читал
1 -› Нейтрально, но жалко хлопну в ладоши
~ 4 - ›Не нравится все, но есть хорошие моменты
~ 16 - ›Это довольно хорошая статья
~ 50 -› Все должны это прочитать!