Чтобы понять различные конструкции языка программирования и его возможности, полезно знать некоторые критерии оценки.

В этой статье я представлю и прокомментирую вам основные критерии оценки языка программирования.
Однако будьте осторожны, выбирая язык для определенной задачи, недостаточно учитывать, сколько я объясню, но также и функциональность языка в рамках конкретной задачи.
Например, Python, безусловно, является отличным языком для выполнения численных вычислений или анализа данных, но, безусловно, не подходит для систем жесткого реального времени. Или язык C отлично подходит для программирования микроконтроллеров, но он становится непригодным для выполнения сложных численных расчетов.
В зависимости от цели, которую необходимо достичь, мы можем определить языки, которые можно использовать, и степень поддержки решаемой проблемы; выбор между тем, какие из них использовать (или начать изучать), может основываться на том, что я собираюсь вам рассказать.
Прежде чем мы начнем, позвольте мне сказать, что все, что я вам здесь рассказываю, свободно вдохновлено книгой « Концепции языков программирования », Р. В. Себеста.

Прежде чем мы начнем обсуждать концепции языков программирования, мы должны рассмотреть несколько предварительных сведений. Во-первых, мы объясняем некоторые причины, по которым студенты, изучающие информатику, и профессиональные разработчики программного обеспечения должны изучать общие концепции языкового проектирования и оценки. (Роберт В. Себеста)

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

К счастью, Роберт объясняет некоторые из них. Какие?
• Читаемость
• Возможность написания
• Надежность
• Стоимость

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

Читаемость

Первым и наиболее очевидным критерием, безусловно, является удобочитаемость.
До 70-х, когда вычислительные ресурсы и память были ограничены, программирование основывалось на эффективности. В результате реализация языков программирования отразила эту характеристику.
Даже сегодня мы можем увидеть усилия по достижению эффективности, глядя на имена некоторых библиотек или функций C язык. Часто встречаются такие имена, как «stdio» или «strcmp». Хотя значение легко определить, сегодня мы не увидели бы причины такого типа имен и, более того, критиковали бы их использование, поскольку их трудно читать.

Начиная с 70-х, мы начали понимать, что одним из основных компонентов производства программного обеспечения было не первое написание, а модификация (и исправление ошибок); скажем, обслуживание. Это понимание было частью зарождающейся концепции жизненного цикла программного обеспечения: у программного обеспечения есть жизнь разработки, выраженная в разные фазы. Среди этих этапов обслуживание является наиболее важным.
Акцент сместился с эффективности на продуктивность программиста.

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

Не забудьте рассмотреть удобочитаемость в контексте приложения. Язык легко использовать в контексте, для которого он был разработан. Например, использование языка, предназначенного для анализа данных, для создания СУБД, несомненно, затруднит чтение кода.

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

Почему это проблема? Потому что программист выучит подмножество языковых конструкций.
Это нормально? Давайте посмотрим на это на примере.
Предположим, что Боб изучил и использовал небольшой набор конструкций в своем проекте; он работал последние 6 месяцев, и его продукт - это десятки строк кода и десятки файлов. В тот момент, когда Алиса, которая годом позже выучила подмножество конструкций, отличное от Боба, должна будет поддерживать работу Боба, у нее возникнут большие трудности с пониманием того, что имел в виду Боб. Часы будут проходить на Stackoverflow, проклиная Боба за то, что он плохой программист.

Проще говоря: это может произойти, когда язык позволяет делать одно и то же по-разному.

Другой пример проблемы простоты: возможность перегрузки оператора. Хотя это очень полезная функция в языке, она может вызвать путаницу, если читатель не знает, как была произведена перегрузка.

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

Другой пример, давайте посмотрим на ассемблерный код.
При сборке большинства современных процессоров используется RISC ISA. Это означает, что для выполнения сложных задач вам понадобится комбинация нескольких инструкций; минимум две операции доступа к памяти и одна типа ALU (логическая арифметическая единица). Инструкция высокого уровня переводится в более низкоуровневые инструкции; как если бы мы разделяли большую инструкцию на более простые инструкции.
Следуя потоку данных и управления длинными маленькими инструкциями, программа может быть сложной.

C code:
int square(int num) {
return num * num;
}
     
Assembly Code RISC-V gcc 8.2.0
square(int):
addi sp,sp,-32
sd s0,24(sp)
addi s0,sp,32
mv a5,a0
sw a5,-20(s0)
lw a4,-20(s0)
lw a5,-20(s0)
mulw a5,a4,a5
sext.w a5,a5
mv a0,a5
ld s0,24(sp)
addi sp,sp,32
jr ra

Таким образом, слишком маленькая простота может привести к трудностям понимания, слишком простота может привести к трудностям чтения. Помните, добродетель находится между крайностями.

Ортогональность
Определение ортогональности немного громоздко: возможность комбинировать примитивные конструкции языка небольшим количеством способов для создания структур данных и управления.
Если любая возможная комбинация примитивов разрешена и имеет смысл, тогда мы имеем полную ортогональность.
Отсутствие ортогональности приводит к исключениям в поведении языка.

Например, предположим, что у нас нет возможности определять указатели на массивы на языке C; это помешало бы нам создавать очень полезные структуры данных, хотя в языке есть как указатели, так и массивы.
Или представьте ситуацию, в которой вы вынуждены использовать два разных оператора для вычисления суммы либо между целыми числами, либо между плавает. Операция суммирования концептуально одинакова, но мы не можем использовать один и тот же оператор в разных конструкциях (int или float).

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

Давайте посмотрим на простоту за счет ортогональности с чистыми функциональными языками.
Эти языки «простые», потому что они могут выполнять любую задачу с помощью одной конструкции, вызова функции, вставки, самое большее, в других вызовах.
Вычисление происходит через непрерывные вызовы функций, применяемых к определенным параметрам.
С другой стороны, в императивном программировании (считайте его аналогом функционального) вычисление выполняется через переменные, присваивания и алгоритмы. Есть разница в простоте.

Однако, как вы теперь понимаете, у всего есть своя цена. Такая простота обходится без эффективности.

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

Bool IsEnd = True;
Int IsEnd = 1.

Такой подход вызывает замешательство у читателя, поскольку он не знает, какое значение дать числу «1», логическому или числовому. Ему следует полагаться на хорошую практику именования переменных, которая не всегда присуща всем программистам.

Синтаксис
На удобочитаемость влияет синтаксис элементов языка, то есть то, как элементы языка выглядят. Например, как появляются специальные слова языка (while, class, for,…).

Различная читаемость цикла for в Python по сравнению с C:

Ptyhon:
for x in range (0,3):
   print “hello world”
C:
for (int i = 0; i<3, i ++) {
printf (“hello world”);
}

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

Более того, этот пример позволяет увидеть разницу в создании составных операторов: в C (и его потомках) используются пары скобок, в python используется отступ. Что из двух более читабельно? Есть противоречивые мнения.

Есть и другие языки, в которых пары ключевых слов используются для явного указания начала и конца составного оператора. Например, что-то вроде:

for x from 0 to 3:
print (“hello world”);
end

Хотя я не уверен, что этот синтаксис принадлежит конкретному языку, он более или менее распространен во всех функциональных языках (или тех, которые реализуют функциональную парадигму).

Структура намного ближе к письменному языку и по этой причине более читабельна. Тем не менее, как правило, такая удобочитаемость ложится большей нагрузкой на компилятор / интерпретатор. Чем больше мы удаляемся от машинного языка, тем сложнее становится хороший перевод кода.

Еще одна важная проблема - выразительность простого оператора.
Например, использование grep в системах UNIX непонятно для любого, кто подходит к таким системам. Только самые опытные пользователи знают, что в редакторе ed команда / regular_expression / позволяет искать совпадение с регулярным выражением в строке; они также знают, что добавление g к команде делает ее глобальной командой; они также знают, что добавление p после команды приводит к тому, что вывод команды печатается.
Итак, g / regular_expression / p, то есть grep , печатает все строки в файле, содержащие указанную подстроку, через регулярное выражение

Еще одна синтаксическая проблема: возможность использовать специальные слова языка в качестве имен переменных; в таком случае это повлияет на читаемость.

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