Объясняю каждую строчку. Основы. Простые переменные разной степени переменчивости

Объясняю каждую строчку. Основы. Простые переменные разной степени переменчивости

Продолжение серии статей об основах ремесла. В первой части мы разбирали самые простые команды. А сегодня займёмся самыми простыми данными.

Часть первая:

В прошлый раз я упоминал "формулу" - "Программа = алгоритмы + структуры данных". В первой части я рассказывал о том, что "Алгоритм" - это пошаговая инструкция. В нашем случае, пошаговая инструкция для компьютера. Почему ещё нужны какие-то структуры данных? Любой алгоритм подразумевает взаимодействие с чем-то. Что-то на входе, что-то на выходе. Какие-то изменения. Алгоритм сбора шкафа из IKEA бесполезен, если у нас нет запчастей, а после его выполнения нет шкафа.

Миром ЭВМ, который меняют алгоритмы, и являются те самые "структуры данных". Даже ваша любимая ви-туб-кошкожена не обходится без структур данных. Там есть алгоритм, который ловит движения какой-то тёти и выводит вам анимешную тян. Но этот алгоритм не сможет работать без данных, то есть без информации о том, где какой палец, где глаза, какого это всё цвета, как это освещается. И этими данными невозможно управлять, если они не организованы в структуры.

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

Переменные

Без примеров будет тяжело.

user_input = input("I need a number: ") numbers = [] while user_input.isdigit() and int(user_input) >= 0: numbers.append(int(user_input)) user_input = input("I need more numbers: ") checking_now = 0 maximum = 0 while checking_now < len(numbers): if numbers[checking_now] > maximum: maximum = numbers[checking_now] checking_now += 1 print("our biggest number is", maximum)
var numbers = new List<int>(); var user_input = Console.ReadLine(); while (int.TryParse(user_input, out var number) && number >= 0) { numbers.Add(number); user_input = Console.ReadLine(); } var maximum = 0; var checking_now = 0; while (checking_now < numbers.Count) { if (numbers[checking_now] > maximum) maximum = numbers[checking_now]; checking_now += 1; } Console.WriteLine($"Самое большое число - {maximum}");

user_input, numbers, checking_now, maximum - это и есть переменные. Переменная - это такой слот, если хотите, под какие-нибудь данные. Многие языки позволяют создавать их на ходу. Изредка необходимо заранее расписывать, какие нужны переменные.

А теперь немного об отдельных свойствах переменных:

Переменность

Обычно нет проблем, чтобы написать что-нибудь такое.

var remained = 500; ... remained = 450; ... remained = 400;

Переменная, она так и называется, потому что её можно менять. Верно? Ну, иногда это свойство создаёт проблемы.

var monday = 1; var tuesday = 2; var wednesday = 3; var thursday = 4; var friday = 5; var saturday = 6; var sunday = 7;

А потом приходит какой-нибудь особо упоротый любитель "code golf", который ради того, чтобы сэкономить полтора байта или сделать какое-нибудь особо "элегантное" решение пишет что-нибудь вроде

if (IsWinterNow) { monday = -1; ... sunday = -7; }

А вы потом, спустя сотню строк проверяете, а не выходной ли сегодня часом такой конструкцией:

if (day == 6 || day == 7) { MarkTodayAsDayOff(); } //Кто-то зимой, очевидно, будет херачить без выходных

В общем, некоторые вещи должны оставаться неизменными. Для этого практически во всех языках есть способы "зафиксировать" переменную, превратив её в константу. Пара примеров того, что не должно меняться во время выполнения программы из C#.

const int SecondsInDay = 24 * 60 * 60; const int NotFound = 404; const string NotFoundErrorMessage = "Page is not found."; readonly Vector2 ZeroVector = new Vector2(0, 0);

Правилом хорошего тона считается "фиксировать" всё, что не планируешь менять. На всякий случай. А ещё есть языки, где переменных в принципе нет, есть только константы, или иммутабельные переменные, по умному. Очень сильно упрощает оптимизацию, между прочим.

Тип

У каждой переменной есть тип. Число, строка, список, буква, вектор, модель, светильник, запрос к серверу. То есть то, что может лежать в этой ячейке. Если совсем умными словами "тип - это множество возможных значений". Какие именно типы бывают, мы ещё будем говорить очень долго и муторно. Пока что чуть-чуть о том, как языки с типами работаем.

Вернёмся к C#. Он относится к языкам со статической типизацией. Это значит, что у каждой переменной должен заранее определён её тип. Ещё до того, как текст будет превращён в программу. Ключевое слово "var" (от "variable"), которые вы видели, появилось не сразу, сначала тип переменной надо было прописывать явно всегда и везде. Да и теперь "var" можно написать только там, где из контекста легко понять тип переменной.

int checking_now = 0; List<int> numbers = new List<int>(); string errorMessage = "Something went wrong"; //Тоже самое, что: var checking_now = 0; var numbers = new List<int>(); var errorMessage = "Something went wrong";

Одно из последствий статичности - нельзя сделать вот так:

int number = 5; number = 7; // Это ещё норм number = "Seven"; // А вот так уже написать не получится, компилятор вас завернёт

Примером языка с динамической типизацией как раз является python.

number = 7 number = "Seven" #Вообще никаких проблем

Почему тренд и мода явно любят статичность? Потому что в случае со статичной типизацией ошибка "чёт не могу сложить цифру 7 и абырвалг" свалится ещё на этапе компиляции (до того, как текст превратиться в программу). А в случае с динамической типизацией она свалится во время выполнения вашей программы. И дай бог, во время выполнения на тестовой машине, а не перед носом у охреневшего пользователя.

И python, и C# относят к языкам с сильной типизацией. Это значит, что в этих языках попытка сложить 7 и "абырвалг" хотя бы считается ошибкой. В отличие от языков со слабой типизацией, где можно сделать 7 + "абырвалг" и ещё умножить это на Йожина с Бажин. К таким относятся, например, Javascript и PHP. Классический пример работы со слабой типизацией в Javascript:

Я её не осуждаю<br />
Я её не осуждаю

Как вы можете догадаться, слабая и динамическая типизация очень сильно способствуют хаосу. Поэтому тренд и мода явно благоволят типизации сильной и статической. Даже в языках, где "исторически сложилось" иначе пытаются что-то с этим делать. Например, в python можно в коде дописать, где какой тип желательно иметь, а потом проверить код на соответствие вашим желаниям специальными инструментами. А в случае с JavaScript придумали целый "аддон" - TypeScript, где вы сначала дописываете типы переменных везде, где только можно, а потом проверяете у себя, всё ли всему соответствует. А браузер потом игнорирует эти "дописки" и выполняет проверенный на "вшивость" код как обычный JavaScript.

Пока освежал свои знания, нашёл чуть более задротское изложение темы:

Имя

Да, у каждой переменной есть имя. Имя переменной - это важно. Правильно именовать вещи - это вообще очень важно.

Почему? Цитата одного из классиков.

Любой дурак может писать код, который будет понятен компьютерам. Хорошие программисты пишут код, который будет понятен людям

Мартин Фаулер

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

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

Идея простая - имя переменной должно описывать то, что там хранится. Описывать словами (двумя-тремя, даже четырьмя, если нужно), а не одной буквой. Проблемы начинаются, если вы сами не понимаете, что тут хранится и зачем. Эта проблема касается всех имён и служит служит таким сигнальным огнём: если вы не понимаете и близко, как что-то назвать в вашей программе, возможно, вы не понимаете, как она должна работать.

Тут нам, к сожалению, очень сильно подгадили учёные, а точнее, математики. Математики с их дурной привычкой называть свои переменные x, y, z, a, b, c, i, j и так далее. Оказывается, то, что годилось для формул, которые можно охватить взглядом, недопустимо для строительства гигантских систем хотя бы на десятки тысяч строк кода (а ведь по масштабам отрасли это ещё не так много). Недопустимо на уровне "мы не осуждаем тех, кто за такое пиздит ногами в подсобке после код-ревью".

Просто сравните эти два кусочка кода на Pascal. Так я писал в школе, когда ещё обучался программированию у учителей информатики (то есть математиков):

for i:=0 to 9 do for j:=0 to 9 do begin a[i,j]:=b[i,j] - c[i,j]; (*Представьте себе на месте этого комментария ещё строк 10*) (*И где-то там я ВСЕГДА путал i и j местами. ВСЕГДА!*) end;

А потом я осознал, что можно писать немного иначе, и это было как конец недельного запора, настолько легче мне стало жить:

for row:=0 to 9 do for column:=0 to 9 do begin profit[row,column]:=revenue[row,column] - expenses[row,column]; (*Да, строка стала длиннее, зато это теперь не похоже на высшую математику*) (*И уж теперь row и column вы точно хрен перепутаете*) end;

И, сука, до сих пор находятся люди, которые в примерах программ для КУРСОВ, ОБУЧАЮЩИХ ПРОГРАММИРОВАНИЮ, пишут i вместо index. Не делайте так, пожалуйста.

Bonus content, так сказать, о важности имён переменных.

Одна дама после ночёвки у любовника пошла с утра в ванную, чтобы помыться. Уже на выходе из ванной она встретила своего мужчину и спросила его:
- Слушай, там же у тебя два полотенца висело. Одно было помечено, как "М", а второе как "Ж". Ну, типа одно "мужское", а второе "женское". Я воспользовалась тем, что "Ж". Ничего страшного?
Мужик задумчиво почесал свою щетину и немного растерянно ответил:
- Ну, вообще-то "М" - это "Морда".

Просто услышанный мною ещё в детстве анекдот

Из объяснительной записки:12.12 на складе №4 был разгружен ламинат "..." с большим количеством брака. Пачки были отсортированы мной и подписаны, хорошие пачки "Х", а плохие пачки "П". При возврате товара поставщику, начальник склада ошибочно посчитал, что "Х" означает "хуёвые", а "П" означает "пиздатые", что в корне меняло ситуацию. Грузчики его поддержали и на возврат был отгружен качественный товар. Виновным в данном инциденте себя не признаю, услуги транспортной компании оплачивать отказываюсь.

https://bash.im/quote/414893, спасено из веб-архива

Самые простые структуры данных

Теперь, когда мы поговорили о "слотах", в которых эти самые структуры данных хранятся, будем говорить о том, какими они бывают. Уже вроде как стало общеизвестной вещью, что компьютерам намного проще оперировать с двоичными числами. А это значит, что структуры данных могут быть всем, что можно к двоичным числам свести. Например:

Целые числа

Внезапно, да? Тут есть всего лишь один нюанс, но перед тем, как его объяснять, я на пальцах расскажу о разнице между высокоуровневыми и низкоуровневыми языками программирования. Низкоуровневый язык - это язык, близкий к машине (ассемблеры всех цветов и расцветок, C, из относительно новых - rust). Высокоуровневый язык - это язык, близкий к человеку (python, javascript). Это не две категории, это целый спектр, где одно плавно переходит в другое.

Так вот в чём дело. Если язык прям совсем высокоуровневый, то у нас один тип данных - "целое число". Даже если это целое число состоит из миллионов цифр и занимает несколько мегабайт. "Бог тебе судья" как бы скажет тебе твоя ЭВМ и будем перемножать два таких числа несколько минут. Так делают те же python и javascript.

Языки уровнем чуть пониже уже начинают озадачивать программиста тем, насколько эти числа влазят в память и как быстро с ними работается вашей ЭВМ. И там, как правило, есть несколько типов данных навроде "очень большое число", "большое число", "число", "число поменбше", "число маленькое вообще жесть". Названия этих типов, например, в C# byte, short, int, long. Так как в редакторе постов DTF нет таблиц, вот список того, насколько большими допустимо делать числа в зависимости от их размера:

  • 1 байт - от -128 до 127 или от 0 до 256
  • 2 байта - от -32 768 до 32 767 или от 0 до 65 536
  • 4 байта - от -2 147 483 648 до 2 147 483 647 или от 0 до 4 294 967 296
  • 8 байт - от -9 223 372 036 854 775 808 до 9 223 372 036 854 775 808 или от 0 до 18 446 744 073 709 551 616

Для всего, что больше, уже надо обычно искать какую-нибудь отдельную библиотеку.

Вещественные числа

Так, а теперь прошу вспомнить школьный курс физики, совсем чуть-чуть. Там, чтобы написать очень большое число с 20-30 нулями, спереди или сзади, никто не писал такого: 65 700 000 000 000 000 000 000 000. Вместо этого писали 6,57 * 10^25. Это позволяло не таскать постоянно при расчётах за собой хвостом 23 нуля. Так как компьютеры делались изначально учёными и для учёных, то когда им понадобилось хранить не целые числа для своих расчётов, они такие "О! Вот так и сделаем!"

Так исторически сложилось, что хранение вещественных чисел во всех ЭВМ оказалось стандартизировано. Практически вне зависимости от уровня языка, оно будет соответствовать IEEE 754. И чаще всего используются числа одинарной и двойной точности.

Одинарная точность - 4 байта, точность до семи знаков после запятой (одна десятимиллионая), не больше 38 нулей. Двойная точность - 8 байт, точность до пятнадцати знаков после запятой (одна квадриллионная), не больше 308 нулей после запятой.

Обратите внимание на "точность". С каждой операцией накапливается небольшая ошибка. И иногда случаются смешные вещи, которые заставляют вас рвать волосы на заднице. Например, 2,2 + 2,2 оказывается НЕ РАВНО 4,4.

Числа двойной точности.<br />
Числа двойной точности.

Числа с фиксированной запятой

Они же decimal. Внутри они устроены точно так же, как и целые числа, но только там где-то посреди воткнута запятая. Размер ограничен, но зато никакой ошибки округления, сколько ты не складывай. Может случиться переполнение, конечно, но вместо чуть-чуть неправильного числа ты получишь ошибку.

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

Буквы

Каждой букве ставится в соотвествие число. Тут есть много способов, которые называются кодировками. И вот ведь засада, единого стандарта нет.

Ну как, сначала был ASCII, где был один байт. А туда лезет от 0 до 255, как я уже писал выше. От 0 до 127 там были английские буквы, числа, знаки препинания. А всё остальное, ну-у-у-у-у-у, зависит от ситуации. В какой-то момент, когда компьютерами и интернетами стало пользоваться не только Министерство Обороны США, всплыли откуда-то русские буквы, немецкие умляюты, арабская вязь и, о ужас, иероглифы. В один байт это никак не влезало.

Долгое время кодирование текста представляло из себя дикий запад, а ещё в середине нулевых Джоэль Спольски писал отчаянные статьи на тему "запомните, суки, нет такой вещи, как кодировка по умолчанию, нет". Но теперь хотя бы в интернете всё захватил UTF-8 (97,7%, если верить консорциуму W3).

Он очень хитро и удобно для всех устроен. Смотрим на первый байт. Если там от 0 до 127, то там всё точно так же, как и в ASCII, которая ещё Хрущёва застала. В иных случаях мы берём от 2 до 4 байт из которых складывается одна буква. Там получается что-то около двух миллионов возможных комбинаций. Хватает и на китайские иероглифы, и на японские, и ещё на эмодзи остаётся.

Истина/Ложь

Так называемые булевые переменные, у которых только два значения - True и False. Ну, или 0/1. Появились сами собой, как самая маленькая еденица информации в компьютере. И очень плотно вошли почти во все языки. Причина - ветвления и циклы. True/False - определяет какая часть программы будет выполняться дальше. Поэтому тут подробностей будет дальше больше, чем о прочих типах.

Какую информацию они обычно хранят? Они хранят ответ на вопрос, на который можно ответить "Да" или "Нет". Если вы в переменной такого типа пытаетесь хранить что-то другое, вы используете её неправильно.

Отсюда очень-очень часто переменные этого типа называются чем-то на is. isReady, isAlive, isCalculationFinished и так далее. Если ваша булёвая переменная называется как-то типа "status" или "error" вы что-то делаете не так. Если у вас, например, три возможных состояния, а вы пытаетесь как-то ими управлять с помощью одной или двух булевых переменных, вы опять-таки, что-то делаете не так.

Что ж, сейчас самое время, чтобы дать краткий экскурс в булеву арифметику. Ну, или в логику. В учебниках И и ИЛИ (и ИСКЛЮЧАЮЩЕЕ ИЛИ) пишут капсом, чтобы отдельно дать понять - наше "ИЛИ" работает немножко не так, как ваше "или".

Сначала ИЛИ. ИЛИ правдиво, когда правдиво хотя бы одно из вывыражений. Итак, пример. Допустим, вы молодой парень, к которому домой вломились какие-то непонятные мужики и среди них покойный Максим "Тесак" Мартинкевич спрашивает у вас "ты пидорас ИЛИ ты педофил".

  • Если вы любите мальчиков и чтоб помоложе, то вы отвечаете ДА.
  • Если вы любите свой пол, но зрелых, то вы отвечаете ДА.
  • Если вы любите девочек, но чтоб помоложе, то вы отвечаете ДА.
  • Если вас интересуют девочки, и девочки такого возраста, что вас за это не посадят, то вы отвечаете НЕТ.

Теперь И. И правдиво, когда правдивы все выражения. Допустим, покойный Максим "Тесак" Мартинкевич спрашивает у вас "ты пидорас И ты педофил?".

  • Если вы любите мальчиков и чтобы помоложе, то вы отвечаете ДА.
  • Если вы любите свой пол, но зрелых, то вы отвечаете НЕТ.
  • Если вы любите девочек, но чтобы помоложе, то вы отвечаете НЕТ.
  • Если вас интересуют девочки, и девочки такого возраста, что вас за это не посадят, то вы отвечаете НЕТ.

Есть ещё редко используемое ИСКЛЮЧАЮЩЕЕ ИЛИ. Это такое или, которое правдиво, если правдиво только одно из выражений. "Выбирай - или я, или водка" - подразумевает, что любовь истинна, если истинно только ОДНО из выражений. Допустим, к вам опять прикопался Тесак. На этот раз с "ты пидорас ИСКЛЮЧАЮЩЕЕ ИЛИ ты педофил"?.

  • Если вы любите мальчиков и чтобы помоложе, то вы отвечаете НЕТ.
  • Если вы любите свой пол, но зрелых, то вы отвечаете ДА.
  • Если вы любите девочек, но чтобы помоложе, то вы отвечаете ДА.
  • Если вас интересуют девочки, и девочки такого возраста, что вас за это не посадят, то вы отвечаете НЕТ.

Через И, ИЛИ можно соединить и три выражения, и четыре, если нужно. Правила останутся теми же. ИЛИ правдиво, если правдиво хотя бы одно из выражений. И правдиво только если правдивы все выражения. А ещё из можно комбинировать. Чтобы не запутаться, где, что и как, используйте скобочки.

Примеры на C#, выдернутые из моего драгоценного проекта (&& - И, || - ИЛИ, ^^ - ИСКЛЮЧАЮЩЕЕ ИЛИ). Кстати, во второй строчке приходится делать такое странное сравнение, потому что вещественные числа неточны и эта неточность со временем накапливается.

var crossingWallBottomLine = (newHitbox.Top > oldHitbox.Top) && (newHitbox.Top > wall.Bottom) && (oldHitbox.Top <= wall.Bottom); var touchingHorizontalFringes = System.Math.Abs(oldHitbox.Left - wall.Right) < Math.Constants.Epsilon || System.Math.Abs(oldHitbox.Right - wall.Left) < Math.Constants.Epsilon; //Мы не можем написать здесь oldHitbox.Left == wall.Right из-за неточности вещественных чисел //Math.Constants.Epsilon = то ли 0.000001, то ли 0.00001 //&& - означает И, || - означает ИЛИ

Примеры из головы на python:

calculation_stopped = percent_done >= 100 or calculation_aborted green = 0 yellow = 1 red = 2 current_signal = 1 stop_now = current_signal == yellow or current_signal == red successfully_processed = percent_done = 100 and error is None

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

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

Итак, какое-то условие. Выбор одного из двух. Да, в большинстве языков все эти if'ы и when'ы работают от бинарной проверки. Есть языки, где можно написать что-то типа "if 1000 then" или "while 'non-empty string' do". Так вот, никогда так не делайте, пожалуйста. Даже если ваш ЯП разрешает засунуть в if число, строку или жабку, делайте нормальную проверку по условию. "if 1000 != 0" или "while 'non-empty string' != '' do". Просто подумайте, что проще понять постороннему читателю: "Если деньги" или "Если деньги ещё есть"?

Условия и циклы появляются в программах постоянно. И в каждом из них есть выражение, которое возвращает true/false. Поэтому понимать, как они работают будет не лишним.

Ну и ещё один маленький трюк. Иногда случается такое, что у вас образуется жирное условие из нескольких обычных условий и вам тяжело понять, что там проверяется. Так вот, можно разделить его на части, поместив каждую в отдельную переменную, и через имя переменной объяснив, то все эти выражения проверяют. Сравните два куска кода:

if mouse_x >= hitbox_left and mouse_x <= hitbox_right and mouse_y >= hitbox_top and mouse_y <= hitbox_bottom \ and right in clicked_buttons and wheel_change > 0: trigger_combo()
cursor_touched_hitbox = mouse_x >= hitbox_left and mouse_x <= hitbox_right and mouse_y >= hitbox_top and mouse_y <= hitbox_bottom right_button_clicked = right in clicked_buttons wheel_scrolled_down = wheel_change > 0 combo_triggered = cursor_touched_hitbox and right_button_clicked and wheel_scrolled_down if combo_triggered: trigger_combo()

Второй кусок кода длинее, но это только потому, что он подробнее объясняет, что именно мы проверяем в if'е.

Bonus content. Перечисления

Это всё ещё сравнительно примитивный тип данных, но он есть не в каждом языке, поэтому проходит по категории "Bonus content". Тем не менее, пропускать не рекомендую, так как тут есть одна важная мысль. Буду рассказывать на примере C#.

Я уже говорил, что булевая переменная не годится, если вам надо хранить больше, чем ответ на вопрос "да/нет". Но что делать, если это ответ на вопрос типа "какой цвет светофора горит теперь?" или "какую ноту мы теперь играем?". Первое, что приходит в голову, это вот такое решение из примера выше:

int green = 0; int yellow = 1; int red = 2; int currentSignal = 1; bool stopNow = currentSignal == yellow || currentSignal == red;

Я специально указал типы переменных явно. Итак, эта идея вполне себе рабочая, но есть некоторые неудобства. Во-первых, нет до конца ясности, что делать со случаем, когда переменной current_signal присваивается -1 или 1000. Во-вторых, можно написать что-то типа "status = yellow;" или "crystalls_amount = red;". То есть, на деле это всё ещё число, но мы изо всех сил делает вид, что это цвет горящего огня светофора.

Именно здесь появляется такой тип типов данных, как перечисления. А теперь та самая важная мысль: вы можете создавать свои типы данных. Невозможно работать со сплошными нагромождениями чисел, нужно создавать какие-то свои абстракции. И самыми простыми "кастомными" типами данных являются перечисления. В C# можно написать так:

public enum TrafficLightSignal { Green, Yellow, Red } TrafficLightSignal current = TrafficLightSignal.Yellow; bool stopNow = current == TrafficLightSignal.Yellow || current == TrafficLightSignal.Red;

Видите, мы больше не имеем в коде дел с числами. Мы теперь работаем именно с сигналами светофора. Это значит, что нельзя теперь написать вот так:

TrafficLightSignal currentSignal = 1; //Обе линии теперь выдадут ошибку int trafficLightsAmount = TrafficLightSignal.Green; //Теперь так нельзя

Да, там "под капотом" всё ещё числа и если сильно надо до этого "под капотом" можно докопаться. Но даже компилятор теперь знает, что в этой переменной мы храним сигнал светофора, а не число.

Кстати, помните выше был пример с днями недели? Так вот:

public enum WeekDay { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } //Больше никаких чисел

Пример из жизни. Кто следил за моим проектом знает, что я выбрал свой, особый подход к музыке. В частности, у меня там в типавосьмибитный писк перерабатываются ноты. Всем со школы известно, что нот семь: "до", "ре", "ми", "фа", "соль", "ля", "си". Музыканты их обычно обозначают латинскими буквами C, D, E, F G, A, H. (Иногда вместо H - B). Ноты группируются в октавы. После "Си" первой октавы идёт "До" второй. Октавы идут в таком порядке: Субконтр, контр, большая, малая, первая, вторая, третья, четвёртая, пятая. Чтобы мне не приходилось возиться с числами, я создал два перечисления. Собственно, как они выглядят:

public enum NoteType { C = 1, D = 2, E = 3, F = 4, G = 5, A = 6, H = 7 } //C# позволяет напрямую указать числа "под капотом". Тут я это сделал, кажется, просто для удобства public enum Octave { SubContra, Contra, Great, Small, OneLine, TwoLine, ThreeLine, FourLine, FiveLine }

Есть ещё у нот "модификаторы". Они же знаки альтерации (accidentals по-английский). Диез (Sharp) означает, что частота ноты должна быть на полтона выше, бемоль (Flat), что частота ноты должна быть на полтона ниже, бекар (natural) отменяет действие диезов и бемолей. Этот абзац был нужен, чтобы я мог дать пример кода, очищенный от радостей ООП:

public enum Accidental { None, Sharp, Flat, Natural } //Заметьте, мне пришлось учесть случай, когда нота ничем не "альтерирована" private const Single Semitone = 1.059463094F; Accidental accidental = GetAccidental(); switch (accidental) { case Accidental.None: case Accidental.Natural: frequency = notesFrequencies[note]; case Accidental.Sharp: frequency = notesFrequencies[note] * Semitone; case Accidental.Flat: frequency = notesFrequencies[note] / Semitone; }

Тут видно одно из самых ходовых применений перечислений: они хорошо ложатся на "многоветочные if'ы".

Аутро

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

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

Ссылки

Канал на ютубе

Паблик вк

Дискорд-сервер

Иллюстрация нарисована этим художником:

37
5 комментариев