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

Пары JS: Составные данные

Пары

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

Ошибки

Работая с парами, очень легко допустить ошибку, которая будет выглядеть так:

Argument must be a pair, but it was ...

Парой является только то, что создано с помощью конструктора cons. Если по какой-то причине в селекторы произошла передача не пары, то результатом будет как раз такая ошибка. Проверить это очень легко:

const pair = 3;
car(pair); // Argument must be a pair, but it was '3'

То же самое, если передать селектору строку вместо пары:

const pair = 'hello';
car(pair); // Argument must be a pair, but it was 'hello'

Конспект урока

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

import { cons, car, cdr } from 'hexlet-pairs';

// Конструктор
const pair = cons(8, 7);

car(pair); // 8
cdr(pair); // 7

const pair2 = cons(3, pair);

Устроены они достаточно просто и используют структуру данных, которая называется парой. Пар в самом языке JavaScript не существует, мы их реализовали с помощью отдельной библиотеки, и выше можно увидеть пример того, как они используются. Мы импортируем из библиотеки конструктор cons и селекторы car и cdr. Конструктор создаёт пару, а селекторы служат для извлечения из пары первого значения (с помощью car) и второго значения (с помощью cdr). Всё достаточно просто и очень похоже на реализацию точек из прошлого урока.

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

import { cons, car, cdr, toString } from '@hexlet/pairs';

const pair = cons('first', 'second');
console.log(car(pair)); // => first
console.log(cdr(pair)); // => second

// don't do it
console.log(pair); // => { [Function] pair: true }

console.log(toString(pair)); // => (first, second)

https://repl.it/@hexlet/js-compound-data-pair

Давайте посмотрим, как представлены наши точки с помощью пар:

import { cons, car, cdr } from 'hexlet-pairs';

const makePoint = (x, y) => cons(x, y);

const getX = point => car(point);
const getY = point => cdr(point);

const toString = point => String(point);

Здесь всё предельно просто: makePoint — это функция, которая принимает x и y и вызывает конструктор пары с этими аргументами. То же самое с селекторами: getX и getY принимают на вход точку и вызывают с этой точкой car и cdr соответственно.

Можно заметить, что сработало бы даже такое определение:

const makePoint = cons;

const getX = car;
const getY = cdr;

Здесь всё верно с синтаксической точки зрения и с точки зрения получения конечного результата. Но с таким способом определения есть некоторые проблемы: по сути, когда мы делаем такое присваивание, получается, что makePoint и cons являются одним и тем же объектом. Кто-то может сказать, что они ссылаются на одну и ту же функцию, но это уже тонкости реализации конкретного языка программирования. На практике это означает, что, запустив построенный таким образом код, вы не увидите вызова функций makePoint, getX или getY, потому что их фактически не существует. При отладке вы не найдёте этих функций в трассировке стека. Вы можете захотеть увидеть все вызовы, например, makePoint, но вы точно не захотите отслеживать в вашей программе все вызовы cons, которые могут использоваться не только для точек, а вообще для любых библиотек. Поэтому мы не используем такое определение, но о нём нужно знать, чтобы понимать, как в целом всё работает.

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

const point1 = makePoint(1, 2);
const point2 = makePoint(10, -2);

const segment = makeSegment(point1, point2);

startSegment(segment); // (1, 2)
endSegment(segment);   // (10, -2)

Нам нужно сделать две точки (потому что любой отрезок представлен двумя точками). После этого мы используем конструктор makeSegment и передаём туда наши точки, а с помощью селекторов startSegment и endSegment мы получаем точки. Важно, что мы получаем именно точки, потому что это тоже составные данные со своими селекторами, с помощью которых можно получать примитивные значения и производить над ними какие-либо манипуляции при необходимости.


Дополнительные материалы

  1. История возникновения cons/car/cdr

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

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

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

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Для полного доступа к курсу нужна профессиональная подписка

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

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

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

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

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

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

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

Есть вопрос или хотите участвовать в обсуждении?

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

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»