0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p><strong>Помеченные данные</strong>- это одна из ключевых тем нашего курса, поэтому крайне важно разобраться с этим уроком и понять всё, что здесь объясняется.</p>
1
<p><strong>Помеченные данные</strong>- это одна из ключевых тем нашего курса, поэтому крайне важно разобраться с этим уроком и понять всё, что здесь объясняется.</p>
2
<h2>Недостатки имплементации</h2>
2
<h2>Недостатки имплементации</h2>
3
<p>Давайте посмотрим на реализацию (<em>имплементацию</em>), которую мы сделали до этого и разберём те недостатки, которые она в себе несёт.</p>
3
<p>Давайте посмотрим на реализацию (<em>имплементацию</em>), которую мы сделали до этого и разберём те недостатки, которые она в себе несёт.</p>
4
<p>Достаём имя карты, damage и применяем его к здоровью игрока и после этого получаем новое здоровье.</p>
4
<p>Достаём имя карты, damage и применяем его к здоровью игрока и после этого получаем новое здоровье.</p>
5
<p>С первого взгляда видно, то что здесь у нас нет никакой абстракции и мы работаем напрямую с парами, что не очень здорово. Например, если наши карты станут другие, они станут сильно сложнее и будут содержать больше информации, нам придётся полностью переписывать игровую логику и использовать здесь какую-то другую структуру. Естественно нам нужна какая-то абстракция.</p>
5
<p>С первого взгляда видно, то что здесь у нас нет никакой абстракции и мы работаем напрямую с парами, что не очень здорово. Например, если наши карты станут другие, они станут сильно сложнее и будут содержать больше информации, нам придётся полностью переписывать игровую логику и использовать здесь какую-то другую структуру. Естественно нам нужна какая-то абстракция.</p>
6
<h2>Прячем реализацию</h2>
6
<h2>Прячем реализацию</h2>
7
<p>Создаём отдельный файл под реализацию карты конкретного типа. Например, у нас есть простая карта и процентная карта, которая снимает урон в зависимости от того какой процент ей сказали снимать. make - это конструктор , который возвращает пару, getName извлекает имя (это car в паре) и damage извлекает cdr из карты и применяет его к переданному здоровью (второй параметр). Это классическая абстракция.</p>
7
<p>Создаём отдельный файл под реализацию карты конкретного типа. Например, у нас есть простая карта и процентная карта, которая снимает урон в зависимости от того какой процент ей сказали снимать. make - это конструктор , который возвращает пару, getName извлекает имя (это car в паре) и damage извлекает cdr из карты и применяет его к переданному здоровью (второй параметр). Это классическая абстракция.</p>
8
<h2>Используем абстракцию</h2>
8
<h2>Используем абстракцию</h2>
9
<p>Используем make, чтобы создать карту, а getName и damage для извлечения того, что нам нужно.</p>
9
<p>Используем make, чтобы создать карту, а getName и damage для извлечения того, что нам нужно.</p>
10
<p>Есть один маленький нюанс. В одном случае функции damage нужно здоровье, в другом нет. В JavaScript функции работают так, что если у нас есть второй параметр и он не всегда обязательный, то его можно не передавать. Это будет важно в дальнейшем когда мы сведём всё в один интерфейс.</p>
10
<p>Есть один маленький нюанс. В одном случае функции damage нужно здоровье, в другом нет. В JavaScript функции работают так, что если у нас есть второй параметр и он не всегда обязательный, то его можно не передавать. Это будет важно в дальнейшем когда мы сведём всё в один интерфейс.</p>
11
<h2>Проблема: не знаем тип карты</h2>
11
<h2>Проблема: не знаем тип карты</h2>
12
<p>Мы берём какую-то карту и после этого мы должны извлечь её имя, но мы не знаем её тип. Это слово само сюда ворвалось естественным образом, то есть нам нужно знать с какой картой мы работаем для того, чтобы понимать какой модуль к ней применить.</p>
12
<p>Мы берём какую-то карту и после этого мы должны извлечь её имя, но мы не знаем её тип. Это слово само сюда ворвалось естественным образом, то есть нам нужно знать с какой картой мы работаем для того, чтобы понимать какой модуль к ней применить.</p>
13
<p>Функция getName у нас одинаковая (это могло быть не так), но функция damage разная и она зависит от того, с каким типом мы сейчас работаем. Из этого примера видно, что когда у нас появляется многообразие типов, с которыми мы хотим работать одинаковым способом, то обычный подход перестаёт работать целиком и полностью, потому что мы теперь не знаем, что здесь находится. И для этого нам нужно их как-то различать. Мы должны точно знать, что сейчас нам пришло, чтобы вызвать соответствующие функции.</p>
13
<p>Функция getName у нас одинаковая (это могло быть не так), но функция damage разная и она зависит от того, с каким типом мы сейчас работаем. Из этого примера видно, что когда у нас появляется многообразие типов, с которыми мы хотим работать одинаковым способом, то обычный подход перестаёт работать целиком и полностью, потому что мы теперь не знаем, что здесь находится. И для этого нам нужно их как-то различать. Мы должны точно знать, что сейчас нам пришло, чтобы вызвать соответствующие функции.</p>
14
<h2>Решение: помеченные данные</h2>
14
<h2>Решение: помеченные данные</h2>
15
<p>Есть методика, которая называется<strong>помеченные данные</strong>. Всё сводится к тому, что мы берём какие-то данные и помечаем их какой-то специальной меткой, которая называется метка<em>типа</em>. Именно она будет определять с чем мы работаем.</p>
15
<p>Есть методика, которая называется<strong>помеченные данные</strong>. Всё сводится к тому, что мы берём какие-то данные и помечаем их какой-то специальной меткой, которая называется метка<em>типа</em>. Именно она будет определять с чем мы работаем.</p>
16
<p>Как мы будем это использовать? У нас появляется модуль type, который в себе содержит несколько функций: attach и contents. Работают они крайне просто.</p>
16
<p>Как мы будем это использовать? У нас появляется модуль type, который в себе содержит несколько функций: attach и contents. Работают они крайне просто.</p>
17
<p>Теперь в нашей функции make мы используем attach, который первым параметром принимает метку - это имя типа, а вторым параметром те данные, которые мы там конструируем. У функции getName теперь тоже есть отличие. Мы используем функцию contents, которая принимает на вход нашу карту и именуем её self. Мы обозначаем её так, потому что она ссылается сама на себя. self - это помеченная карта, поэтому сначала нужно из неё извлечь контент и делается это с помощью contents. Отсюда видно, что наш модуль type построен очень грамотно с точки зрения модульности. Мы можем его применять абсолютно к любым данным и нам вообще не важна их структура.</p>
17
<p>Теперь в нашей функции make мы используем attach, который первым параметром принимает метку - это имя типа, а вторым параметром те данные, которые мы там конструируем. У функции getName теперь тоже есть отличие. Мы используем функцию contents, которая принимает на вход нашу карту и именуем её self. Мы обозначаем её так, потому что она ссылается сама на себя. self - это помеченная карта, поэтому сначала нужно из неё извлечь контент и делается это с помощью contents. Отсюда видно, что наш модуль type построен очень грамотно с точки зрения модульности. Мы можем его применять абсолютно к любым данным и нам вообще не важна их структура.</p>
18
<h2>Устройство type внутри</h2>
18
<h2>Устройство type внутри</h2>
19
<p>attach представляет собой еще одну пару поверх данных, где первый параметр - это метка типа, а второй - данные, с которыми мы работаем. Функция contents возвращает сами данные, а функция typeTag возвращает имя тега (это нужно для того, чтобы узнать тип сущности, с которой мы работаем).</p>
19
<p>attach представляет собой еще одну пару поверх данных, где первый параметр - это метка типа, а второй - данные, с которыми мы работаем. Функция contents возвращает сами данные, а функция typeTag возвращает имя тега (это нужно для того, чтобы узнать тип сущности, с которой мы работаем).</p>
20
<h2>Тесты</h2>
20
<h2>Тесты</h2>
21
<p>С использованием отдельных типов карт наши тесты будут выглядеть так:</p>
21
<p>С использованием отдельных типов карт наши тесты будут выглядеть так:</p>
22
<p>Во-первых, карты теперь нужно импортировать отдельно, они у нас содержатся в собственных модулях. Имена функций у них пересекаются, что достаточно важно. Нет смысла делать разные имена когда подразумевается одна и та же работа у одинакового типа данных. В следующих уроках мы увидим это особенно явно и чётко, почему их нужно делать одинаковыми. По этой причине мы импортируем модули как имена, а не загружаем функции напрямую из-за совпадения имён.</p>
22
<p>Во-первых, карты теперь нужно импортировать отдельно, они у нас содержатся в собственных модулях. Имена функций у них пересекаются, что достаточно важно. Нет смысла делать разные имена когда подразумевается одна и та же работа у одинакового типа данных. В следующих уроках мы увидим это особенно явно и чётко, почему их нужно делать одинаковыми. По этой причине мы импортируем модули как имена, а не загружаем функции напрямую из-за совпадения имён.</p>
23
<p>Теперь создание карт выглядит следующим образом: вызываем make с приставкой конкретного модуля.</p>
23
<p>Теперь создание карт выглядит следующим образом: вызываем make с приставкой конкретного модуля.</p>
24
<p>В данном курсе, для простоты, мы работаем с двумя картами, но их может быть больше и они могут быть гораздо сложнее. Наша система уже позволяет расширять эту функциональность без проблем.</p>
24
<p>В данном курсе, для простоты, мы работаем с двумя картами, но их может быть больше и они могут быть гораздо сложнее. Наша система уже позволяет расширять эту функциональность без проблем.</p>
25
<h2>Итоги</h2>
25
<h2>Итоги</h2>
26
<ul><li>Реализацию необходимо прятать</li>
26
<ul><li>Реализацию необходимо прятать</li>
27
<li>Иногда вызываемый код зависит от типа</li>
27
<li>Иногда вызываемый код зависит от типа</li>
28
<li>Типы можно ввести через специальные метки</li>
28
<li>Типы можно ввести через специальные метки</li>
29
<li>Типы помогают определить сущность</li>
29
<li>Типы помогают определить сущность</li>
30
</ul><p>Работать с парами напрямую подразумевая, что это карты - не очень правильно, потому что получается достаточно хрупкий код, который легко будет меняться, если поменяется реализация карт. Иногда вызываемый код зависит от типа и если у вас есть множество подобных типов, которые решают одну задачу и могут взаимозаменяться, то нужен какой-то механизм выбора. Поэтому вводятся теги, которые позволяют нам определять с каким типом данных мы сейчас работаем.</p>
30
</ul><p>Работать с парами напрямую подразумевая, что это карты - не очень правильно, потому что получается достаточно хрупкий код, который легко будет меняться, если поменяется реализация карт. Иногда вызываемый код зависит от типа и если у вас есть множество подобных типов, которые решают одну задачу и могут взаимозаменяться, то нужен какой-то механизм выбора. Поэтому вводятся теги, которые позволяют нам определять с каким типом данных мы сейчас работаем.</p>