Для программистов, инженеров данных, специалистов по данным и всех, кто программирует на данных

Часть I: Концептуальная основа

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

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

Итак, приступим. При программировании правильно думать об этом, смотреть на это с точки зрения другого человека. Эту идею можно сформулировать как следующий принцип:

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

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

Хорошо, тогда что такое простота? Оказывается, что определить простоту напрямую непросто (не говоря об этом иными словами), и это не так уж и полезно. Вместо этого лучше было бы понять это через его противоположность - сложность. Потому что, по крайней мере, в контексте кодирования, как будет показано, простота - это не что иное, как избегание сложности. Однако избежать сложности можно, только поняв ее. Теперь, когда у нас есть второе важное понятие в нашем исследовании предмета программирования - сложность - давайте разберемся, что это такое.

Что такое сложность?

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

Это НЕ о вычислительной сложности

Возможно, наиболее распространенное использование термина «сложность» в информатике и смежных областях - это вычислительная сложность, которую часто также называют просто сложностью. Вычислительная сложность имеет очень специализированное значение: это мера эффективности выполнения алгоритмов или количества ресурсов, требуемых алгоритму для работы. В частности, при анализе сложности рассматриваются два наиболее важных и распространенных ресурса: Время и Пространство. Например, существуют разные алгоритмы сортировки массивов, такие как сортировка по выбору, пузырьковая сортировка, сортировка вставкой, сортировка слиянием, быстрая сортировка, сортировка кучи и т. Д. Для одного и того же массива каждый из этих алгоритмов требует разного количества места и времени для выполнения. Это потребление ресурсов для каждого алгоритма определяет их соответствующую вычислительную сложность. Чем больше ресурсов использует алгоритм, тем он сложнее. Математически сложность вычислений представлена ​​так называемой нотацией Big O.

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

Теперь перейдем к следующей теме - цикломатической сложности.

Цикломатическая сложность

Согласно Википедии,

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

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

z = x * 2

его сложность 1. Так как для этого кода есть только один путь. Но для следующего кода сложность увеличена, теперь она составляет 2

if (condition is true) then
    z = x * 2
else
    z = x * 4

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

Концепция цикломатической сложности полезна, поскольку она дает представление о природе сложности кода. Это должен знать любой серьезный программист. Тем не менее, как бы он ни был важен, он не является достаточно широким и не объясняет типы сложности, которые обычно возникают в программировании обработки данных. Почему? Потому что он ориентирован на измерение потока управления программы. Анализ потока управления, хотя и важный фактор в парадигмах программирования, в DPP многие вычисления носят декларативный характер (как в SQL), где нет потока управления, а их сложность происходит из других источников. Даже в большинстве случаев операторов if-else, switch или любых условных их семантика может быть преобразована в декларативную, что устраняет проблемы с потоком управления. (это будет основной темой в части III). Чтобы понять тип сложности, связанный с DPP, вводится новая концепция, которую я называю структурной сложностью.

Структурная сложность

Может быть, для объяснения структурной сложности лучше начать с чего-то другого, аналогии из математики, где объекты отчетливы и ясны. Хотя поначалу примеры по аналогии могут показаться слишком тривиальными, идея является достаточно мощной с аналитической точки зрения и помогает понять сложность кода в DPP и, возможно, сложность в целом.

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

В любом случае, это линейное уравнение:

24 = 2x                                     (1)

Легко решить, не правда ли? Просто разделите обе стороны на 2, и решение будет x = 12. Теперь рассмотрим следующее уравнение.

24 = 2x + 5x — 3x + 6x + 4x                 (2)

Это уравнение кажется более сложным, чем первое; он длиннее; есть больше терминов и т. д. Но это не так. Его можно свести к

24 = 20x

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

Теперь рассмотрим следующее квадратное уравнение:

24 = 2x^2                                     (3)

Хотя оно выглядит проще (короче и компактнее), чем уравнение (2), на самом деле оно имеет более высокую степень сложности. Это не так интуитивно и легко понять, как линейное уравнение. Ее больше нельзя решить с помощью основных арифметических операций (+, -, *, / ). Это требует отдельного метода (квадратная формула) и большего количества математических устройств, таких как радикалы, иррациональные числа, множественные решения и т. Д. (Фактически, современная формулировка квадратичной формулы должна была подождать до 1637 года Рене Декарта.) Говоря с точки зрения структурной сложности (2x^2), более сложная структура, чем (2x + 5x — 3x + 6x + 4x), поскольку ее сложнее понять и решить.

Используя эту аналогию в области программирования, следующий вложенный цикл

for i in array1:
    for j in array2:
        Do something involving i and j at once

является более сложным, чем следующий фрагмент кода, состоящий из двух циклов.

for i in array1:
    Do something with i
for j in array2:
    Do something with j

Почему? Поскольку в первом случае необходимо отслеживать значения индексов, i и j, вместе со всеми переменными и логикой, связанной с ними одновременно, по мере выполнения обоих циклов ( он усложняется по мере усложнения циклов и операций в них). Две петли переплетаются, и одна не может быть отделена от другой. Во втором случае каждый цикл не зависит от другого и включает в себя отслеживание только одного за раз. Значит проще!

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

с участием трех уровней SELECT, более сложен, чем три последовательных запроса SELECT.

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

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

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

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

Далее: Программирование обработки данных: подход к разработке программного обеспечения (3): разделение проблем ››

Первоначально опубликовано на https://objectacademy.com.