Почему массивы начинаются с нуля
Самое очевидное объяснение: индекс — это смещение относительно начала массива. Так элементы массива легче адресовать в памяти.
Проверим это на C.
#include int main() < int data[3] = ; int i = 0; printf("Array address: %p\n", data); do < printf("Array[%u] = %p\n", i, (void *)(&data[i])); i++; >while(i
Array address: 0x7ffd7c514a6c
Array[0] = 0x7ffd7c514a6c
Array[1] = 0x7ffd7c514a70
Array[2] = 0x7ffd7c514a74
Как первый (нулевой) элемент, так и сам массив находятся по одному и тому же адресу, поскольку 0-й элемент удалён на 0 элементов от начала. Эта связь между указателями и массивами в C настолько тесная, что их даже можно рассматривать вместе.
Однако это ответ на вопрос «зачем», а не «почему». Нумеровать массивы с нуля стали не сразу. Удивительно, но развитие такого простого вопроса не умещается в предложении или абзаце.
Потому что так удобнее
К началу 1960-х годов сформировалось три подхода к организации структуры, которую сегодня мы называем статическим массивом:
-
Исчисление с 0. Нижняя граница массива начинается с нуля.
Непривычно для обывателя, если это не житель Германии, свыкшийся с нумерацией этажей в зданиях. Последний элемент массива из, скажем, 8 элементов имеет номер 7.
Не всё так однозначно. Единообразия даже у самых первых языков программирования не существовало:
- Нумерация с нуля: LISP 1.5, APL (допускает выбор при запуске программы).
- Нумерация с единицы: APL, Бейсик, ранний Фортран.
- Произвольные границы: Алгол-60, затем Фортран, CPL, SIMULA, CLU, PL/1, Кобол, Паскаль, Алгол-68, JOVIAL.
Конечно, это не самый сильный аргумент. Часто языки снабжались синтаксическим сахаром для нумерации с 1. К примеру, в Алголе массив V[0:3] начинается с 0, а массив V[3] — с 1.
Создатель языка APL Кеннет Айверсон в книге «A Programming Language» объясняет, почему далее по тексту он будет пользоваться нумерацией элементов массива с нуля. При этом язык допускает отсчёт как с единицы, так и с нуля. Айверсон приводит уже описанный выше аргумент о сдвигах в позиционной нумерации.
Тем не менее и в эпоху до C звучали предложения выбирать нумерацию с 0. Логично предположить, что это была историческая неизбежность, до которой оставалось несколько лет.
Потому что парусные регаты мешали вычислениям
В 1967 году Мартин Ричардс впервые реализует компилятор своего детища — BCPL (Basic Combined Programming Language). Этот язык программирования был призван исправить проблемы созданного в начале шестидесятых языка CPL путём отказа от технологий, затрудняющих компиляцию.
Первый компилятор BCPL был написан для операционной системы CTSS машины IBM 7094 Массачусетского технологического института. На тот момент компьютеры — это уже не целые комнаты и электронные лампы, но всё ещё огромные шкафы с транзисторами и консоли управления без экранов. Ресурсов серии 7090 хватило, чтобы запустить американца в космос. Но мощность измерялась в тысячах машинных слов, микросекундах тактов и килофлопсах, а цена — в миллионах долларов.
В пятидесятых и шестидесятых IBM выдавала институту щедрые скидки на свои научные компьютеры или даже предоставляла их бесплатно. В 1963 году в МТИ поставили IBM 7094. На компьютер уговор был такой: 8 часов в сутки получают специалисты института, 8 часов — другие колледжи и университеты Новой Англии, а третью смену отдавали самой IBM для личных нужд.
На этом особенности не кончались. Несколько раз в год IBM обсчитывала регаты: президент IBM соревновался в заплыве на больших яхтах в проливе Лонг-Айленд, и каждое из судов получало очки гандикапа по специальной сложной формуле. Когда в вычислительный центр приходило задание, операторам полагалось всё бросить и запустить вычисления этих очков.
Машинный зал с установленным компьютером IBM 7094 в Колумбийском университете США. Фотоархив
Вообще, и без этого был шанс не получить результат вычислений. Изначально 7094 работал в режиме пакетной обработки. Вспомогательная система на IBM 1401 загружала пакет задач с перфокарт на ленту, а затем запускала задачи одну за другой и записывала результаты для последующей печати. Для каждой задачи приводилась оценка времени. Если задача выходила за пределы отпущенного, её принудительно прерывали.
В итоге компиляция в BCPL была оптимизирована по максимуму. То есть и указатели на элементы массива должны максимально близко походить на машинный код, без необходимости вычитать единицу при указании на элемент массива. Хотя во время выполнения программы не играет роли схема организации массивов, нумерация с нуля могла появиться для оптимизации времени компиляции.
Впрочем, конкретно эта версия — лишь гипотеза Майка Хойе. Ей нет никаких подтверждений от собствено Ричардса или хотя бы упоминаний в литературе. Мартин Ричардс в переписке с Хойе лишь приводит общие соображения о том, что он хотел достичь близости к машинному коду, поэтому указатель p и p + 0 — это одна и та же переменная. Ни на какие яхты Ричардс не жалуется.
К тому же на момент появления первого компилятора BCPL уже была готова операционка Compatible Time-Sharing System. В ней на одной машине с разделением времени компьютер выполняет одну задачу за один раз, но с оптимизацией ввода и вывода, чтобы паузы одного пользователя заполнялись работой других. Это уже не былая пакетная обработка задач.
Точно известно, что в дальнейшем BCPL значительно повлиял на все современные языки программирования.
В 1969 году Кен Томпсон урезал функциональность BCPL до языка B. В дальнейшем, для развития операционной системы Unix, Деннис Ритчи улучшил B добавлением функций PDP-11, в результате чего и получился C. Полвека спустя список языков, на которые оказал влияние C, занимает в «Википедии» целую страницу.
В языке BCPL v!5 и 5!v совпадают, поскольку являются указателем на !(v+5) или !(5+v) . Аналогично в C v[5] эквивалентно 5[v] .
Потому что так предложил Дейкстра
В 1982 году Эдсгер Дейкстра опубликовал статью «Почему нумерация должна начинаться с нуля». В ней он низверг как нумерацию с единицы, так и произвольные границы индексов. Очевидно, что Дейкстра — не человек, который легко поддаётся влиянию C, но также он раскритиковал Алгол-60, своё собственное детище, и Паскаль.
Известно, что Дейкстра ближе к концу жизни всё больше писал от руки, а не набирал тексты на машинке. Архив рукописей Эдсгера Вибе Дейкстры
Если необходимо записать интервал натуральных чисел 2, 3, …, 12 без опухоли из точек, возможны четыре варианта:
Затем автор замечает, что для нижней границы предпочтителен знак «⩽», поскольку наименьшее натуральное число существует. В противном случае, если последовательность начинается с наименьшего натурального числа, нижняя граница не будет натуральным числом.
Затем статья делает вывод, что из соображений простоты для последовательности из N членов предпочтительнее диапазон 0 ⩽ i < N , а не 1 ⩽ i < N+1 .
Куда менее известный документ — это полушуточная техническая заметка от 1 апреля 1980 года IEN 137 под названием «О священных войнах и призыв к миру» [On Holy Wars and a Plea for Peace].
В заметке Дэнни Коэн приводит интересный аргумент: в системе счисления с основанием b при отсчёте с нуля первые b ^ N неотрицательных чисел представляются ровно N цифрами. Например, если речь про двоичную запись, то 2 ^ 3 = 8 , и восьмой элемент массива будет иметь номер 1112, а не 10002. Понятно, что с такими преобразованиями легко бы мог справиться компилятор.
Потому что так более элегантно
Это объяснение может раздражать субъективностью. Соображения о красоте у каждого свои и совпадать не обязаны. Но авторы языков программирования — тоже люди со своими предпочтениями.
В конце восьмидесятых Гвидо ван Россум при создании Python как учёл свой предыдущий опыт с языком ABC, так и задумал привлечь аудиторию хакеров от мира Unix и C.
С 1983 года Гвидо работал в Центре математики и информатики в Амстердаме над реализацией языка ABC. Проект ставил целью создать язык программирования, пригодный для обычных людей, но не настолько ужасно реализованный, как Бейсик. Нумерация массивов в ABC начиналась с единицы. Такой же схемы придерживались другие знакомые Россуму языки — Алгол, Фортран и Паскаль.
Однако в вышедшем в 1991 году Python нумерация начинается с нуля, а не единицы. Получилось так в результате долгих размышлений.
Одна из причин — слайсы. Чаще всего при создании слайса используются операции «получить первые n элементов» и «начиная с i, получить следующие n элементов». При этом первый случай эквивалентен i == первый индекс . Гвидо посчитал, что лучше, если обе операции возможны без лишней головной боли в виде коррекции +1 и −1.
Если первый элемент имеет номер 1, то возможно указывать первый элемент и число элементов, которые нужно получить. Россум уже был знаком с подобным по ABC и вполне мог бы положиться на этот опыт.
Тем не менее автора Python очаровал синтаксис слайсов полуоткрытого интервала, если нумерация начинается с нуля: a[:n] (или a[0:n] ) и a[i:i+n] . К примеру, строку a легко разбить на три части a[:i] , a[i:j] и a[j:] .
Заметим, что в воспоминаниях Гвидо не приводит ни призывы гениев информатики, ни практики других языков, ни принципы, заложенные мастодонтами компьютерных вычислений шестидесятых. Зато слово «элегантность» в пяти абзацах его объяснения встречается 3 раза.
Почему же массивы начинаются с нуля?
Так исторически сложилось.
Массивы (списки)
Внимание! Все тесты в этом разделе разработаны пользователями сайта для собственного использования. Администрация сайта не проверяет возможные ошибки, которые могут встретиться в тестах.
Программирование. Язык Python. Программирование. Язык Python. Программирование. Язык Python.
Система оценки: 5 балльная
Список вопросов теста
Вопрос 1
Отметьте все правильные утверждения о списках в языке Python.
Варианты ответов
- элементы списка могут быть разных типов
- все элементы списка должны быть одного типа
- элементы списка могут нумероваться с единицы
- элементы списка всегда нумеруются с нуля
- размер списка может меняться во время работы программы
Вопрос 2
Как называется число, которое указывает на конкретный элемент массива и записывается в квадратных скобках?
Вопрос 3
Какой индекс имеет последний элемент массива A?
A = [8]*6
Варианты ответов
Вопрос 4
Требуется заполнить массив именно так:
X = [1 3 5 7 9 11]
Какой оператор надо поместить в тело цикла вместо многоточия?
X = [0]*6 for k in range(6): .
Варианты ответов
- X[k] = k
- X[k] = 2*k
- X[k] = 2*k — 1
- X[k] = 2*k + 1
- X[k] = 2*(k + 1)
Вопрос 5
Требуется заполнить массив именно так:
X = [12 9 6 3 0 -3]
Какой оператор надо поместить в тело цикла вместо многоточия?
X = [0]*6 for k in range(6): .
Варианты ответов
- X[k] = k
- X[k] = 12 — 2*k
- X[k] = 3*k — 12
- X[k] = 3*(k + 1) + 9
- X[k] = 12 — 3*k
Вопрос 6
Требуется заполнить массив именно так:
X = [1 2 4 8 16 32]
Какой оператор надо поместить в тело цикла вместо многоточия?
X = [1]*6 for k in range(1,6): .
Варианты ответов
- X[k] = k
- X[k] = 2*k
- X[k] = X[k-1] + 1
- X[k] = 2*X[k-1]
- X[k] = 2*(X[k-1] — 1)
Вопрос 7
Требуется заполнить массив именно так:
X = [0 3 4 7 8 11]
Какой оператор надо поместить в тело цикла вместо многоточия?
X = [0]*6 for k in range(6):
Варианты ответов
- X[k] = 3*k — k % 2
- X[k] = 2*k + k % 2
- X[k] = 2*k — k % 2
- X[k] = 2*k + k / 2
- X[k] = 2*(k — 1)
Вопрос 8
Что будет в результате выполнения программы:
Архимед Python 19/20
Строка считывается со стандартного ввода функцией input() . Напомним, что для двух строк определа операция сложения (конкатенации), также определена операция умножения строки на число.
Строка состоит из последовательности символов. Узнать количество символов (длину строки) можно при помощи функции len :
>>> S = 'Hello' >>> print(len(S)) 5
Срез (slice) — извлечение из данной строки одного символа или некоторого фрагмента подстроки или подпоследовательности.
Есть три формы срезов. Самая простая форма среза: взятие одного символа строки, а именно, S[i] — это срез, состоящий из одного символа, который имеет номер i , при этом считая, что нумерация начинается с числа 0. То есть если , то , , , , .
Номера символов в строке (а также в других структурах данных: списках, кортежах) называются индексом.
Если указать отрицательное значение индекса, то номер будет отсчитываться с конца, начиная с номера -1 . То есть , , , , .
Или в виде таблицы:
Строка S | H | e | l | l | o |
Индекс | S[0] | S[1] | S[2] | S[3] | S[4] |
Индекс | S[-5] | S[-4] | S[-3] | S[-2] | S[-1] |
Если же номер символа в срезе строки S больше либо равен len(S) , или меньше, чем -len(S) , то при обращении к этому символу строки произойдет ошибка IndexError: string index out of range .
Срез с двумя параметрами: S[a:b] возвращает подстроку из b-a символов, начиная с символа c индексом a , то есть до символа с индексом b, не включая его. Например, S[1:4]==’ell’ , то же самое получится если написать S[-4:-1] . Можно использовать как положительные, так и отрицательные индексы в одном срезе, например, S[1:-1] — это строка без первого и последнего символа (срез начинается с символа с индексом 1 и заканчиватеся индексом -1, не включая его).
При использовании такой формы среза ошибки IndexError никогда не возникает. Например, срез S[1:5] вернет строку ‘ello’ , таким же будет результат, если сделать второй индекс очень большим, например, S[1:100] (если в строке не более 100 символов).
Если опустить второй параметр (но поставить двоеточие), то срез берется до конца строки. Например, чтобы удалить из строки первый символ (его индекс равен 0, то есть взять срез, начиная с символа с индексом 1), то можно взять срез S[1:] , аналогично если опустиить первый параметр, то срез берется от начала строки. То есть удалить из строки последний символ можно при помощи среза S[:-1] . Срез S[:] совпадает с самой строкой S .
Если задать срез с тремя параметрами S[a:b:d] , то третий параметр задает шаг, как в случае с функцией range , то есть будут взяты символы с индексами a , a+d , a+2*d и т.д. При задании значения третьего параметра, равному 2, в срез попадет кажый второй символ, а если взять значение среза, равное -1 , то символы будут идти в обратном порядке.
Метод — это функция, применяемая к объекту, в данном случае — к строке. Метод вызывается в виде Имя_объекта.Имя_метода(параметры) . Например, S.find(«e») — это применение к строке S метода find с одним параметром «e» .
Метод find и rfind
Метод find находит в данной строке (к которой применяется метод) данную подстроку (которая передается в качестве параметра). Функция возвращает индекс первого вхождения искомой подстроки. Если же подстрока не найдена, то метод возвращает значение -1. Например:
>>> S = 'Hello' >>> print(S.find('e')) 1 >>> print(S.find('ll')) 2 >>> print(S.find('L')) -1
Аналогично, метод rfind возвращает индекс последнего вхождения данной строки (“поиск справа”).
>>> S = 'Hello' >>> print(S.find('l')) 2 >>> print(S.rfind('l')) 3
Если вызвать метод find с тремя параметрами S.find(T, a, b) , то поиск будет осуществляться в срезе S[a:b] . Если указать только два параметра S.find(T, a) , то поиск будет осуществляться в срезе S[a:] , то есть начиная с символа с индексом a и до конца строки. Метод S.find(T, a, b) возращает индекс в строке S , а не индекс относительно
Метод replace заменяет все вхождения одной строки на другую. Формат: S.replace(old, new) — заменить в строке S все вхождения подстроки old на подстроку new . Пример:
>>> 'Hello'.replace('l', 'L') 'HeLLo'
Если методу replace задать еще один параметр: S.replace(old, new, count) , то заменены будут не все вхождения, а только не больше, чем первые count из них.
>>> 'Abrakadabra'.replace('a', 'A', 2) 'AbrAkAdabra'
Подсчитывает количество вхождений одной строки в другую строку. Простейшая форма вызова S.count(T) возвращает число вхождений строки T внутри строки S . При этом подсчитываются только непересекающиеся вхождения, например:
>>> 'Abracadabra'.count('a') 4 >>> ('a' * 100000).count('aa') 50000
При указании трех параметров , будет выполнен подсчет числа вхождений строки T в срез S[a:b] .
Массивы. Тест 1. Вариант 2.
Внимание! Все тесты в этом разделе разработаны пользователями сайта для собственного использования. Администрация сайта не проверяет возможные ошибки, которые могут встретиться в тестах.
Тест по информатике для проверки знаний учащихся 9 классов.
Система оценки: 5 балльная
Список вопросов теста
Вопрос 1
Какое значение имеет последний элемент массива A?
A = [8]*6
Вопрос 2
Какой индекс имеет последний элемент массива A?
A = [6]*8
Вопрос 3
Требуется заполнить массив именно так:
X = [0 2 4 6 8 10]
Какой оператор надо поместить в тело цикла вместо многоточия?
X = [0]*6 for k in range(6): .
Варианты ответов
- X[k] = k
- X[k] = 2*k
- X[k] = 2*k — 1
- X[k] = 2*k + 1
- X[k] = 2*(k + 1)
Вопрос 4
В записи m[7]=9
m[7] обозначает
Варианты ответов
- имя массива
- имя элемента массива
- номер элемента массива
- значение элемента массива
Вопрос 5
В записи m[7]=9
m обозначает
Варианты ответов
- имя массива
- имя элемента массива
- номер элемента массива
- значение элемента массива
Вопрос 6
Выберите правильное описание массива а из 20 элементов, целого типа
Варианты ответов
- a=[0]*20
- a=[]
- a=[0]*10
- a=[0]*n
Вопрос 7
Выберите правильное заполнение массива m, состоящего из 10 элементов вещественного типа, значения вводятся с клавиатуры
Варианты ответов
- for i in range(n): m[i]=int(input())
- for i in range(10): m[i]=int(input())
- for i in range(10): m[i]=float(input())
- for i in range(10): m[i]=input()
Вопрос 8
Выберите правильное заполнение массива a из 20 элементов случайными целыми числами из диапазона от -20 до 39
Варианты ответов
- from random import randint for i in range(n): a[i]=randint(-20,40)
- from random import randint for i in range(20): a[i]=randint(-20,39)
- from random import randint for i in range(20): a[i]=randint(-20,40)
- for i in range(20): a[i]=randint(-20,40)
Вопрос 9
Дан массив a : 12, 7, 15, 10, 3, 58, 8, 25, 19, 17
Что выполнит фрагмент программы:
for i in range(10):
if a[i]%2==0:
print(a[i])
Варианты ответов
- выведет все положительные элементы массива
- выведет все четные элементы массива
- выведет все нечетные элементы массива
- выведет все отрицательные элементы массива
- выведет количество четных элементов
Вопрос 10
Дан массив a : 12, 7, 15, 10, 3, 58, 8, 25, 19, 17
Что выполнит фрагмент программы:
for i in range(10):
if a[i]%2!=0:
print(a[i])
Варианты ответов
- выведет все положительные элементы массива
- выведет все четные элементы массива
- выведет все нечетные элементы массива
- выведет все отрицательные элементы массива
- выведет количество четных элементов
Вопрос 11
Дан массив a : 12, 7, 15, 13, 32, 58, 8, 25, 19, 18
Что появится на экране после выполнения фрагмента программы:
z=0
for i in range(10):
if a[i]%2==1:
z+=1
print(z)
Вопрос 12
Дан массив a : 12, 7, 15, 13, 32, 58, 8, 25, 19, 18
Что появится на экране после выполнения фрагмента программы:
z=0
for i in range(5):
if a[i]%2==1:
z+=1
print(z)
Вопрос 13
Дан массив a : 14, 2, 15, 10, 32, 22, 8, 25, 19, 17
Что появится на экране после выполнения фрагмента программы:
z=0
for i in range(10):
if a[i]%10==2:
z+=1
print(z)
Вопрос 14
Дан массив a : 14, 2, 15, 10, 32, 22, 8, 25, 19, 17
Что появится на экране после выполнения фрагмента программы:
z=0
for i in range(10):
if a[i]%10==2:
z+=a[i]
print(z)