Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Реализация пар Python: Составные данные

Как видно, совершенство достигается не тогда, когда уже нечего прибавить, но когда уже ничего нельзя отнять.

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

pair = cons(a, b)

a == car(pair)  # True
b == cdr(pair)  # True

Итак, пара — это соединение a и b, при этом a мы получаем через car, b — через cdr; a и b — это какие-то другие данные и они тоже могут быть парами. По сути, пара — это конструктор, селекторы и правила, которые определяют соотношения между конструктором, селекторами и данными.

def cons(a, b):
    def f(message):
        if message == "car":
            return a
        if message == "cdr":
            return b
    return f

def car(pair):
    return pair("car")

def cdr(pair):
    return pair("cdr")

Начнем с определения cons: это функция, которая принимает a и b. При этом внутри себя она содержит другую функцию, которая будет возвращена наружу. Внутри этой другой функции мы делаем проверку, и если message равен 'car', возвращаем a, если 'cdr' — возвращаем b. Итак, в результате создания пары снаружи оказывается функция. Как же она работает? Очень просто: селекторы принимают пару и вызывают ее как функцию, передавая в нее соответствующие сообщения. Если это селектор car, то передается сообщение 'car', если селектор cdr — то 'cdr'. Пара — это функция, которая принимает сообщение, поэтому мы получаем то или иное значение в зависимости от переданного сообщения. Значения сохраняются во внутренней функции за счет замыкания.

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

pair = cons(4, 5)
# def cons(a, b):
#     def f(message):
#         if message == "car":
#             return a
#         if message == "cdr":
#             return b
#     return f

car(pair)  # 4
# pair('car')

В этом примере мы можем представить, что пара — это функция, которая принимает сообщение, и, если сообщение равно 'car', то возвращает 4, если 'cdr' — возвращает 5. Вызывая селектор car с аргументом pair, мы внутри вызываем эту функцию с сообщением 'car'.

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

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

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

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


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и соглашаетесь с «Условиями использования»