<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 22:39:13 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="Xk9DM1TNip3XC3VCrsVRXvkjW10irJ5rXqbcXmr5SLGxnogEprMn_WFIUdqiyqEpOSp29yqbYMnjRkYKOP6v3w";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Стандартные интерфейсы | JS: Последовательности</title>
<meta name="description" content="Стандартные интерфейсы / JS: Последовательности: Разбираемся в преимуществах хорошей абстракции и выясняем, как писать код так, чтобы его было легче комбинировать">
<link rel="canonical" href="https://ru.hexlet.io/courses/sequences/lessons/conventional_interfaces/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Стандартные интерфейсы">
<meta property="og:title" content="JS: Последовательности">
<meta property="og:description" content="Стандартные интерфейсы / JS: Последовательности: Разбираемся в преимуществах хорошей абстракции и выясняем, как писать код так, чтобы его было легче комбинировать">
<meta property="og:url" content="https://ru.hexlet.io/courses/sequences/lessons/conventional_interfaces/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="VojRXpqJ_cittp0U1N4MJVjQM_ZMhJoYV3PtpyqiAri5WRppaPdQqBv1uYzY0fxSmNkeXESzZLrqk3fzeKXl1g" />
<script src="/vite/assets/inertia-DfXos102.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-cb8xch9l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc2MCwicHVyIjoiYmxvYl9pZCJ9fQ==--9348098e4053d798b6f34bee4ef66947540261e4/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-rafiki.png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T22:39:12.948Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"himMvEJ1EdN8NmfjsousypHiGtYzKWfvGOKZ6CTmgw1p-EeLsAu8s8p1Q3u-hFy9Ues3fDsemU2lAgO8duFkYw","topics":[{"id":45850,"title":"Добрый вечер! Почему \"Выше конкуренция\" не является в том числе корректным ответом - чем больше видов отверток, тем выше конкуренция!...","plain_title":"Добрый вечер! Почему \"Выше конкуренция\" не является в том числе корректным ответом - чем больше видов отверток, тем выше конкуренция!... ","creator":{"public_name":"Denis","id":258546,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":99047,"body":"Приветствую, Денис!\n\nЕсли нет стандарта, то каждый производитель будет выпускать свою собственную отвертку и таких отвёрток будут тысячи или больше. Таким образом конкуренция будет падать и каждый человек будет использовать для ремонта только какую-то свою особую отвертку. Это как раз и называется привязка к вендору, когда нужный инструмент выпускает только одна компания. А при наличии стандартного интерфейса производители начинают выпускать разные по характеристикам отвертки для него из которых у покупателя появляется выбор. Соответственно возрастает конкуренция.","topic_id":45850},{"creator":{"public_name":"Denis","id":258546,"is_tutor":false},"id":99070,"body":"**Роман Ашиков**, Спасибо!","topic_id":45850}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Стандартные интерфейсы","entity_url":null,"active":true}},{"id":7164,"title":"У меня получается, что в wordsCount в учительском решении функция map - лишняя. Вполне достаточно filter и reduce. Ревью - https://ru.hexlet.io/code_reviews/11833","plain_title":"У меня получается, что в wordsCount в учительском решении функция map - лишняя. Вполне достаточно filter и reduce. Ревью - https://ru.hexlet.io/code_reviews/11833 ","creator":{"public_name":"Игорь Б.","id":122370,"is_tutor":false},"comments":[{"creator":{"public_name":"Йоси Адлер","id":124498,"is_tutor":false},"id":13569,"body":"Тоже реализовал через `filter` и `reduce`. Хотя и понимал, что задание подразумевает разбитие одной задачи на несколько более простых подзадач, но все же не додумался, куда там можно присунуть еще и `map`. Однако решение учителя показало, что все возможно. Ну что ж. Век живи - век учись!","topic_id":7164},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":13240,"body":"Вы не уловили идею курса. Цель в том чтобы задачу разбить на подзадачи и делать их независимо, а так можно было все и в reduce засунуть, но тогда теряется весь смысл подхода \"разделяй и властвуй\".","topic_id":7164}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Стандартные интерфейсы","entity_url":null,"active":true}},{"id":34767,"title":"В упражнении про фильтры в решении учителя был такой вызов `map`, можно и здесь использовать:\n```\nconst values = map(getValue, filtered);\n```","plain_title":"В упражнении про фильтры в решении учителя был такой вызов map, можно и здесь использовать: const values = map(getValue, filtered); ","creator":{"public_name":"Sergei Bondarenko","id":172010,"is_tutor":false},"comments":[{"creator":{"public_name":"Stanislav Dzisiak","id":212236,"is_tutor":true},"id":75939,"body":"**Sergei Bondarenko**, приветствую!\n\nСпасибо. Поправил.","topic_id":34767}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Стандартные интерфейсы","entity_url":null,"active":true}},{"id":28931,"title":"Решил wordsCount, применив только одну функцию reduce. В задании же не написано, что нужно все или какие-то две из трех применять. В чем принципиально лучше решение учителя, или даже, наоборот - чем это хуже? https://ru.hexlet.io/code_reviews/119777 ","plain_title":"Решил wordsCount, применив только одну функцию reduce. В задании же не написано, что нужно все или какие-то две из трех применять. В чем принципиально лучше решение учителя, или даже, наоборот - чем это хуже? https://ru.hexlet.io/code_reviews/119777 ","creator":{"public_name":"Вадим С.","id":154224,"is_tutor":false},"comments":[{"creator":{"public_name":"Dmitriy Bataev","id":106130,"is_tutor":false},"id":62449,"body":"функцию `func` тяжело читать, слишком многословна ([Злые однострочники](https://ru.hexlet.io/blog/posts/sovershennyy-kod-zlye-odnostrochniki)). В функции по извлечению header, можно и обойтись без предварительного извлечения значения из элемента, но это опционально. С другой стороный mappedFirst\\mappedSecond, не очень помогают понять что происходит.","topic_id":28931}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Стандартные интерфейсы","entity_url":null,"active":true}},{"id":13047,"title":"Я - кулхацкер! Я обманул тесты)\n```\nexport const wordsCount = () => 2;\n```\n","plain_title":"Я - кулхацкер! Я обманул тесты) export const wordsCount = () => 2; ","creator":{"public_name":"Олег Ильин","id":133462,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":27317,"body":"Хехе, поправил тесты!","topic_id":13047}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Стандартные интерфейсы","entity_url":null,"active":true}},{"id":6263,"title":"Не могу понять что надо записывать в **text** в функции **wc(word, text)**\nесли у меня есть **element = head(dom)**, то запись будет **wc(word, element)** или надо уже содержимое элемента без тегов передавать **wc(word, value(element))** ?","plain_title":"Не могу понять что надо записывать в text в функции wc(word, text) если у меня есть element = head(dom), то запись будет wc(word, element) или надо уже содержимое элемента без тегов передавать wc(word, value(element)) ? ","creator":{"public_name":"Николай Полунин","id":124993,"is_tutor":false},"comments":[{"creator":{"public_name":"Николай Полунин","id":124993,"is_tutor":false},"id":11469,"body":"Добавил проверку на чистый лист и все заработало)","topic_id":6263},{"creator":{"public_name":"Николай Полунин","id":124993,"is_tutor":false},"id":11440,"body":"просто в случае когда я передаю просто **wc(word, element)** вылетает ошибка типов **TypeError: text.match is not a function** а во втором случае **wc(word, value(element))** ошибка **TypeError: pair is not a function**","topic_id":6263},{"creator":{"public_name":"Николай Полунин","id":124993,"is_tutor":false},"id":11442,"body":"```\nexport const wordsCount = (tag, word, dom) =>{\n return reduce((element, acc) => is(tag, element)? acc += wc(word,element) : acc , 0, dom);\n}\n```\nможет кто объяснить почему код не работает и что за ошибка \n** TypeError: text.match is not a function**","topic_id":6263},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":11455,"body":"`text` это текст внутри которого ведется поиск.\n\n`а во втором случае wc(word, value(element)) ошибка TypeError: pair is not a function` Это значит что в некоторых ситуациях, похоже, `element` не является парой, то есть скорее всего он равен null. Вам нужно воспользоваться отладочной печатью чтобы понять в чем ваш алгоритм не прав.\n\nРекомендую ознакомиться с нашим вспомогательным материалом: http://help.hexlet.io/article/7-how-to-debug-code","topic_id":6263}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Стандартные интерфейсы","entity_url":null,"active":true}},{"id":30079,"title":"Добрый день, не могу понять в чем дело в функции wordsCount. Увеличения счетчика не происходит, когда нужно проверить два тега. Предпоследний тест.\n\nhttps://ru.hexlet.io/code_reviews/130120","plain_title":"Добрый день, не могу понять в чем дело в функции wordsCount. Увеличения счетчика не происходит, когда нужно проверить два тега. Предпоследний тест. https://ru.hexlet.io/code_reviews/130120 ","creator":{"public_name":"Андрей Забелин","id":205551,"is_tutor":false},"comments":[{"creator":{"public_name":"Андрей Забелин","id":205551,"is_tutor":false},"id":65177,"body":"Дополню: getTags возвращает список отфильтрованных html-элементов. На примере предпоследнего теста - это будет два тега h2 с текстом \"haskell\".\n\ngetText возвращает список текстов из тегов.\ngetQuantity должен искать в каждом тексте вхождения искомого слова и увеличивать acc на единицу. Но он этого не делает)\n\nЯ думаю, что я как-то не так воспользовался функцией reduce","topic_id":30079},{"creator":{"public_name":"Андрей Забелин","id":205551,"is_tutor":false},"id":65189,"body":"Решил, пришел к выводу, что acc не увеличивается потому, что он обновляется с каждым новым переходом на элемент.\n\nПоправьте, если это не так.","topic_id":30079},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":65235,"body":"> Я думаю, что я как-то не так воспользовалася функцией reduce\n\nУгу, в первом варианте в callback-функции (ту, которую передаёшь в reduce) у тебя попросту нет аккумулятора (только один параметр `element`, но ведь нужен ещё аккумулятор). А результатом работы `reduce` является вызов callback-функции на последнем элементе обрабатываемой коллекции.","topic_id":30079}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Стандартные интерфейсы","entity_url":null,"active":true}},{"id":17771,"title":"день добрый\nподскажите по коду\n\n`// removed`\n\nругается \n\nArgument must be list, but it was 'undefined'\n \n at checkList (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/src/index.js:39:11)\n at Object.isEmpty (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/src/index.js:92:3)\n at toString (../../local/share/.config/yarn/global/node_modules/hexlet-html-tags/dist/index.js:49:12)\n at Object.it (__tests__/html-tags.test.js:22:41)\n at Promise.resolve.then.el (../../local/share/.config/yarn/global/node_modules/p-map/index.js:42:16)\n\nи даже на такой код ругается также\n\n`// removed`\n\nчто и где не так?","plain_title":"день добрый подскажите по коду // removed ругается Argument must be list, but it was 'undefined' at checkList (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/src/index.js:39:11) at Object.isEmpty (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/src/index.js:92:3) at toString (../../local/share/.config/yarn/global/node_modules/hexlet-html-tags/dist/index.js:49:12) at Object.it (__tests__/html-tags.test.js:22:41) at Promise.resolve.then.el (../../local/share/.config/yarn/global/node_modules/p-map/index.js:42:16) и даже на такой код ругается также // removed что и где не так? ","creator":{"public_name":"Антон Глушенков","id":183835,"is_tutor":false},"comments":[{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":37555,"body":"Отлично!","topic_id":17771},{"creator":{"public_name":"Антон Глушенков","id":183835,"is_tutor":false},"id":37550,"body":"ожидает список\nreturn node('p', value(element)); - вот это я ему даю","topic_id":17771},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":37549,"body":"> в файле с тестами на этой строке стоит expect(htmlToString(headersAsP)).toBe(result);\n\nУгу. Какой из этого можно сделать вывод? Скажите, что (какой тип данных) ожидает на вход функция `htmlToString`?","topic_id":17771},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":37493,"body":"Приветствую!\n\n`Argument must be list, but it was 'undefined'` - а вы понялиZ где ошибка возникает, т.е. в какой конкретно вызов функции передаётся аргументом значение `undefined` вместо ожидаемого списка (тип `list`)?\n\nПодсказка: это вызов производится в модуле с тестами и конкретное место можно найти в стектрейсе ошибки (то, что вы выше привели).","topic_id":17771},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":37552,"body":"> ожидает список\n\nда\n\n> return node('p', value(element)); - вот это я ему даю\n\nа что это за значение? Откуда взято это выражение?","topic_id":17771},{"creator":{"public_name":"Антон Глушенков","id":183835,"is_tutor":false},"id":37543,"body":"ни в одном уроке не было про стектрейсе ошибки\n\nя не понимаю что там написано","topic_id":17771},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":37546,"body":"> в этом уроке перечислены виды ошибок\n\nв этом уроке демонстрируется стектрейс\n\n> про то о чем я спрашиваю там нет ни слова\n\nмы сейчас обсуждали стектрейс ошибки\n\n> вы пишите что ошибка производится в модуле с тестами\n\nда, в месте где происходит проверка вашего кода\n\n> как я могу повлиять на нее если я не понимаю где она возникает\n\nв стектрейсе приведён адрес, зайдите в модуль с тестами и посмотрите место возникновения ошибки. Это позволит понять, почему она возникает - а править надо код в основном модуле, где пишите реализацию функции","topic_id":17771},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":37544,"body":"> ни в одном уроке не было про стектрейсе ошибки\n\nвернитесь к этому уроку:\n\nhttps://ru.hexlet.io/courses/introduction_to_programming/lessons/errors/theory_unit","topic_id":17771},{"creator":{"public_name":"Антон Глушенков","id":183835,"is_tutor":false},"id":37545,"body":"в этом уроке перечислены виды ошибок\n\nпро то о чем я спрашиваю там нет ни слова\n\nвы пишите что ошибка производится в модуле с тестами\n\nкак я могу повлиять на нее если я не понимаю где она возникает","topic_id":17771},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":37553,"body":"> вот это я ему даю\n\nкак мы уже установили выше (по крайней мере, из этого можно сделать вывод), ваша функция возвращает значение `undefined` вместо ожидаемого списка","topic_id":17771},{"creator":{"public_name":"Антон Глушенков","id":183835,"is_tutor":false},"id":37548,"body":"`at checkList (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/src/index.js:39:11)\n at Object.isEmpty (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/src/index.js:92:3)\n at toString (../../local/share/.config/yarn/global/node_modules/hexlet-html-tags/dist/index.js:49:12)\n at Object.it (__tests__/html-tags.test.js:22:41)\n at Promise.resolve.then.el (../../local/share/.config/yarn/global/node_modules/p-map/index.js:42:16)`\n\n\nошибка происходит в index.js и в html-tags.test.js\nк первому файлу у меня ессно нет доступа\nв файле с тестами на этой строке стоит expect(htmlToString(headersAsP)).toBe(result);\n\nмой код\n\n`// removed`\n\nчерез console.log на выходе то же что и надл -> '<p>haskell</p><p>prolog</p><p>haskell</p>'\n\nобъясните где ошибка не врублюсь","topic_id":17771},{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":37542,"body":"> нет я не понял где ошибка\n\nВы прочитали мою подсказку?","topic_id":17771},{"creator":{"public_name":"Антон Глушенков","id":183835,"is_tutor":false},"id":37554,"body":"переписал код \n\n`// removed`\n\nвсё заработало\n\nоказывается я ****балван**** общий ретурн забыл \n\n**_\n\n### СПАСИБО\n_**","topic_id":17771},{"creator":{"public_name":"Антон Глушенков","id":183835,"is_tutor":false},"id":37541,"body":"нет я не понял где ошибка\nпоэтому и попросил помочь","topic_id":17771}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Стандартные интерфейсы","entity_url":null,"active":true}},{"id":13508,"title":"[решил вот так](https://ru.hexlet.io/code_reviews/37296)\nПосмотрел решение учителя и понял что я немного схитрил и срезал углы, однако тесты это решение проходит.\nДопустимо ли оно? Или просто тесты не отлавливают какие то проблемы с ним.","plain_title":"решил вот так (https://ru.hexlet.io/code_reviews/37296) Посмотрел решение учителя и понял что я немного схитрил и срезал углы, однако тесты это решение проходит. Допустимо ли оно? Или просто тесты не отлавливают какие то проблемы с ним. ","creator":{"public_name":"Андрей Лукин","id":3739,"is_tutor":false},"comments":[{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":28388,"body":"Думаю, что ваше решение с ошибкой, потому что по условию задания функция `wordsCount` должна искать вхождения слова только в **теле (тексте) тега**, вы же расширяете зону поиска и в результаты могут попасть вхождения слова, которые являются другими частями тега, а не его текстом.\n\nДобавьте в группу тестов `#wordsCount` следующую проверку: \n`expect(wordsCount('h2', 'h2', dom)).toBe(0);` \nи посмотрите на результаты.","topic_id":13508}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Стандартные интерфейсы","entity_url":null,"active":true}},{"id":8637,"title":"Подскажите, плиз, что в моей функции `wordCount` не так.\n\n```\n// removed\n```\n\nОшибка: \n```\n TypeError: (0 , _htmlTags.wordsCount) is not a function\nat Object.it (__tests__/html-tags.test.js:26:37)\n```\n\nАлгоритм функции:\n1) создаем функцию с 3 параметрами: `тег`, `искомое слово`, `список`.\n2) создаем функцию `predicate` с параметром `элемент`, которая возвращает `true`, если условие совпадает, и `false` в обратном случае (условие: наличие `тега` в элементе и `искомого слова` в тексте).\n3) создаем функцию `func` с параметрами `элемент` и `аккумулятор`; если `predicate` возвращает `true`, то функция `func` прибавляет к `аккумулятору` количество вхождения `искомого слова`, используя функцию `wc`.\n4) вывоз функции `reduce` с аргументами: `func` - функция считающая количество вхождений `искомого слова` в тексте, начальное значение `аккумулятора` и `список`.\n\n\n\n","plain_title":"Подскажите, плиз, что в моей функции wordCount не так. // removed Ошибка: TypeError: (0 , _htmlTags.wordsCount) is not a function at Object.it (__tests__/html-tags.test.js:26:37) Алгоритм функции: 1) создаем функцию с 3 параметрами: тег, искомое слово, список. 2) создаем функцию predicate с параметром элемент, которая возвращает true, если условие совпадает, и false в обратном случае (условие: наличие тега в элементе и искомого слова в тексте). 3) создаем функцию func с параметрами элемент и аккумулятор; если predicate возвращает true, то функция func прибавляет к аккумулятору количество вхождения искомого слова, используя функцию wc. 4) вывоз функции reduce с аргументами: func - функция считающая количество вхождений искомого слова в тексте, начальное значение аккумулятора и список. ","creator":{"public_name":"Davud Kakhrimanov","id":139303,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":17095,"body":"`acc +=` ох уж эти мутации). Сложный код в этом месте, так не пишут.","topic_id":8637},{"creator":{"public_name":"Davud Kakhrimanov","id":139303,"is_tutor":false},"id":17082,"body":"Ясно. Без этого выражения лучше )). Спасибо!","topic_id":8637},{"creator":{"public_name":"","id":299,"is_tutor":false},"id":17073,"body":"Вас сразу должна настораживать фраза: ```...htmlTags.wordsCount) is not a function``` - то есть тесты не видят функцию. Как правило такие проблемы возникают когда была неправильно экспортирована функция.\n\nВ данном случае вы назвали свою функцию wordCount, а тесты вызывают функцию wordsCount.","topic_id":8637},{"creator":{"public_name":"Davud Kakhrimanov","id":139303,"is_tutor":false},"id":17080,"body":"теперь выдает другую ошибку\n```\n Argument must be pair, but it was 'quote'\n \n at checkPair (../../local/share/.config/yarn/global/node_modules/hexlet-pairs/dist/index.js:23:11)\n at car (../../local/share/.config/yarn/global/node_modules/hexlet-pairs/dist/index.js:56:3)\n at name (../../local/share/.config/yarn/global/node_modules/hexlet-html-tags/dist/index.js:29:31)\n at is (../../local/share/.config/yarn/global/node_modules/hexlet-html-tags/dist/index.js:35:18)\n at predicate (html-tags.js:20:100)\n at func (html-tags.js:21:34)\n at iter (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/dist/index.js:226:56)\n at iter (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/dist/index.js:226:38)\n at iter (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/dist/index.js:226:38)\n at Object.reduce (../../local/share/.config/yarn/global/node_modules/hexlet-pairs-data/dist/index.js:228:10)\n\n```\n\nуказывает на строчки 20 и 21:\n\n```\nconst predicate = element => is(tagName, element) && is(word, value(element));\n const func = (element, acc) => predicate(element) ? acc + wc(word, value(element)) : acc;\n```\nСкорее всего речь идет о `predicate(element)`, только я никак не пойму, почему там не пара, когда функция `reduce` передает туда значение `head(list)`? \n\n \n","topic_id":8637},{"creator":{"public_name":"","id":299,"is_tutor":false},"id":17081,"body":"```is(word, value(element))``` - вот здесь","topic_id":8637}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Стандартные интерфейсы","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":453,"slug":"js_sequences_conventional_interfaces_exercise","name":null,"state":"active","kind":"exercise","language":"javascript","locale":"ru","has_web_view":false,"has_test_view":false,"reviewable":true,"readme":"## html-tags.js\n\nРеализуйте и экспортируйте функцию `extractHeaders()`, которая извлекает тексты всех заголовков `h2` из переданного HTML и возвращает HTML, в котором каждый из этих текстов обернут в `p`.\n\nНапример, такой HTML в строковом представлении `<h2>header1</h2><h2>header2</h2><p>content</p>` превратится в такой `<p>header1</p><p>header2</p>`. Ниже развернутый пример.\n\n```javascript\nimport { node, append, make, reduce, toString as htmlToString } from '@hexlet/html-tags';\n\nconst html1 = append(make(), node('h2', 'header1'));\nconst html2 = append(html1, node('h2', 'header2'));\nconst html3 = append(html2, node('p', 'content'));\n// <h2>header1</h2><h2>header2</h2><p>content</p>\n\nhtmlToString(extractHeaders(html3));\n// <p>header1</p><p>header2</p>\n```\n\nРеализуйте и экспортируйте функцию `wordsCount()`, которая считает вхождения слова в\nопределенный тег. Для подсчета слов в тексте одного тега воспользуйтесь вспомогательной функцией `wc()`,\nкоторая уже импортирована в модуль *html-tags*.\n\n```javascript\nimport { make, append, node } from '@hexlet/html-tags';\n\nconst html1 = append(make(), node('h2', 'header1 lisp'));\nconst html2 = append(html1, node('p', 'content'));\nconst html3 = append(html2, node('h2', 'lisp header2 lisp'));\nconst html4 = append(html3, node('p', 'content lisp'));\n\nwordsCount('h2', 'lisp', html4); // 3\n```\n\n### Подсказки\n\n* Подсчет слов в тексте: `wc(word, text)`, где `word` искомое слово, а `text` это текст, в котором\n ведется поиск.\n\n ```javascript\n wc('what', 'what, what, who, what'); // 3\n wc('la', 'loli'); // 0\n ```\n\n* При необходимости вы можете самостоятельно импортировать функцию `toString()` из библиотеки `@hexlet/pairs-data` и использовать её для отладки решений. Эта функция возвращает строковое представление списка\n* При необходимости вы можете самостоятельно импортировать функцию `toString()` из библиотеки `@hexlet/html-tags` и использовать её для отладки решений. Эта функция возвращает строковое представление html-списка\n* Для разрешения противоречий в случае импорта нескольких функций с одинаковыми именами используйте псевдонимы (aliases)\n","prepared_readme":"## html-tags.js\n\nРеализуйте и экспортируйте функцию `extractHeaders()`, которая извлекает тексты всех заголовков `h2` из переданного HTML и возвращает HTML, в котором каждый из этих текстов обернут в `p`.\n\nНапример, такой HTML в строковом представлении `<h2>header1</h2><h2>header2</h2><p>content</p>` превратится в такой `<p>header1</p><p>header2</p>`. Ниже развернутый пример.\n\n```javascript\nimport { node, append, make, reduce, toString as htmlToString } from '@hexlet/html-tags';\n\nconst html1 = append(make(), node('h2', 'header1'));\nconst html2 = append(html1, node('h2', 'header2'));\nconst html3 = append(html2, node('p', 'content'));\n// <h2>header1</h2><h2>header2</h2><p>content</p>\n\nhtmlToString(extractHeaders(html3));\n// <p>header1</p><p>header2</p>\n```\n\nРеализуйте и экспортируйте функцию `wordsCount()`, которая считает вхождения слова в\nопределенный тег. Для подсчета слов в тексте одного тега воспользуйтесь вспомогательной функцией `wc()`,\nкоторая уже импортирована в модуль *html-tags*.\n\n```javascript\nimport { make, append, node } from '@hexlet/html-tags';\n\nconst html1 = append(make(), node('h2', 'header1 lisp'));\nconst html2 = append(html1, node('p', 'content'));\nconst html3 = append(html2, node('h2', 'lisp header2 lisp'));\nconst html4 = append(html3, node('p', 'content lisp'));\n\nwordsCount('h2', 'lisp', html4); // 3\n```\n\n### Подсказки\n\n* Подсчет слов в тексте: `wc(word, text)`, где `word` искомое слово, а `text` это текст, в котором\n ведется поиск.\n\n ```javascript\n wc('what', 'what, what, who, what'); // 3\n wc('la', 'loli'); // 0\n ```\n\n* При необходимости вы можете самостоятельно импортировать функцию `toString()` из библиотеки `@hexlet/pairs-data` и использовать её для отладки решений. Эта функция возвращает строковое представление списка\n* При необходимости вы можете самостоятельно импортировать функцию `toString()` из библиотеки `@hexlet/html-tags` и использовать её для отладки решений. Эта функция возвращает строковое представление html-списка\n* Для разрешения противоречий в случае импорта нескольких функций с одинаковыми именами используйте псевдонимы (aliases)\n","has_solution":true,"entity_name":"Стандартные интерфейсы"},"units":[{"id":1366,"name":"theory","url":"/courses/sequences/lessons/conventional_interfaces/theory_unit"},{"id":1520,"name":"quiz","url":"/courses/sequences/lessons/conventional_interfaces/quiz_unit"},{"id":1367,"name":"exercise","url":"/courses/sequences/lessons/conventional_interfaces/exercise_unit"}],"links":[],"ordered_units":[{"id":1366,"name":"theory","url":"/courses/sequences/lessons/conventional_interfaces/theory_unit"},{"id":1520,"name":"quiz","url":"/courses/sequences/lessons/conventional_interfaces/quiz_unit"},{"id":1367,"name":"exercise","url":"/courses/sequences/lessons/conventional_interfaces/exercise_unit"}],"id":692,"slug":"conventional_interfaces","state":"approved","name":"Стандартные интерфейсы","course_order":700,"goal":"Разбираемся в преимуществах хорошей абстракции и выясняем, как писать код так, чтобы его было легче комбинировать","self_study":null,"theory_video_provider":"vimeo","theory_video_uid":"170597958","theory":"Хорошая абстракция даёт нам много разных преимуществ. Одно из которых — это увеличение модульности программы. Выражается это в том, что разные куски кода нам гораздо проще комбинировать и, имея небольшой набор базовых функций и компонентов, мы за счёт их сочетания можем получать гораздо более сложное поведение и строить любые конструкции.\n\nВ реальной жизни мы с этим тоже встречаемся очень часто. Знакомый всем пример, который отражает эту идею — конструктор Lego. Этот принцип называется — использование **стандартных интерфейсов**. Практически все детали в этом конструкторе сделаны таким образом, что они друг с другом комбинируются разными способами. Всё это работает благодаря тому, что все детали содержат пипочки, которые расположены на одинаковом расстоянии друг от друга. И вы можете их соединять как угодно. Это отличный пример и приём, который показывает то, как, используя небольшой набор базовых компонентов, можно строить действительно сложные вещи. \n\nЕсть другие конструкторы, где большое количество деталей уникально, и в конечном итоге вы не сможете их особо комбинировать. Всё, что вы сможете сделать с такими конструкторами, строить заранее продуманные решения, которые предполагались для сборки производителем.\n\nВ программировании, по крайней мере в том, что мы уже знаем и продолжаем изучать, такими стандартными интерфейсами являются списки. Наши функции, в особенности функции высшего порядка, занимаются тем, что принимают на вход списки и выдают на выходе чаще всего также списки. Что очевидно позволяет выстраивать их в цепочки и получать достаточно сложное поведение.\n\n## Определение частоты слов в заголовках\n\n```\n| Обучение | 3 |\n| Практика | 2 |\n| Онлайн | 1 |\n| Хекслет | 1 |\n```\n\nДавайте рассмотрим достаточно простую задачу: вычислить частоту слов в заголовках. В результате должна получиться таблица, как в примере выше. Предположим, у нас есть HTML, в котором есть разные заголовки. И мы хотим посмотреть, какие слова в них наиболее часто встречаются. Это вполне реальная задача. Она часто возникает в различных инструментах для маркетинга в SEO-направлении, которые анализируют правильно ли составлены страницы и дают рекомендации по улучшению.\n\n## Монолитное решение\n\n* Цикл/Рекурсия\n* Один проход\n* Все действия сразу\n\nМонолитное решение заключается в том, что в один проход будет выполнен цикл, либо рекурсия. С какой-то стороны это эффективно, потому что проход всего лишь один. Все действия выполняются сразу, они разбросаны по коду. И даже если мы по спецификации представляем это всё, как независимые куски, когда выборка слов — это несколько разных этапов, о которых мы сейчас поговорим, то здесь все эти этапы будут перемешаны. Невозможно будет взглянув на решение, сразу разобраться, что в нём происходит. Его нужно анализировать и понимать всё целиком, что может быть достаточно сложно.\n\n## Разделение\n\n* Фильтрация (оставляем только заголовки)\n* Отображение (извлекаем текст)\n* Свертка (компонуем текст)\n* Свертка (строим результирующую таблицу)\n\nЕсли применять подход стандартных интерфейсов, то можно увидеть, что эта задача, да и большинство задач, которые мы встречаем в разработке, может быть выражена через такие простые действия, как фильтрация-отображение-свёртка и множество других более специфических. Наша задача может быть разделена на четыре этапа:\n\n1. Фильтрация. Оставляем только заголовки. Наш фильтр принимает на вход список и возвращает список.\n2. Отображение. Извлекаем из заголовков текст. Опять же принимает и возвращает список, который уже содержит не теги, а текст.\n3. Свёртка. Мы компонуем текст, и на выходе получается один большой массив текста, в котором возможно сразу будут выделены слова.\n4. Повторная свёртка. Проходим по этим словам, подсчитываем их количество и строим результирующую таблицу.\n\n```javascript\nimport {\n filter, map, reduce,\n} from 'hexlet-pairs-data'\nimport { is, value } from 'hexlet-html-tags'\n\nconst html2 = filter(item => is('h2', item), html)\n\nconst values = map(item => value(item), html2)\n\nconst words = reduce((value, acc) =>\n addWords(acc, value), l(), values)\n```\n\nЭто решение в коде выглядит абсолютно также, как мы его описали в тексте. Сначала мы делаем `filter`. На вход приходит `html`, из него мы достаём только заголовки. Код очень декларативный, понятный, и спецификация восстанавливается по нему очень просто. После этого делаем `map` (отображение), посредством которого мы извлекаем значение из каждого тега, полученного на предыдущем этапе. Далее отдаём эти значения в `reduce`. Начальным аккумулятором он принимает список `l()`. И внутри него мы вызываем функцию `addWords`, которая добавляет все слова в аккумулятор. `values` на этапе, когда срабатывает `reduce`, это массив текста, т.е. просто строчка из заголовков. Естественно, их нужно разбить и добавить в аккумулятор, чтобы в итоге у нас получился список из слов.\n\nВнутреннее устройство функции `addWords` мы не приводим, чтобы не отвлекаться от сути. Она работает по такому же принципу: разбивает текст на слова и добавляет по слову в аккумулятор с помощью внутреннего `reduce`. В конечном итоге мы получаем список всех слов `words`. Слова, естественно, могут и должны дублироваться.\n\nПосле этого делается ещё один более сложный `reduce`, который идёт по всем словам, убирает дубли и инкрементирует счётчик каждого слова, которое повторяется. В конечном итоге мы также получаем список слов, которые содержит количество вхождений каждого из них в заголовки.\n\n## Стандартные интерфейсы\n\n\n\nТеперь мы видим, что стандартные интерфейсы — это отличное решение, которое даёт нам несколько важных преимуществ.\n\n1. Увеличивают модульность.\n2. Упрощают понимание.\n Программа разбивается на множество последовательных независимых кусков, которые могут анализироваться и восприниматься самостоятельно.\n3. Унифицируют вычисления.\n Программисты, владеющие этими выразительными средствами, создают такие программы, которые действительно очень легко читать. Они очень хорошо разбиваются на элементы. И эта унификация позволяет вам легко анализировать достаточно большие куски кода и без проблем дописывать их самостоятельно.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":1351,"name":"theory","url":"/courses/sequences/lessons/intro/theory_unit"}],"links":[{"id":425614,"name":"Документация по функциям для работы с парами js-pairs","url":"https://github.com/hexlet-components/js-pairs/tree/main/docs"}],"ordered_units":[{"id":1351,"name":"theory","url":"/courses/sequences/lessons/intro/theory_unit"}],"id":686,"slug":"intro","state":"approved","name":"Введение","course_order":100,"goal":"Знакомимся с курсом и проектом «Генератор HTML», который будет постепенно разрабатываться в течение всего курса","self_study":null,"theory_video_provider":"vimeo","theory_video_uid":"170597336","theory":"Этот курс — логическое продолжение предыдущего курса про [Составные данные](https://ru.hexlet.io/courses/compound_data). Теперь мы будем говорить о составных данных в чуть более сложном и продвинутом их виде. Как это часто бывает на Хекслете, данный курс будет не про JavaScript и не про изучение его возможностей, а про некоторые фундаментальные вещи, которые обязан знать программист, не зависимо от того, на каком языке он пишет код. JavaScript в данном случае всего лишь способ выражения той идеи, которую мы хотим донести.\n\n## Что такое последовательности\n\n\n\nПоследовательность — это упорядоченная совокупность объектов данных.\n\nДавайте разберём это определение. Оно звучит немного страшновато, но в реальности всё очень просто. Под совокупностью подразумевается некая единая сущность, а под объектами данных подразумевается всё, что угодно. В нашем случае, в программировании, это могут быть: числа, строки, составные объекты (например, пары). В математике последовательности представлены очень широко. Этому посвящены целые её разделы, например, матанализ, который изучает различные числовые последовательности: натуральные числа или числа Фибоначчи, с которыми мы так или иначе имеем дело когда учимся программированию. В реальной жизни с ними никто не сталкивается, но их любят использовать при обучении.\n\n- Натуральные числа\n\n```\n1, 2, 3, 4, 5, 6, 7, 8, ...\n```\n\n- Числа Фибоначчи\n\n```\n0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...\n```\n\n## Списки\n\n- список файлов\n- список дел\n- список сотрудников\n- список сайтов\n- список списков :)\n\nВ программировании гораздо чаще, чем с числовыми последовательностями, мы будем иметь дело со списками. Списки являются практически центральной частью в любой системе. Например, в функциональных языках манипуляции со списками являются ключевым моментом при написании кода.\n\nКакие бывают списки? Если на компьютере вы открываете папку, то видите список файлов. У вас где-то записан список дел, более того, вы можете их вести в онлайновых системах. На предприятии есть список сотрудников. А если вы ищете в Яндекс, Google или другом поисковике, то видите список сайтов с пейджингом (переключатель, который позволяет вам ходить по страницам одного большого списка). И даже такой мета-список, который вы видите сейчас перед собой в данный момент — список списков. Это тоже список. Так что списки можно комбинировать, делать из них новые списки и это очень напоминает то, что мы делали в курсе Составные данные.\n\nВ этом курсе мы будем разрабатывать проект, который называется **Генератор HTML**. Это библиотека, генерирующая части HTML-кода. Если на текущий момент вы не знакомы с HTML, то не стоит переживать, потому что в будущих уроках мы будем обязательно разбирать, что это такое, как он работает, для чего нужен и что такое языки разметки вообще. Это крайне простая тема, на освоение которой не уйдет много времени.\n\n## Принцип работы\n\nПринцип работы нашего проекта показан в примере ниже:\n\n```javascript\nimport {\n make, append, addChild, toString, node,\n} from '@hexlet/html-tags'\n\nconst ul1 = node('ul')\nconst ul2 = addChild(ul1, node('li', 'hello'))\nconst ul3 = addChild(ul2, node('li', 'world'))\n\nconst html1 = make()\nconst html2 = append(html1, ul3)\n```\n\nСейчас не обязательно пытаться полностью понять данный код. Он иллюстрирует, что в конечном итоге получится библиотека, в которой есть обычные функции, позволяющие нам строить древовидную структуру. Которая с одной стороны список,а с другой может быть сложней, чем список. Например, она может быть деревом, о чём будет рассказано позже. В конечном итоге после того, как мы создали эту структуру, мы можем её распечатать и увидеть, что генерируется такое представление:\n\n```javascript\ntoString(html2)\n// <ul>\n// <li>hello</li>\n// <li>world</li>\n// </ul>\n```\n\nЭтот кусочек и есть часть HTML-кода.\n\n## Применение\n\n- программная (динамическая) генерация\n- манипуляции\n- анализ (например, проверка корректности)\n- внутреннее представление в браузере\n\n## Зачем такие библиотеки вообще нужны?\n\n1. Программная (динамическая) генерация HTML очень востребованная операция. Существует большое количество сайтов, которые используют, например, технологии [Single Page Application](https://en.wikipedia.org/wiki/Single-page_application), когда не происходит перезагрузки страницы, а всё меняется прямо на самом сайте. HTML в таком случае действительно генерируется динамически и в итоге представление перерисовывается непосредственно на клиенте.\n\n2. Кроме этого такие библиотеки позволяют манипулировать текущим HTML, который уже загружен и используется. Собственно сайты это постоянно и делают, когда вы закрываете какое-то окно, что-то открываете или меняете на странице.\n\n3. Помимо самого использования есть и анализ, например, валидация или проверка корректности. Верификация — когда проверяется правильно ли создана структура, так как HTML обладает определёнными правилами построения и их можно нарушить. То есть когда в браузере идёт работа с HTML, можно создать HTML, который будет невалиден — не пройдет верификацию. Библиотеки позволяют анализировать и давать рекомендации о том, как лучше делать, а как лучше не делать.\n\n4. И самое интересное, что в реальности именно так внутри браузера и представлен HTML. Если мы загрузим какую-то страницу и посмотрим её исходный код, мы увидим, что там есть HTML как текст. Но текст — это всего лишь текстовое представление HTML, а в реальности он загружается в виде некоторой программной сущности. У неё есть специальное название — [DOM](https://en.wikipedia.org/wiki/Document_Object_Model). Модель построения элементов на веб-странице. Она хранится где-то внутри и ею можно манипулировать\n\n## Document Object Model\n\n```javascript\nconst impl = document.implementation\nlet doc = impl.createDocument('', '', null)\nlet peopleElem = doc.createDocument('people')\n\nlet personElem1 = doc.createDocument('personal')\npersonElem1.setAttribute('first-name', 'eric')\n\nlet addressElem = doc.createDocument('address')\naddressElem.setAttribute('street', '321 south st')\npersonElem1.appendChild(addressElem)\n\npeopleElem.apendChild(personElem1)\ndoc.appendChild(peopleElem)\n```\n\nЗдесь как раз показан пример того, как в браузере происходит манипуляция DOM, т.е. реальным представлением HTML, как он представлен в программном коде. Опять же, не нужно рассчитывать на то, что вы сейчас поймёте этот код, тем более, что он содержит некоторые новые аспекты. Важно, что та библиотека, которую мы разрабатываем, является по сути локальным представлением того, что происходит в браузере. Зная принципы её работы, умея самостоятельно её написать, вы будете легко ориентироваться в том, как это происходит в браузере. По сути мы делаем прототип настоящего DOM. Конечно, сложность очень сильно отличается, потому что DOM в браузере очень навороченная вещь. Наша библиотека гораздо проще, но главное это понять общий принцип.\n\n## Темы\n\n- списки, множества, деревья\n- отображение, фильтрация, агрегация\n- стандартные интерфейсы\n- уровневое проектирования\n\n### Какие темы мы рассмотрим в этом курсе?\n\nИх много, и они достаточно серьёзные.\n\n1. Первое — это структуры данных. Мы рассмотрим списки, познакомимся с понятием множеств и с деревьями.\n\n2. Мы научимся с ними работать посредством тройки функций: отображение (`map`), фильтрация (`filter`) и агрегация (`reduce`). Это тройка методов, которые используются для обработки различных списков, множеств, деревьев и других структур данных, которые существуют в JavaScript. Когда в будущем мы будем работать с внутренними структурами языка, вы увидите, что это основные способы манипуляции структурами в JS и, кстати, во всех функциональных языках и даже не в функциональных. Во всех достаточно продвинутых языках программирования, которые поддерживают функции высшего порядка, реализована эта тройка. А код с использованием данных методов является чаще всего каноническим, т.е. так принято и правильно писать, а не использовать, например, циклы, о чём мы позже обязательно поговорим.\n\n3. Мы познакомимся с таким подходом, как стандартные интерфейсы. Ярким примером данного подхода является конструктор для детей Lego.\n\n4. Также мы познакомимся с уровневым проектированием, которое касается не только программирования, а всей инженерной технической области. Именно оттуда оно берет начало и именно оттуда уровневому проектированию можно учиться.\n\n## Превосходство Хекслета\n\n- функциональный стиль и неизменяемость\n- СИКП\n- фокус на программировании, а не синтаксисе языка\n- двигаемся вперед не с помощью изучения новых фич языка, а путем комбинирования изученных инструментов и развития абстрактного мышления\n- нет нового синтаксиса\n\nНапоследок, несколько аспектов, почему курс сделан именно так. Почему и зачем мы освещаем эту тему на Хекслете.\n\nЭтот курс продолжает традицию предыдущего курса. Всё, что мы здесь делаем, будет неизменяемым. Мы используем функциональный стиль. Потому что введение состояния привносит множество сложностей и проблем, и на данном этапе для понимания темы это совершенно не нужно. Поэтому мы оставляем за скобками изменение. Библиотека может показаться вам немного странной, но чуть позже вы поймёте, почему она реализована именно так.\n\nПомимо этого курс продолжает традицию и также основан на СИКПе ([Структура и интерпретация компьютерных программ](https://web.mit.edu/6.001/6.037/sicp.pdf)). В нём отсутствует новый синтаксис. Мы в очередной раз подчёркиваем, что этот курс не про изучение JavaScript, он про изучение программирования. Пройдя его, вы действительно поймёте и, наконец, осознаете разницу между тем, что такое программировать и что такое знать синтаксис языка.\n\nВсё, что происходит в этом курсе — мы просто берём то, что уже изучили, начинаем это комбинировать и получаем какие-то новые возможности нашей системы, более сложное поведение. Данный подход развивает абстрактное мышление. Многие вещи, которые мы делаем в этом курсе, направлены не только на то, чтобы вы понимали, что такое программирование, но и постепенно начинали развивать свой мозг и тренировать его для того, чтобы он мог переваривать всё более и более сложные сущности и концепции, потому что изучение нового синтаксиса не привносит нового развития в части того, как вы размышляете. Оно не позволит вам автоматически строить действительно сложные программы. А вот абстрактное мышление — это как раз та вещь, которая влияет на это больше всего. Некоторые уроки и практические задания могут показаться весьма сложными, потому что в этом курсе будет много кода и он будет заставлять ваш мозг кипеть. В каком-то смысле это лакмусовая бумажка. Если вы пройдёте курс, действительно в нём разберётесь и поймёте его, то скорее всего дальнейшее обучение пройдет для вас достаточно легко и, в целом, в программировании у вас всё будет хорошо получаться.\n\nЖелаю удачи! Вперёд!\n"},"id":120,"slug":"sequences","challenges_count":10,"name":"JS: Последовательности","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы продолжите изучать составные данные на более продвинутом уровне и рассмотрите тип данных «список». Вы узнаете больше о функциях высшего порядка filter, map и reduce и иерархических структурах. В итоге вы научитесь строить сложные структуры данных на базе более простых и проектировать функции так, чтобы их можно было легко соединять друг с другом. Составные данные пригодятся, если вы решите работать над проектами, которые требуют обработку сложных структур данных. Знания из этого курса помогают программистам обрабатывать коллекции, представленные списками с помощью функций высшего порядка.","kind":"additional","updated_at":"2026-01-20T11:55:07.138Z","language":"javascript","duration_cache":36000,"skills":["Строить сложные структуры данных на базе более простых","Проектировать функции так чтобы их можно было легко соединять друг с другом","Обрабатывать коллекции представленные списками с помощью функций высшего порядка (map/filter/reduce)","Разделять код на уровни, выстраивая правильное взаимодействие между слоями"],"keywords":["функции высшего порядка","стандартные интерфейсы","уровневое проектирование"],"lessons_count":9,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6OTE0OCwicHVyIjoiYmxvYl9pZCJ9fQ==--d07901b5af81babe7daa5cc3f1853beb9c936160/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJqcGciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--39ba06fa99226096df9fc6bb31f84e1d29ea98e9/image.png"},"recommendedLandings":[{"stack":{"id":20,"slug":"js-sicp","title":"СИКП на JS","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":4050,"duration_in_months":1},"id":28,"slug":"js-sicp","title":"СИКП на JS","subtitle":"Навык понимать программы на фундаментальном уровне, уверенно проходить собеседования и решать сложные задачи","subtitle_for_lists":"Навык фундаментального программирования","locale":"ru","current":true,"duration_in_months_text":"1 месяц","stack_slug":"js-sicp","price_text":"от 3 900 ₽","duration_text":"1 месяц","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc2MCwicHVyIjoiYmxvYl9pZCJ9fQ==--9348098e4053d798b6f34bee4ef66947540261e4/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-rafiki.png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/sequences/lessons/conventional_interfaces/theory_unit","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><div style="position:absolute;top:0rem" class=""></div><div style="max-width:var(--container-size-xl);height:100%;min-height:0rem" class=""><style data-mantine-styles="inline">.__m__-_R_5ub_{--grid-gutter:0rem;}</style><div style="height:100%;min-height:0rem" class="m_410352e9 mantine-Grid-root __m__-_R_5ub_"><div class="m_dee7bd2f mantine-Grid-inner" style="height:100%"><style data-mantine-styles="inline">.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:91.66666666666667%;--col-max-width:91.66666666666667%;}@media(min-width: 48em){.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem;display:flex" class="m_96bdd299 mantine-Grid-col __m__-_R_rdub_"><style data-mantine-styles="inline">.__m__-_R_6qrdub_{margin-top:0rem;padding-inline:var(--mantine-spacing-xs);width:100%;}@media(min-width: 48em){.__m__-_R_6qrdub_{margin-top:var(--mantine-spacing-xl);width:80%;}}@media(min-width: 62em){.__m__-_R_6qrdub_{padding-inline:var(--mantine-spacing-xl);}}</style><div style="margin-inline:auto;max-width:var(--mantine-breakpoint-xl)" class="__m__-_R_6qrdub_"><div style="color:var(--mantine-color-dimmed)" class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:calc(0.125rem * var(--mantine-scale));color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-lock "><path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6"></path><path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path><path d="M8 11v-4a4 4 0 1 1 8 0v4"></path></svg></div><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">JS: Последовательности</p></div><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size);margin-bottom:var(--mantine-spacing-xl)" class="m_8a5d1357 mantine-Title-root" data-order="1">Теория: Стандартные интерфейсы</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"Стандартные интерфейсы","inLanguage":"ru","isPartOf":{"@type":"LearningResource","name":"JS: Последовательности"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><p>Хорошая абстракция даёт нам много разных преимуществ. Одно из которых — это увеличение модульности программы. Выражается это в том, что разные куски кода нам гораздо проще комбинировать и, имея небольшой набор базовых функций и компонентов, мы за счёт их сочетания можем получать гораздо более сложное поведение и строить любые конструкции.</p>
<p>В реальной жизни мы с этим тоже встречаемся очень часто. Знакомый всем пример, который отражает эту идею — конструктор Lego. Этот принцип называется — использование <strong>стандартных интерфейсов</strong>. Практически все детали в этом конструкторе сделаны таким образом, что они друг с другом комбинируются разными способами. Всё это работает благодаря тому, что все детали содержат пипочки, которые расположены на одинаковом расстоянии друг от друга. И вы можете их соединять как угодно. Это отличный пример и приём, который показывает то, как, используя небольшой набор базовых компонентов, можно строить действительно сложные вещи.</p>
<p>Есть другие конструкторы, где большое количество деталей уникально, и в конечном итоге вы не сможете их особо комбинировать. Всё, что вы сможете сделать с такими конструкторами, строить заранее продуманные решения, которые предполагались для сборки производителем.</p>
<p>В программировании, по крайней мере в том, что мы уже знаем и продолжаем изучать, такими стандартными интерфейсами являются списки. Наши функции, в особенности функции высшего порядка, занимаются тем, что принимают на вход списки и выдают на выходе чаще всего также списки. Что очевидно позволяет выстраивать их в цепочки и получать достаточно сложное поведение.</p>
<h2 id="heading-2-1">Определение частоты слов в заголовках</h2>
<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">| Обучение | 3 |
| Практика | 2 |
| Онлайн | 1 |
| Хекслет | 1 |</code>
<p>Давайте рассмотрим достаточно простую задачу: вычислить частоту слов в заголовках. В результате должна получиться таблица, как в примере выше. Предположим, у нас есть HTML, в котором есть разные заголовки. И мы хотим посмотреть, какие слова в них наиболее часто встречаются. Это вполне реальная задача. Она часто возникает в различных инструментах для маркетинга в SEO-направлении, которые анализируют правильно ли составлены страницы и дают рекомендации по улучшению.</p>
<h2 id="heading-2-2">Монолитное решение</h2>
<ul>
<li>Цикл/Рекурсия</li>
<li>Один проход</li>
<li>Все действия сразу</li>
</ul>
<p>Монолитное решение заключается в том, что в один проход будет выполнен цикл, либо рекурсия. С какой-то стороны это эффективно, потому что проход всего лишь один. Все действия выполняются сразу, они разбросаны по коду. И даже если мы по спецификации представляем это всё, как независимые куски, когда выборка слов — это несколько разных этапов, о которых мы сейчас поговорим, то здесь все эти этапы будут перемешаны. Невозможно будет взглянув на решение, сразу разобраться, что в нём происходит. Его нужно анализировать и понимать всё целиком, что может быть достаточно сложно.</p>
<h2 id="heading-2-3">Разделение</h2>
<ul>
<li>Фильтрация (оставляем только заголовки)</li>
<li>Отображение (извлекаем текст)</li>
<li>Свертка (компонуем текст)</li>
<li>Свертка (строим результирующую таблицу)</li>
</ul>
<p>Если применять подход стандартных интерфейсов, то можно увидеть, что эта задача, да и большинство задач, которые мы встречаем в разработке, может быть выражена через такие простые действия, как фильтрация-отображение-свёртка и множество других более специфических. Наша задача может быть разделена на четыре этапа:</p>
<ol>
<li>Фильтрация. Оставляем только заголовки. Наш фильтр принимает на вход список и возвращает список.</li>
<li>Отображение. Извлекаем из заголовков текст. Опять же принимает и возвращает список, который уже содержит не теги, а текст.</li>
<li>Свёртка. Мы компонуем текст, и на выходе получается один большой массив текста, в котором возможно сразу будут выделены слова.</li>
<li>Повторная свёртка. Проходим по этим словам, подсчитываем их количество и строим результирующую таблицу.</li>
</ol>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">import {
filter, map, reduce,
} from 'hexlet-pairs-data'
import { is, value } from 'hexlet-html-tags'
const html2 = filter(item => is('h2', item), html)
const values = map(item => value(item), html2)
const words = reduce((value, acc) =>
addWords(acc, value), l(), values)</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Это решение в коде выглядит абсолютно также, как мы его описали в тексте. Сначала мы делаем <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">filter</code>. На вход приходит <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">html</code>, из него мы достаём только заголовки. Код очень декларативный, понятный, и спецификация восстанавливается по нему очень просто. После этого делаем <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">map</code> (отображение), посредством которого мы извлекаем значение из каждого тега, полученного на предыдущем этапе. Далее отдаём эти значения в <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">reduce</code>. Начальным аккумулятором он принимает список <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">l()</code>. И внутри него мы вызываем функцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">addWords</code>, которая добавляет все слова в аккумулятор. <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">values</code> на этапе, когда срабатывает <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">reduce</code>, это массив текста, т.е. просто строчка из заголовков. Естественно, их нужно разбить и добавить в аккумулятор, чтобы в итоге у нас получился список из слов.</p>
<p>Внутреннее устройство функции <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">addWords</code> мы не приводим, чтобы не отвлекаться от сути. Она работает по такому же принципу: разбивает текст на слова и добавляет по слову в аккумулятор с помощью внутреннего <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">reduce</code>. В конечном итоге мы получаем список всех слов <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">words</code>. Слова, естественно, могут и должны дублироваться.</p>
<p>После этого делается ещё один более сложный <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">reduce</code>, который идёт по всем словам, убирает дубли и инкрементирует счётчик каждого слова, которое повторяется. В конечном итоге мы также получаем список слов, которые содержит количество вхождений каждого из них в заголовки.</p>
<h2 id="heading-2-4">Стандартные интерфейсы</h2>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6OTIwMCwicHVyIjoiYmxvYl9pZCJ9fQ==--8c61a763a51cb920c146088ce2556a28ef276dde/signals.png" alt="Стандартные интерфейсы" loading="lazy"/></p>
<p>Теперь мы видим, что стандартные интерфейсы — это отличное решение, которое даёт нам несколько важных преимуществ.</p>
<ol>
<li>Увеличивают модульность.</li>
<li>Упрощают понимание.
Программа разбивается на множество последовательных независимых кусков, которые могут анализироваться и восприниматься самостоятельно.</li>
<li>Унифицируют вычисления.
Программисты, владеющие этими выразительными средствами, создают такие программы, которые действительно очень легко читать. Они очень хорошо разбиваются на элементы. И эта унификация позволяет вам легко анализировать достаточно большие куски кода и без проблем дописывать их самостоятельно.</li>
</ol></div><div style="margin-block:var(--mantine-spacing-xl)" class=""><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md)" class="m_8a5d1357 mantine-Title-root" data-order="2">Рекомендуемые программы</h2><style data-mantine-styles="inline">.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_2mremqrdub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/js-sicp?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">1 месяц</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">СИКП на JS</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Навык фундаментального программирования</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc2MCwicHVyIjoiYmxvYl9pZCJ9fQ==--9348098e4053d798b6f34bee4ef66947540261e4/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Low%20code%20development-rafiki.png" alt="СИКП на JS" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div></div></div></div></div><style data-mantine-styles="inline">.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:8.333333333333334%;--col-max-width:8.333333333333334%;}@media(min-width: 48em){.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem" class="m_96bdd299 mantine-Grid-col __m__-_R_1bdub_"><div style="margin-inline:var(--mantine-spacing-xs)" class="mantine-visible-from-sm"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-lg);text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/sequences/lessons/conventional_interfaces/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label"><span style="margin-inline-end:var(--mantine-spacing-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дальше</span>→</span></span></a><a style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Навигация по теме</span><span class="m_57492dcc mantine-NavLink-description">Теория</span></div><span class="m_690090b5 mantine-NavLink-section" data-position="right"></span></a><div style="margin-block:var(--mantine-spacing-lg)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div><div style="margin-block:var(--mantine-spacing-lg)" class=""><div style="justify-content:space-between;margin-bottom:calc(0.1875rem * var(--mantine-scale));color:var(--mantine-color-dimmed);font-size:var(--mantine-font-size-xs)" class="m_8bffd616 mantine-Flex-root __m__-_R_qimrbdub_"><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Завершено</p><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0 / 9</p></div><div style="--progress-size:var(--progress-size-sm)" class="m_db6d6462 mantine-Progress-root" data-size="sm"><div style="--progress-section-size:0%;--progress-section-color:var(--mantine-color-gray-filled)" class="m_2242eb65 mantine-Progress-section" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" aria-valuetext="0%"></div></div></div><button style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root" type="button"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Обсуждения (архив)</span><span class="m_57492dcc mantine-NavLink-description"></span></div></button><div style="--toc-bg:var(--mantine-color-blue-light);--toc-color:var(--mantine-color-blue-light-color);--toc-size:var(--mantine-font-size-sm);--toc-radius:var(--mantine-radius-sm);margin-top:var(--mantine-spacing-xl)" class="m_bcaa9990 mantine-TableOfContents-root" data-variant="light" data-size="sm"></div></div><div class="mantine-hidden-from-sm"><div style="--stack-gap:0rem;--stack-align:stretch;--stack-justify:flex-start" class="m_6d731127 mantine-Stack-root"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-xs);padding:0rem;text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/sequences/lessons/conventional_interfaces/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">→</span></span></a><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" data-disabled="true" type="button" disabled=""><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></span></button><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto mantine-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" type="button"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></span></button></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-Bukl1lYy.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>