Добрый день. Продолжаю свой рассказ про онлайн обучение по государственной программе «Цифровые профессии».
Продолжаю публикации непосредственно по программе обучения. Материал беру из лекций, чтобы можно было оценить ценность урока.
Сегодня мы перейдём к структурам данных. Звучит всё очень масштабно и сильно. Но на самом деле ничего сложного нет, мы с ними познакомимся.
Помимо этого, мы посмотрим на простые алгоритмы с этими структурами, в первую очередь массив. И какие алгоритмы есть в этом массиве.
Составление программы и написание кода — две разные профессии
Скорее всего, у многих из вас мог возникнуть вопрос. Для чего нам эти блок-схемы?
Но поговорим немного про историю. Изначально составление алгоритмов и программ, и написание кода — были двумя разными профессиями.
Были люди, продумывающие алгоритм, что нужно будет сделать компьютеру. И другие люди, составляющие код, делали так, чтобы на конкретном компьютере он мог выполняться. Чтобы можно было занести код в программу, и там происходили вычисления. Раньше были огромные ЭВМ (электронные вычислительные машины), в них нужно было переносить код. Составление программы и занесение кода в машину, чтобы она выполнила его — две разные профессии и задачи.
Если сравнивать с тем, что у нас есть на бытовом уровне, можно вспомнить про стенографистов (людей, записывающих текст, раньше на печатных машинах, сейчас на компьютерах) и людей, произносящих этот текст. Так вот, произнесение текста, составление текста, когда мы эту речь начинаем готовить, продумывать — это составление программы, а вот запись текста — это стенограмма (запись программы на конкретном языке). Да, существуют определённые условия, есть свой синтаксис, надо правильно оформить текст, одни речевые обороты использовать, другие исправлять — это отдельная профессия, ей нужно учиться, нужно уметь это делать. Но при этом это две разные профессии.
То же самое происходит у нас в программировании. У нас есть составление программ, когда вы продумываете алгоритм. Когда думаете, а как же нужно? Какие действия необходимо совершить? В каком порядке? Какие нам нужны переменные? Что мы с ними будем делать? И другая задача — это написание этой программы. То есть, когда у вас уже готова программа, алгоритм составлен, и необходимо его записать. И вот здесь вы выбираете тот язык, который лучше подходит для конкретной задачи. На основе синтаксиса и принципов языка записываете ваше решение на нужном языке. Потом оно выполняется программой. В большинстве случаев, когда учатся программировать не делят эти части.
Массивы
Теперь перейдём к структурам данных. Иногда удобнее работать с какими-то данными, если они подчиняются определённым взаимосвязям и у них есть структура. Массив — это самая первая структура, с которой чаще всего знакомятся.
Посмотрим, что такое массивы? Как они работают? Чтобы было понятнее, мы начнём с простых бытовых примеров.
Рассмотрим гардероб в театре. Каким принципам он подчиняется? У нас есть вешалки, и мы заранее знаем их количество. Допустим, у нас гардероб на 200 посадочных мест, соответственно, у нас есть 200 крючков, 200 номерков, на которые можно повесить элементы одежды. То есть заранее известно количество. Больше оно так просто не станет. Естественно, можно пригласить слесаря прибить новые крючочки, но в целом их количество заранее известно, и оно не меняется.
Помимо этого, на каждый крючок мы вешаем только элементы одежды, то есть на каждом крючке будут находиться однотипные вещи. Мы не сможем в гардероб припарковать машину, например, или положить туда штангу на какой-то крючок. В любом случае на каждом крючке у нас находится одинаковые, очень похожие друг на друга объекты.
На один крючок можно повесить только один элемент, то есть в одну ячейку массива можно положить только один элемент. И естественно, если в театре бывают исключения и вы можете попросить гардеробщицу на один номерок повесить два элемента одежды, то в программировании так сделать нельзя. У нас всё очень строго, в одну ячейку только один элемент. Посмотрим ещё два похожих примера.
Пример подземной парковки
У нас есть парковка подземная, она также очень похожа на массив. Почему? Потому что на парковке однотипные элементы, там только транспортные средства, причём примерно одинаковые. В большинстве случаев автомобили легковые с небольшими вариациями, мы вряд ли на подземный паркинг будем огромную фуру с прицепом загонять.
Помимо этого, все парковочные места у нас пронумерованы. Соответственно, вы можете легко найти свой автомобиль, если знаете, где его оставили. То есть некоторых порядок, есть некоторая структуры между этими элементами, взаимосвязь.
И последнее, у нас ограниченное количество машин может поместиться на эту парковку, то есть размер нашего массива, размер нашей парковки заранее известен, и не может поменяться. Если мы захотим, чтобы больше машин помещалось, то нам либо придётся сломать структуру и оставлять машины, где-то в проходах, где не положено их оставлять, либо делать пристрой и увеличивать. Получается, мы не сможем увеличить размер нашей парковки.
С массивами то же самое. В массивах заранее известно количество элементов, которые в них можно поместить. Заранее известен размер каждого элемента, тип каждого элемента, то есть мы знаем, что вот, например, здесь у нас вмещаются только машины. В гардеробе у нас помещается одежда. Тип элемента, хранящийся в массиве, у нас заранее известен. Помимо этого, мы можем по индексу получить нужный нам объект. В гардеробе можем получить по номерку нашу одежду, на парковке по номеру места найти, где же находится наше транспортное средство.
Пример бабушкиного балкона
Рядом у нас есть пример бабушкиного балкона. Так вот балкон является примером, другой структуры данных, а может быть там и нет структуры. Это не может быть массивом. Почему?
На балконе мы можем хранить и левую сломанную лыжу, и старый неработающий холодильник, и телевизор, и одну гантелью, и многое другое. То есть объекты, хранящиеся на балконе, не одного типа. Там может храниться всё что угодно. По сути, это некоторая свалка объектов.
Далее у нас нет никакой индексации. Мы не можем по номеру или позиции понять, где что находится. Не можем с третьего места на балконе что-то найти. У нас всё равно какая-то общая куча.
Помимо этого, на балкон всегда может что-то ещё поместиться, даже если он кажется полным, сверху можно что-то положить. Так его размер будет увеличиваться. То есть у нас нет заранее фиксированного размера хранилища.
Посмотрите на эти объекты на парковку и балкон. Так вот, парковка относится к массиву, она гораздо ближе к массиву, по принципу своей работы, а балкон — это нечто максимально далёкое от массивов.
Можно выделить три параметра массива
- Нам будет известно начало массива.
Если у нас есть произвольный объект, где-то расположенный, и у него есть первый элемент, который где-то лежит. Мы можем этот первый элемент найти.
- Далее мы можем найти размер каждого элемента.
Если нам известен тип. Если мы знаем, где объекты хранятся. То мы можем найти размер каждого элемента массива. В гардеробе у нас через каждые 30–40 см висят новые крючочки, и мы знаем, что от одного объекта для второго объекта вот такое расстояние. На парковочных местах мы примерно знаем размер автомобиля, а чаще всего на парковках и очерченные размеры парковочных мест. Поэтому размер каждого элемента массива также нам известен.
- Мы знаем количество элементов в массиве это то, о чём мы с вами ранее говорили.
Например, в гардеробе может быть 200 вешалок, а на парковочной площадке помещается 100 автомобилей.
Устройство массивов внутри компьютера
Пример ленты с котиком
Теперь перейдём к устройству памяти компьютера. Если немного упростить то, что происходит в вашем компьютере, то память очень похожа на массив. Её можно представить как очень длинную ленту, в каждой ячейке которой что-то написано. Будем предполагать, что в каждой ячейке памяти хранятся какие-то символы.
Это небольшой кусочек очень длинной ленты и лишь часть мы видим на слайде, потому что память в компьютере очень большая. Есть какие-то символы в разных ячейках.
В данный момент часть памяти можно представить как подмассив, что-то находящиеся внутри. В качестве некоторого массива можно посмотреть на эти ячейки.
Есть три параметра, которые нам нужно знать.
- Где массив начинается. Если нужно получить слово cat (нашего котика), то мы знаем, что первая буква находится в ячейке 127.
- Размер каждой ячейки массива. Здесь ничего сложного, у нас одна ячейка — это один элемент. Получается, в 127 ячейке лежит буква «c», в 128 буква «a», в 129 буква «t».
- Количество элементов, для этого массив должен быть закончен.
Из всей большой ленты я возьму эти три ячейки и буду рассматривать их как массив. Все три параметра, имеющиеся у нас, соблюдены. Мы знаем, откуда стартует массив, его размер и количество ячеек. Таким образом, набор из трёх ячеек можно представить как массив из трёх элементов. Элементами, являются буквы. Каждая буква в нашем случае занимает одну ячейку памяти.
Итак, чтобы вытащить нашего котика из памяти, нужно пронумеровать необходимые ячейки. Нам нужно сказать, что из одной ячейки мы возьмём первую букву, из другой вторую и так далее. С котиком всё понятно, мы видим на экране 127, 128, 129 ячейку. Но мы рассмотрели частный случай. Что будет происходить, если мы не видим перед собой на экране весь массив, а только знаем эти параметры? Мы знаем, где он начинается. Знаем размер каждой ячейки и количество элементов. Как можно получить все эти ячейки?
Формула поиска n-ого элемента в массиве
Для этого есть формула:
an = start + (n-1) * cell_size
Разберёмся в написанном.
В качестве an будем рассматривать любую ячейку массива. Например, если нам нужно найти первый элемент массива, тогда вместо n поставим 1. Вот а1 это первый элемент нашего массива.
Далее start — это адрес первой ячейки (места, откуда начинается массив). Если он нам известен, а он должен быть известен, потому что это один из обязательных параметров, то подставляем его.
Дальше почему-то n-1, с этим сейчас разберёмся.
Теперь умножаем, получившееся здесь, количество на cell_size (с английского cell — ячейка, size — размер).
Как формула работает на нашем котике
Есть cat, мы знаем, что у нас:
● массив начинается в ячейке 127, поэтому start = 127;
● каждый элемент занимает ровно одну ячейку, поэтому cell_size = 1;
● всего три таких элемента.
Теперь найдём, например, адрес первого элемента. Ищем а1.
● start = 127, то есть ячейка под номером 127.
● N-1, мы ищем первый элемент, поэтому 1 - 1 = 0.
● Cell_size = 1.
● В результате — а1 = 127 + 0×1, то есть а1 = 127.
Таким образом, мы понимаем, что первый элемент нашего массива буковка «c» находится в ячейке 127. Теперь вы, наверное, догадались, почему мы вычитаем 1, если бы не вычитали, то эта формула работала бы неправильно. При такой нумерации элементов нам приходится вычитать 1.
Попробуем по аналогии найти, например, третий элемент букву «t». Что у нас получается?
● А3 = 127 + (3–1).
● Умножаем на cell_size. Cell_size = 1.
● Получаем 127+2×1=129. Третий элемент буква «t» находится в ячейке с номером 129.
Таким образом, если вам заранее известны все параметры массива, то вот по такой простой формуле вы можете найти любой элемент этого массива.
Пример с парковкой
Если известно, на каком месте, например, на 10 месте находится ваша машина. Вы знаете, где находится первая машина, то есть откуда необходимо стартовать. Вам необходимо пройти 9 машин, зная, сколько места занимает каждая из них, и 10 машина — уже ваша. Таким образом, в любом массиве это работает.
Пример с гардеробом
Зная, что ваша куртка висит в определённом месте, а вы стоите около первой куртки. Дальше зная свой номерок, вам необходимо просто пройти нужное количество вешалок, и там будет ваша куртка.
Решаем задачу с помощью массива
Нужно понять, как к индексу можно обращаться. Представим блок-схему. Раньше были переменные a, b, c, d и т. д. Потом мы водили переменные, состоявшие из нескольких слов (firstFriendSpeed, secondFriendSpeed). Теперь у нас появляются новый объект, назовём его — numbers (от английского — числа). Считается хорошим тоном, когда вы именуете переменные заложенным в них смыслом. В нашей переменной numbers будет храниться массив, то есть набор некоторых элементов с определённым порядком. Элементы нам хорошо известно, это число 1, 8, 3, 2, 6, допустим это масса гирек или цифры на стаканчиках,
Вводим переменную size, говорящую о размере массива. Теперь нам нужен индекс. Мы будем стартовать с самого начала, поэтому индекс возьмём равным 0. Потому что у самого первого элемента нашего массива индекс равен 0. Также мы введём наш максимум это первый элемент. Если раньше у нас было написано, что максимум равен «а», то сейчас первую гирьку зовут не «а», её зовут numbers с индексом 0. Именно так можно получить элемент нашего массива.
Есть массив numbers, в квадратных скобках может быть указан индекс элемента, который мы хотим из него достать. Поэтому max = numbers [0], значит то же, что мы до этого писали, max=а или max=1, как первая гирька.
Конструкция цикла
Далее, конструкция — это цикл. То есть мы отсюда, в зависимости от ответа на вопрос уходим либо по веточке «yes», либо по веточке «no». Но если мы возвращаемся, то будем снова отвечать на этот вопрос. И мы будем делать это до тех пор, пока ответ на вопрос будет «да». Как только ответ станет «нет», мы выскочим.
Дальше, мы здесь с вами указываем размер нашего массива. Обратите внимание, мы могли не вводить переменную size. И поставить просто число 5. Что тогда бы случилось?
Если мы поменяли массив и в нём количество элементов стало другим вместо 5, например, взяли 10, то нам пришлось бы менять число не только в блоке инициализации, но и внутри нашей программы. А как вы помните, мы хотим сделать так, чтобы программа была более или менее универсальной. В самом начале мы для этого водили переменные. Мы именовали гирьки так, чтобы если взять другие гирьки, то менять числа нужно было только в блоках инициализации, которые этим гирькам соответствуют, хранящихся в этих переменных.
Сейчас мы сделали массив, у него есть параметр размера. Здесь мы его зафиксировали. Size=5. Если мы здесь его не зафиксировали в блоке инициализации, а вот сюда поставили число. То когда мы поменяем размер массива, нужно будет обязательно вспомнить и переписать это внутри всей программы. Поэтому крайне нежелательно внутри программы указывать конкретные числа, которые могут меняться. Вот +1 — это нормально, потому что у вас всегда при любом массиве и любых начальных данных, индекс будет меняться один за одним. Он будет просто расти. А если мы говорим, о каких-то параметрах, каких-то вводных данных в вашей программе, то их всегда необходимо определять в каком-то блоке инициализации, в самом начале работы вашей программы. Чтобы внутри не было никаких чисел.
Магические числа в программировании
В программировании есть для этих чисел — их называют магическими числами. Так как, когда другой человек смотрит на вашу программу, он не понимает, что это за числа. Это какая-то магия. Почему здесь стояла бы 5 вместо size. И человеку, смотрящему на программу, нужно догадаться, что 5 здесь, наверное, показывает какое количество элементов находится в массиве. Поэтому старайтесь в блок-схемах или программах, которые вы сейчас и в дальнейшем составите, не оставлять магических чисел. Всё должно быть понятно читателю.
Так вот, цикл будет работать до тех пор, пока index < size, мы будем выполнять какие-то действия. Но когда index станет равен или больше размера нашего массива, цикл закончится. Потому что, мы точно прошли весь массив и обработали все элементы.
Что же делать, если мы пока ещё смотрим на элементы массива? Если index совпадают. Мы точно так же отвечаем на следующий вопрос. Элемент, который мы сейчас с вами рассматриваем больше максимального или нет? В этом случае, что у нас будет? Index, пока ещё равен 0. Делаем переменную numbers 0 (это элемент массива с индексом 0, то есть 1) и сравниваем её с элементом хранящимся в переменной max (максимальное значение). В max хранится этот же самый элемент 1. На вопрос 1<1 ответ отрицательный, потому что стоит строго больше. Они равны между собой, одна 1 больше другой быть не может. Поэтому ответ отрицательный. Поэтому мы идём по веточке «No», и оказываемся в этой ячейке. С ней мы познакомились, когда работали над задачкой с друзьями и собакой. Мы count увеличивали каждый раз, когда собака добегала до одного из друзей. Сейчас происходит то же самое. Только здесь мы будем увеличивать index. Каждый раз, когда мы заходим в эту ячейку, index увеличивается на 1. Сейчас index равен 0, так и запишем:
index = 0
Подставляем в правой стороне вместо index число и прибавляем 1. Получаем:
0 + 1 = 1
Когда мы вычислили правую сторону, подставляем её в переменную index. И после того как выходим из этой ячейки, у нас в переменной index будет храниться 1.
Возвращаемся в условия цикла, которые необходимо проверить. У нас получается 1<size (size=5). 1<5? Ответ положительный, поэтому мы вновь идём в цикл.
Теперь numbers [index] стал 1, поэтому numbers с index 1. Это следующее число. Именно поэтому меняем index, чтобы можно было переходить от одного элемента к другому. Мы последовательно сможем перебрать все элементы.
Сравниваем 8 и то, что хранится в переменной max. В переменной max до сих пор хранится первый элемент 1, потому что мы его не меняли. Получается numbers с index 1 это 8. Здесь у нас хранится 1. 8>1. Соответственно, идём по веточке «Yes». Что мы делаем? Мы в переменную max записываем следующий элемент numbers с индексом 1, 8 записалась в наш максимум.
Идём дальше по стрелочке, попадаем на увеличение индекса, индекс стал равен 2. Возвращаемся, отвечая на вопрос 2<5? Да. Продолжаем цикл.
Что случится в самом конце? Когда у нас index станет равным 5, то есть, после обработки число с index 6. Мы попадём в ячейку, и станет 4+1=5. В index запишется число 5. Мы вернёмся в цикл и на вопрос 5<5? Мы ответим отрицательно, потому что они опять совпадают, а здесь строгое неравенство. Далее пойдём в веточку «No», это будет завершением нашего алгоритма (блок-схемы). Узнаем, что максимальное число у нас найдено, и оно находится в переменной max.
Теперь мы можем то же самое проделать со стаканчиками. Всё полностью идентично выполняемому раньше. Просто сейчас стаканчики названы не a, b, c, d, e. А numbers 0, numbers 1, numbers 2, numbers 3, numbers 4.
Если мы аккуратно пройдём по этой блок-схеме, то сможем провести алгоритм и найти максимальный элемент. Опять окажется 8.
Что такое псевдокод?
Псевдокод — это текст, максимально похожий на языки программирования, но без жёстких синтаксических требований. Если помните, что у нас, например, в Java нужно было ставить точки запятой. В Python не нужно было брать скобки. И вот для каждого языка, свои требования, в псевдокоде таких жёстких требований нет. Он похож на эти языки и переписать программу с псевдокода на конкретный язык, необходимый нам, не составляет никакого труда. Но псевдокод пишется для человека. Программа в большинстве случаев его не поймёт, только если посмотреть на язык, у которого синтаксис совпадает с тем, что мы здесь написали. Но вот для человека всё становится понятно, потому что синтаксические требования не обязательные для нас, мы просто опускаем.
Разберём запись в псевдокоде
Первые две строчки — это блок инициализации, которые был на блок-схеме. Есть массив numbers, заданный нами. В разных языках он задаётся по-разному, но пока будет так. Далее, ввели переменную size (размер нашего массива). Начинаем алгоритм с того, что пойдём к элементу с индексом ноль (самого первого для нас элемента) и возьмём максимальный элемент самым первым элементом массива, то есть элемента с индексом 0. Теперь начинаем цикл. Стоит уточнить, что для англоговорящих людей программирование на старте даётся проще, чем для людей, не очень хорошо знающих английский язык или незнающих его вообще. Потому что, если посмотреть на написанное, здесь практически связанный английский текст. У нас написано:
● while (index < size) do
Что получается если это перевести?
● До тех пор, пока индекс меньше размера, делай.
То, о чём мы говорили. Пока индекс меньше, чем размер массива, нам нужно что-то делать. И именно это здесь записано. В очень похожих терминах этот цикл создаётся почти в любых языках программирования. У нас есть:
- while do циклы;
- до тех пор;
- после идёт какое-то условие, которое нам необходимо записать;
- какой-то призыв к действию, а где-то он может опускаться просто, чтобы язык стал более лаконичны.
Но изначально у нас циклы называются, чаще всего while do.
Что мы с вами делаем дальше? По нашей блок схеме нам нужно было ответить на вопрос, рассматриваемый сейчас элемент массива больше максимального или нет? Опять же, некоторые знания английского языка позволяет нам прочитать написанное.
Обратите внимание, что часть текста написана друг под другом, а часть с отступами. С одной стороны, это делается для простоты восприятия, с другой стороны, в некоторых языках программирования, например, в Python, используется для отделения блоков кода друг от друга. Есть ряд действий, которые необходимо совершать, некоторые количество раз внутри цикла. Нам надо понять.
● Необходимо проверять и выполнять условие, пока мы в цикле или где-то снаружи тоже нужно делать?
● Вот max = numbers[index] надо делать, только если условия верное или всегда?
На эти вопрос отвечают отступы. Если вы посмотрите, то после while вот эти три строчки находятся немного в глубине, с отдалением от левого края. Эти строчки находятся внутри цикла. То есть весь блок кода мы будем повторять, пока цикл будет выполняться. То же самое относится и к строчке 7. Она у нас находится в отдалении от if. Значит, если условие будет выполняться, тогда мы будем выполнять именно эту строчку. Но как только мы снова попадаем под нашу букву «i», в if уходим на один уровень. Это будет означать, что пошёл следующий блок кода. Если на вопрос if numbers [index] > max мы ответим утвердительно, то будем выполнять эту строчку, а потом перейдём к следующей. А если ответ на вопрос будет отрицательным, тогда мы, соответственно, просто пропустим тот блок кода, который нужно было выполнять и сразу перейдём к следующему.
Это очень удобно. В некоторых языках необходимо брать текст скобочки, в том же Паскале необходимо было выделять, например, блоками begin end (begin — начало, end — конец). Какой-то блок кода могли обернуть этими скобками, но вот в языке Python было придумано такое решение, когда мы просто делаем отступы, и с помощью этих отступов визуально становится понятно, к чему относится та или иная строчка. К циклу while относятся эти три строчки, и мы не перейдём на 9 строку до тех пор, пока этот цикл будет выполняться. Пока он будет крутиться, как любят говорить программисты. Как только условие перестанет выполняться, то есть index станет равен или больше, чем size. То эти три строчки мы пропустим и сразу перейдём на следующую.
Переведём псевдокод на язык программирования Java
Посмотрите немного на код. Мы сейчас попробовали его прочитать. Зная перевод слов, можно понять, что происходит. Есть некоторый цикл, который начинается со слова while (до тех пор), есть некоторые условия, if (если).
Посмотрим на этот код уже не на псевдокоде, то есть без каких-то у нас синтаксических особенностей, а на языке программирования Java. Напомню, что в некоторых языках необходимо указывать тип, создаваемой нами, переменной. Чтобы понимать, что будет храниться внутри переменной. Обратите внимание, в Java две квадратные скобки означают массив. Массив некоторых чисел. В Python можно обходиться без этого, но вот код на Java будет выглядеть так. Если вы попробуйте сравнить с тем, что было на псевдокоде, то всё очень похоже. Нам единственное необходимо оформить этот код в соответствии с синтаксисом языка программирования Java. Сами числа здесь задаются не в квадратных скобках, а в фигурных скобках. В конце каждой строчки нам необходимо поставить точку с запятой. Помимо этого, необходимо указывать тип значения, который будет храниться в этой переменной. Всё остальное максимально похоже на то, что у нас было до этого.
Обращу ваше внимание в Java не обязательно делать эти отступы, вместо отступов блоки кода можно выделять скобками. Но визуально гораздо удобнее воспринимать программу, если соблюдать отступы. Даже если конкретный язык программирования синтаксически этого не требуют, например, в Python это прямо жёсткое требование, и если вы это условия не соблюдаете, программа будет работать неправильно или не будет работать вообще. То в Java это условие не обязательное, но крайне желательное, если вы напишете всё с выравниванием по левому краю, то вашу программу читать станет очень неудобно. Поэтому даже в тех языках, где это не требуется, отступы используются. Итак, надеюсь, что с этой программой вам также всё понятно. Попробуйте сравнить то, что было на псевдокоде. То, что написано в языке Java. У нас есть просто ряд маленьких синтаксических особенностей, которые нам необходимо выполнять, а далее соответственно, всё у нас работает, как и должно.
Что есть программа?
Теперь познакомимся поближе с этой инфраструктурой. Для этого нам необходимо посмотреть, что такое программа. Программа на самом деле это простой текстовый файл с некоторым расширением. То, что мы писали ранее на псевдокоде или на Java, это просто текст, отформатированный по каким-то правилам. Помимо этого, если мы хотим, чтобы наша программа по двойному щелчку запускалась или, какие-то действия происходили, когда мы по ней кликаем, у неё должно быть расширение. И вот знакомые вам расширение указаны в левом блоке. Все вы знаете про jpg, png, txt, mp3 и так далее. То есть, есть много разных расширений, с которыми вы знакомы.
Что же такое расширение?
Какая магия хранится за этим? Маленький секрет в том, что расширения нужны только для нашего удобства. Например, когда мы два раза кликнем по картинке, компьютер понимал, что с картинкой надо делать. Если компьютер знает, что у неё расширение .png, он запустит программу, способную открыть этот файл. Если расширение файла не указано, компьютер сам спросит у нас, в какой программе необходимо запустить файл, по которому сейчас два раза кликнули? Так вот, в компьютере есть некоторые переменные (ячейки памяти), хранящими соответствия между расширениями и программами, умеющими работать с этими расширениями. Например, после запуска текстового файла, ваш компьютер (операционная система) смотрят в таблицу соответствия между расширением и программами. Выбирают нужную программу и запускают её. В запущенную программу передаётся файл, который вы хотели открыть.
С картинками всё так же. Если вы два раза кликнули по картинке, то система запустит нужную программу и передаст в неё изображение. Если соответствие не настроено, то мы можем сами выбрать программу для запуска.
Можете попробовать запустить, например, обработчик текстов. Тот же самый Microsoft Word и попробовать открыть в нём изображение. Ошибки произойдут не потому, что Word, не умеет работать с этим файлом, а потому, что он настроен для другой информации. Можно и картинку отобразить в текстовом редакторе и увидеть её содержимое, но фактически это не текст. Поэтому в операционных системах делают просто связки, что одно файлы открываются в этих программах, а другие файлы в других программах.
Если вы работаете не на Windows, а на Linux или Ubuntu, то, скорее всего, знаете, что расширение файлов не обязательно. Когда мы работаем в операционной системе Unix, то очень часто можем самостоятельно сказать, что в какой программе нужно запускать файл. Расширение файлов — это просто некоторые удобства для нас, чтобы системе было проще помочь нам.
Итак, возвращаемся. Есть программа, написанная нами, это какой-то текстовый файл. Мы добавляем к нему расширение. Например:
для Java — .java;
для Python — .py;
для Java Script — .js.
Для каждого языка зафиксировано своё расширение. Если вы создали программу с таким расширением, то дальше системе будет проще понять, что же с файлом делать. Итак, с текстовым файлом с расширением наша программа исходный код имеет.
Ошибки бывают трёх видов
Теперь рассмотрим, какие бывают ошибки в программировании:
● Синтаксические.
● Выполнения.
● Логические.
Синтаксические ошибки
Когда вы пишете программу, на каком-то конкретном языке. То есть вот алгоритм составили, программа должна это решать. Учли всё, что вам необходимо и начали писать решение на конкретном языке программирования. У этого языка программирования есть набор требований, например, надо поставить точки с запятой, условия взять в скобки или, наоборот, скобки не ставятся, ставится двоеточие, то есть, есть некоторые набор требований для конкретного языка программирования. И если вы какое-то требование упустили, например, не поставили точку с запятой, то это будет синтаксической ошибкой. Транслятор вашего языка программирования, когда попытается преобразовать вашу программу в более удобный для себя вид, в котором сможет передать его следующему посреднику, он не справится с задачей. Не сможет, например, отделить одну команду от другой просто потому, что между ними почему-то не стоит точка с запятой, и просто перестанет работать. Это ошибки, которые проще всего найти, ведь транслятор вам просто подскажет, что вот в нужной строчке в какой-нибудь пятой строке у вас, например, не хватает точки с запятой, скорее всего. Попросит вас её исправить. Вы её исправите и запустите программу ещё раз. Это вот синтаксические ошибки, они относятся к конкретному языку программирования.
Ошибки выполнения
Когда синтаксически ваша программа написана, верно, но, например, вы начали делить на 0. В школьной математике вы сами прекрасно знаете, что делить на ноль нельзя, но компьютер заранее это проверить не может. Например, вы делите число «а» на число «b». В одном случае, когда одни входные данные, дали число «b» у вас, например, равно 2 и всё прекрасно работает. А когда вы поменяли входные данные, взяли другие наборы чисел или ещё что-то поменяли, у вас число «b» в одном случае стало, например, 0. И вы взяли какое-нибудь число и поделили его на 0. Программа не знает, что с этим делать, но на этапе синтаксического анализа, когда она только проверяла, насколько красиво вы написали программу, она поверить это не смогла. Что вы будете делать, зависит от заданных входных параметров. Когда-то это работает, когда-то нет. Это называется ошибками выполнения, когда по ходу выполнения программы получилось так, что конкретно в этом случае программа сломалась, что-то работает неправильно. Такие ошибки найти уже сложнее, потому что здесь вам помогает транслятор, а там эти ошибки выявляются не всегда.
Логические ошибки.
Когда у вас сам алгоритм составлен неверно. Например, вы хотели найти максимальное число, но где-то поменяли знак не в ту сторону и нашли минимальное число. При этом синтаксические у вас всё работает, верно. Ошибок выполнения никаких не возникает, но результат выполнения программы не соответствует тому, что нужно было получить. Эти ошибки возникают уже на этапе составления алгоритма, на этапе проектирования блок-схем, когда вы определяете, что же нужно было сделать.
Логические ошибки почти так же сложно найти, как ошибки выполнения. Но я бы сказал, что их даже сложнее найти. Потому что вам компилятор про них или интерпретатор вообще ничего не скажет. Если ошибки выполнения всё-таки можно детектировать с помощью вашего транслятора. Когда вы в каких-то ситуациях поделили на 0, то всё это появилась. В логических ошибках только вы сами сможете найти проблему, когда поймёте, что программа почему-то сделала не то, что вы от неё хотели.
Поэтому вот когда вы пишете ваши программы, обращайте внимание на то, что могут быть такие типы ошибок. С синтаксическими вам поможет транслятор. Ошибки выполнения нужно просто смотреть и понимать, где ваша программа может сломаться, и это всё может зависеть от входных данных. А логические ошибки — просто необходимо очень аккуратно готовить ваши алгоритмы, блок-схемы, и потом аккуратно же переводить их в синтаксис языка, выбранного для написания программы.
Статья написана по материалам лекции: «Что такое программирование и языки программирования»: Ильнар Шафигуллин, компания «GeekBrains».