Урок русского языка по теме: «Составление слова по схемам.Разбор слова по составу»
Тема урока:
«Составление слов по схемам. Порядок разбора слова по составу»
Цель: создать условия для исследования и обобщения знаний о частях слова, развивать умения разбирать слова по составу и составлять слова по схемам.
Задачи:
— создать условия для исследования и обобщения знаний о частях слова;
— развивать умения распознавать и определять части слова;
— составить алгоритм разбора слова по составу;
— развивать умения разбирать слова, по составу применяя алгоритм;
— развивать умения составлять слова по схемам;
— развивать познавательную самостоятельность через наблюдения, сравнения и выводы;
— активизировать словарный запас учащихся;
— воспитывать коммуникативную культуру учащихся.
Личностные результаты:
— осознавать роль языка и речи в жизни людей;
— воспитывать потребность совершенствовать свою устную и письменную речь;
Метапредметные результаты:
Регулятивные УУД:
— определять и формулировать цель деятельности на уроке с помощью учителя;
— учиться высказывать свое предположение на основе работы с материалом учебника;
Познавательные УУД:
— ориентироваться в учебнике;
— делать выводы в результате совместной работы класса и учителя;
Коммуникативные УУД:
— оформлять свои мысли в устной и письменной форме;
— выразительно читать и пересказывать текст;
Предметные результаты:
— обобщить знания о частях слова;
— развивать умения разбирать слова по составу;
— развивать умения составлять слова по схемам;
— развивать связную устную и письменную речь;
— продолжить работу по наблюдению за лексическим значением слов;
— развивать умения работать со словарем и находить нужную информацию.
ХОД УРОКА
1.Организационный момент
— Ребята, у нас на уроке присутствуют гости. Повернитесь. Улыбнитесь им. Они вам тоже улыбаются. Тепло улыбок наших гостей поможет нам в работе.
-Присаживайтесь.
— Открываем тетради. Проверьте число. Сегодня 18 декабря. Классная работа.
— А теперь чистописание.
— От чего зависит выполнение работы в тетради? (Посадка, положение тетради.)
— Запишите следующие соединения слов:
ни жела по е
— Подумайте, что можно составить из этих соединений? (Слово.)
Запишите это слово. (Пожелание).
— У меня пожелание, чтобы у вас на уроке все получилось.
— На уроках русского языка мы говорим, что слова состоят из частей.
— Из каких же частей состоят слова? (Корень, суффикс, окончание, приставка. )
— Что такое окончание?
— Что такое основа слова?
— Из чего состоит основа?
— Каждая часть слова имеет свое обозначение. Мы сейчас вспомним, как обозначаются части слова. Итак, самая главная часть слова – это Корень. Положите карточку. Часть слова, которая стоит перед корнем. Это? (Приставка).
— Часть слова, которая стоит после корня. Это? (Суффикс.)
— Изменяемая часть слова. Это? (Окончание.)
— Что еще мы не обозначили? (Основу.) Что такое основа?
— Что у нас получилось? (Схема слова.)
— Посмотрите на схему, назовите главную часть слова. (Корень).
— Я предлагаю вам корень МОРОЗ. Назовите группу однокоренных слов.
— Какие слова называются однокоренными?
— Какое из них подходит к этой схеме?
— Запишите это слово. Обозначьте части слова.
Проверка. Светофорчик. ЗАМОРОЗКИ.
— Что мы изобразили при помощи схем? (Состав слова).
— Дети, Вам знакомо слово СОСТАВ? (Встречали). Где мы встречали его в жизни? (В математике – состав числа, состав – железнодорожный поезд, в русском языке – состав слова).
— Как вы думаете, какой СОСТАВ интересует нас сегодня на уроке? (Состав слова).
— Верно, нас интересует состав слова.
— Где можно узнать значение слова СОСТАВ? (В Словаре). Сегодня экспертами будут работать Шилин Д., Бурносова А.
— Вернемся к нашим схемам. Какое задание мы выполняли? (Составляли слова по схеме. Говорили про состав слова).
— Давайте вместе сформулируем тему урока. (Составление слов по схемам. Порядок разбора слов по составу.)
— Какую цель поставим перед собой? (Научиться составлять слова по схемам, разбирать слова по составу. )
— Составляем план.
1. Повторить о частях слова.
2. Составлять слова по схемам.
3. Разбирать слова по составу.
— Тему урока сформулировали, план работы составили, цели поставили.
— Посмотрите на экран. Что это такое? (Части слова).
— Какое задание можно придумать? (Составить слова).
Работа в парах.
— Какие слова получились? (зима, зимушка, зимой, корм, кормушка, кормит, покормит, птиц, птица, птицы).
— Запишите в тетради и на доске. Какое задание можно еще выполнить? (Составить предложение).
— Какое предложение получится? (Покорми птиц зимой!)
— Молодцы!
Физкультпауза.
Птички к нам прилетели.
На кормушку дружно сели.
Хлебных крошек поклевали.
Ягод спелых отыскали.
Птицы могут зимовать. Мы им будем помогать.
— Открываем учебник на с. 158. Упр. 200. Прочитайте. Работа в парах.
1 ряд – 1 столбик
2 ряд – 2 столби
3 ряд – 3 столбик
— Запишите слова в тетради, обозначьте части слова.
— Почему возможны разные слова? (Могут быть разные суффикс и окончание у однокоренных слов).
— Обменяйтесь тетрадями. (Взаимопроверка.) Сверьте с образцом.
— Кто испытывал затруднения при работе? У кого все получилось?
— Чему учились? (Учились составлять слова по схемам).
— Дети на предыдущих уроках мы разбирали слова по составу. Давайте вспомним, с чего начинали?
— Будем составлять план-памятку разбора слова по составу. Итак, с чего начинаем?
1. Прочитать слово.
2. Окончание.
3. Основа слова.
4. Корень.
5. Суффикс.
6. Приставка.
— Получилась памятка разбора слова по составу.
— Давайте сверим с образцом.
— Прочитайте.
— А теперь я предлагаю вам пользуясь памяткой разобрать слово «подснежники».
— С чего начнем?
— Запишите слово. Читайте памятку и объясняйте.
Самостоятельно по вариантам разобрать слова по составу. Упр. 201.
Проверка. Светофорчик.
Тест
1. Часть, без которой слово не может существовать?
д) суффикс;
п) корень;
2. Родственные слова это:
у) слова, которые сходны по смыслу;
о) слова, которые имеют общую часть и сходны по смыслу.
3. Окончание служит для образования новых слов
р) нет;
с) да.
4. Суффикс служит для связи слов в предложении
в) да;
я) нет.
5. Корень – это:
д) общая часть родственных слов;
и) общая часть слов.
6. Разбор слова по составу надо начинать с:
с) корня;
о) окончания.
7. Приставка это значимая часть слова.
к) да
а) нет
— О каком порядке говорили сегодня?
— Понравился ли вам урок?
— Достигли мы поставленной цели?
— Какова была тема урока?
— Над чем мы сегодня работали?
— В каком порядке нужно разбирать слова по составу?
— В чем были трудности?
— Для чего нужно знать части слова, уметь находить их в слове? Где могут пригодиться полученные знания?
— Чем полезен оказался урок?
Рефлексия.
Я не знал(а)… Теперь я знаю…
Мне было трудно…
Теперь я могу…
Мне было легко, потому что…
Мне захотелось…
Научился…
Я понял, что…
Мне захотелось… Все понял, могу помочь другим.
Я могу похвалить себя и своих одноклассников
8
Упражнение 233 — ГДЗ Русский язык 4 класс Канакина учебник часть 2
- Главная
- ГДЗ
- 4 класс org/ListItem»> Русский язык
- Канакина учебник
Вернуться к содержанию учебника
Вопрос
Вариант вопроса #1:
Прочитайте строки из стихотворения «Моя весна».
Всё загудело2, всё блеснуло,
Так стало шумно и светло!
В лазури облако блеснуло3,
Как лебединое крыло.
В. Набоков
- Спишите. Найдите в предложениях глаголы. Укажите над ними род, выделите окончания.
- Подготовьтесь объяснить правильность выполнения заданий.
Вариант вопроса #2:
Прочитайте.
Всё загудело2, всё блеснуло,
Так стало шумно и светло!
В лазури облако блеснуло3,
Как лебединое крыло.
В. Набоков
- Скажите, какой частью речи являются слова, в которых выделена буква о. Объясните. Укажите над ними часть речи.
- Какой значимой частью слова является выделенная в словах часть: окончанием или суффиксом?
- Спишите. Выделите родовые окончания глаголов.
Ответ
Вариант ответа #1:
ср. р. ср. р.
Всё загудело, всё блеснуло,
ср. р.
Так стало шумно и светло!
ср. р.
В лазури облако блеснуло,
Как лебединое крыло.
Загудело
Блеснуло — гл., что сделало?, н. ф. — блеснуть
Пояснения:
суффикс глаголов прошедшего времени -л- является формообразующим, поэтому он не входит в основу слова.
Вариант ответа #2:
Всё загудело2, всё блеснуло,
Так стало шумно и светло!
В лазури облако блеснуло3,
Как лебединое крыло.
В. Набоков
Всё (что сделало?) загудело, блеснуло — глаголы.
Стало (как?) шумно, светло — наречия.
(Что?) облако — существительное.
Блеснуло (как что?) крыло — существительное.
В словах загудело (загудели), блеснуло (блеснула), облако (облака), крыл о (крыла) буква о — это окончание;
в словах шумно, светло буква о — это суффикс.
Загудело
- загудело, загудели → окончание -о;
- гудеть, гудок → корень -гуд-;
- приставка за-;
- суффикс глагола -е-;
- суффикс прошедшего времени -л-;
- основа загуде (суффикс -л- не входит).
Блеснуло — гл., что сделало?, н. ф. — блеснуть, I спр., в прош. вр., в ср. р., ед. ч., сказуемое.
Вернуться к содержанию учебника
Заявка на патент США для системы и способа создания и воспроизведения синхронизированных художественных мультимедийных представлений печатных, устных или загруженных повествований, театральных сценариев, диалогов, текстов песен или других лингвистических текстов. Заявка на патент (заявка № 20110040555, выданная 17 февраля 2011 г.
![](/800/600/http/data15.gallery.ru/albums/gallery/223008-f3857-62000383-m750x740-u0a204.jpg)
Уровень техники
Возможное использование изобретения.
Художественное мультимедийное представление композиций и/или повествований может использоваться для образования, общения, безопасности, наблюдения, развлечения и художественных целей.
Пользователи могут использовать художественные мультимедийные представления текста и композиций и/или повествований, чтобы понять, как слова и понятия в текстовых композициях работают вместе или в связанных контекстах.
Художественные мультимедийные представления композиций и/или повествований могут использоваться в качестве визуального и звукового вознаграждения для пользователей и изучающих язык с помощью графики, движений и звуков за их письмо, речь и/или пение.
Художественное мультимедийное представление композиций и/или повествований может повлиять на писателей, ораторов или певцов, чтобы они добавляли больше деталей или были более описательными в своих композициях, поскольку выбор слов был бы обязательным для итогового мультимедийного представления.
Художественное мультимедийное представление композиции и/или повествования может стать новым типом медиа для представления письма, речи и пения в искусстве, развлечениях и других областях.
Художественное мультимедийное представление сценариев или диалоговых форм композиций может использоваться для понимания времени и отношений субъектов в указанных сценариях или диалогах.
Художественные мультимедийные представления композиций и/или повествований могут использоваться в качестве образовательного инструмента для изучения языка как детьми, так и изучающими второй язык.
Один из возможных вариантов системы и метода может использоваться службой безопасности для визуализации связи. Художественное мультимедийное представление текстовых композиций можно использовать для создания более понятной визуализации текстов, введенных из устных или письменных сообщений наблюдения.
РЕЗЮМЕ
Система и метод, используемые для преобразования напечатанного, устного или загруженного повествования, сценария, диалога или других типов текстов, далее именуемых композицией, в синхронизированный и воспроизводимый художественный мультимедиа представление указанной композиции, и результирующее художественное мультимедийное представление затем выводится на графический и/или звуковой пользовательский интерфейс и/или другие пользовательские интерфейсы.
Композиция проходит через систему на компьютере или компьютеризированном устройстве, которая использует метод и алгоритм для разбора и распределения слов и/или пар слов и/или фраз в части речи.
При входе в систему через окно графического интерфейса ввода, диалоговое окно выбора загрузки файла, систему автоматического распознавания речи (ASR) или любые другие удобные средства получения текстового ввода от пользователя каждая композиция разбивается на абзацы , предложения и отдельные слова. Для каждого предложения и/или абзаца в композиции отдельные слова, пары слов и/или фразы классифицируются в соответствии с идентифицируемыми лексемами. Затем алгоритм использует ссылки на лексемы для назначения слов, пар слов и/или фраз воспроизводимым структурам.
Игровые структуры включают как минимум подлежащее (существительное) и действие (глагол), но могут также включать прилагательные, наречия и другие связующие слова, которые изменяют или добавляют компоненты воспроизводимой структуры. Слова и пары слов в отдельных предложениях и/или абзацах используются для определения сцены.
Сцены состоят из одной или нескольких воспроизводимых структур, полученных из ссылок на отдельные слова и/или пары слов и/или фразы в предложениях и/или абзацах. Сцены могут включать в себя движение, анимацию, графику, звуки, раскрашивание, скорость, направление и/или локальные модификации указанных движений, анимации и/или графики и других медиа и представляются пользователю через графический пользовательский интерфейс и аудиоинтерфейс. . Несколько воспроизводимых структур в сценах соединяются друг с другом с помощью соединителя. Несколько сцен или одна сцена в определенной временной последовательности создают мультимедийную версию композиции.
Временная последовательность сцен или отдельная сцена, которая представляет композицию, затем воспроизводится от начала до конца для пользователя через пользовательский интерфейс и/или другие устройства и пошагово проходит через мультимедийное представление композиции или повествования в временным способом.
Случайные числа, сгенерированные генератором случайных чисел, могут использоваться для изменения временных параметров и воспроизводимой структуры, а также соединительных компонентов для придания естественного вида и ощущения при воспроизведении мультимедийного представления композиции.
ОПИСАНИЕ РИСУНКОВ И ПРИЛОЖЕНИЕРИС. 1: блок-схема шагов ввода и токенизации в системе и методе. На шаге 1, обозначенном (1), пользователь печатает, говорит, поет или загружает текст. На шаге 2 (2) текст последовательно разбивается на абзацы, предложения и слова.
РИС. 2: блок-схема шагов создания воспроизводимой структуры (структур) в системе и методе. На этапе 3, обозначенном (3), каждое слово, и/или пара слов, и/или фраза ссылаются на базу данных слов для структуры данных слова/пары слов/фраз.
РИС. 3: Блок-схема построения сцен(ы) шагов в системе и методе. На шаге 4, обозначенном (4), алгоритм анализирует типы слов и присваивает словам, парам слов и/или фразам воспроизводимые структуры.
РИС. 4: блок-схема шагов вывода в системе и способе. На шаге 5, обозначенном (5), алгоритм назначает все воспроизводимые структуры сценам (сценам) и назначает любые отношения между воспроизводимыми структурами. Существительные и действия изменяются прилагательными. На шаге 6, обозначенном (6), компоненты сцены ссылаются на базу данных слов, и информация о каждом слове, паре слов и/или фразе копируется в воспроизводимую структуру. На этапе 7, обозначенном (7), сцена выводится на пользовательский интерфейс и/или динамики или другое устройство в течение определенного времени с использованием информации о воспроизводимой структуре и взаимосвязях. Следующая сцена, если она есть, готова к воспроизведению после окончания предыдущей сцены.
Приложение 1: Псевдокод алгоритма системы и метода, с помощью которых может быть создано художественное мультимедийное представление текста или композиции. Приложение включает в себя: функции «обработка», «разбор» и «раздел».
Приложение 1. Псевдокод
Следующие алгоритмы представляют осуществимый и проверенный подход к преобразованию текста на естественном языке в воспроизводимое (воспроизводимое) художественное мультимедийное представление. современные методы компьютерного программирования и включены сюда только в справочных целях.
1. Алгоритм Псевдокод Листинг Функция «Процесс».
A. Объявленная здесь функция «Процесс», начинающаяся в строке (1), представляет собой основной вход в алгоритм анализа текста и грамматики для отображаемого логического алгоритма.
B. Результирующая структура данных функции «Процесс», «ListOfListOfRenderableItems», представляет собой многомерный последовательный набор визуализируемых структур, каждая из которых содержит агломерацию медиа-элементов, предназначенных для совместной визуализации в течение аналогичного промежутка времени, то есть, как визуализируемая сцена или набор сцен, предназначенный для мультимедийного представления композиции на естественном языке, введенной в функцию (строковая переменная, «текст»).
C. Функция «Разбор», указанная в строке (3), подробно описана в листинге № 2 псевдокода алгоритма для функции с именем «Разбор». См. ниже.
D. Структура данных «ListOfAssociatedElements» в строке (4) представляет собой набор ассоциаций между словами и/или грамматическими элементами и элементами мультимедиа, которые итеративно накапливаются посредством различных поисков в базе данных, грамматических правил, значений по умолчанию, логики сценариев, процедурных конкатенаций. , и другие процессы.
E. Функция «Соединить» в строке (5) работает с несвязанными словами и/или медиа-элементами и, используя логику соединительного слова, создает анимацию или другие ассоциации между медиа-элементами, например, утверждение «X идет к Y» создает связь между X и Y.
F. Функция «Разрешить» в строке (6) выполняет различные прямые, обратные и нелинейные поиски по списку ассоциаций для устранения грамматических пробелов, например, в следующем примере: «Слон большой. Я боюсь этого. Он бежит за мной». Субъект «Слон» указан в первом предложении, однако в третьем предложении есть ссылка на него, для которого мы должны сделать обратную ссылку, чтобы выяснить, что «бежит за мной». Функция «Разрешить» решает такие проблемы при обратном поиске, и в этом случае неявная структура принимает вид: «Слон большой. Я боюсь [слона]. [Слон] бежит за мной».
G. Заключительной операцией «Процесса» является завершение всех визуализируемых структур и подготовка их к выводу. Это включает удаление закладок и дополнительной памяти, которая не используется в процессе рендеринга. Кроме того, он включает в себя синхронизацию внутреннего состояния визуализируемой структуры с выходной схемой, компиляцию динамических сценариев, загрузку внешних или постоянных файлов, хранящихся в базе данных, и т. д. Возвращаемая коллекция все еще довольно абстрактна в том смысле, что она похожа на музыкальный инструмент. оценка, что дает большую свободу действий для интерпретации подсистемой рендеринга.
2. Алгоритм листинга псевдокода Функция «Разбор».
A. Функция, объявленная в (1), предназначена для обеспечения общего обзора фазы синтаксического анализа после получения текстового блока (корпуса) от пользователя.
B. Функция «ExternalParseCorpus» в (4), определенная как вызов модульной или сменной подсистемы обработки естественного языка и метода, которые могут быть изменены по мере изменения или улучшения технологии.
C. Цикл, начинающийся в (7), перебирает структуры уровня абзаца, возвращенные функцией «ExternalParseCorpus» в (4).
D. Структура «GrammarDataElement» в (15) содержит только элементы, найденные в базе данных визуализируемых элементов. Другие грамматические элементы опущены. Например, известные слова, такие как «the», обычно не сопоставляются с какими-либо известными визуализируемыми объектами. Неизвестные или непредвиденные слова, не поддерживаемые в базе данных, удаляются и могут быть добавлены во внешнюю таблицу базы данных или файл неизвестных слов, которые предлагается добавить позднее.
E. Цикл, начинающийся с (19), перебирает слова, присутствующие в текущем предложении.
F. Функция «NormalizeAndSpell» в строке (21) нормализует слова в соответствии с правилами регистра и пытается исправить возможные ошибки из-за опечаток. Примечание. Эта проверка орфографии могла уже выполняться в функции «ExternalParseCorpus» в (4).
G. Строки (24-29) пытаются сопоставить нормализованные слова, помеченные частями речи, с визуализируемыми категориями или онтологиями элементов слов, присутствующих в базе данных.
H. Наконец, если слово не соответствует какой-либо известной категории, ему присваивается «отброшенная» категория, как показано в строке (32).
3. Алгоритм Псевдокод Листинг Функция «Раздел».
A. Функция, объявленная в (1), предназначена для того, чтобы дать некоторое представление о внутренней работе, только для справочных целей, операции в функции «Подключение» в листинге псевдокода № 1-5, «Процесс».
B. Алгоритм «Разделение» работает путем определения, посредством грамматической структуры и базовой логики, среди прочих правил, к какому списку ассоциаций принадлежит каждое слово и последующий набор медиа-элементов.
C. «Разделение» продолжается путем повторения грамматических данных и сначала гарантируется, что каждый набор связанных элементов имеет как минимум: а., подлежащее (например, существительное) и б., глагол. Лакуны или отсутствующие элементы заполняются позже родительским алгоритмом «Процесс».
Parser Combinators: прохождение
04 декабря, 2020 | 14 минут чтения
Или: Напишите вам парсек во благо
Большинство людей путешествуют с Haskell по тем же первым шагам. Во-первых, вам нужно познакомиться с самим языком, особенно если вы приходите к нему без каких-либо знаний функционального программирования: его необычный синтаксис, ленивость, отсутствие изменяемого состояния… Оттуда вы можете перейти к функциям и объявлениям типов, ознакомиться освойте функции Prelude (и их известные недостатки) и начните писать свои первые программы. Следующий большой шаг — это, конечно же, монады, печально известное слово на букву «м» 9.0137 [1] и как мы используем их для структурирования наших программ.
Но потом, когда ты начал развивать интуицию монад… Путь становится неясным. Можно было бы охватить гораздо больше, но ничто из этого не является столь фундаментальным. Есть примерно три категории тем, которые можно изучить:
- Идем дальше с самим языком: изучаем языковые расширения, теорию, лежащую в их основе, и расширенные функции, которые они предоставляют.
- Узнайте больше о различных способах структурирования программы (таких как преобразователи монад или системы эффектов).
- Изучение некоторых наиболее часто используемых библиотек и понимание того, что делает их такими вездесущими: QuickCheck, Lens… и Parsec.
Сегодня я хочу изучить Parsec, а особенно как работает Parsec . Синтаксический анализ используется повсеместно, и большинство программ на Haskell будут использовать Parsec или один из его вариантов (мегапарсек или аттопарсек). Хотя можно использовать эти библиотеки, не заботясь о том, как они работают, мне кажется интересным разработать ментальную модель их внутреннего устройства; а именно комбинаторы монадических синтаксических анализаторов
Чтобы лучше понять этот метод, в ходе этой статьи мы сначала повторно реализуем упрощенную версию Parsec , а затем используем ее для написания парсера для значений JSON [2] . Наша цель здесь не будет заключаться в разработке полноценной библиотеки, как раз наоборот: мы реализуем строгий минимум, который нам нужен, чтобы сосредоточиться на основных идеях.
Выбор представления типа для синтаксических анализаторов
С точки зрения высокого уровня синтаксический анализатор можно рассматривать как функцию перевода: он принимает в качестве входных данных некоторые слабоструктурированные данные (чаще всего это текст) и пытается преобразовать их в структурированные данные, следуя правилам формальная грамматика. Компилятор преобразует последовательность символов в абстрактное синтаксическое дерево, анализатор JSON преобразует последовательность символов в эквивалентное представление значения JSON в Haskell. Есть несколько алгоритмических подходов к этой проблеме; комбинаторы парсера являются примером рекурсивный спуск : мы анализируем каждый термин нашей грамматики, рекурсивно вызывая синтаксические анализаторы для каждого подтермина.
Это означает, что под «парсером» мы оба подразумеваем общее высокоуровневое преобразование и каждый из его отдельных шагов, поскольку каждый парсер рекурсивно выражается как комбинация других парсеров.
Минимальная жизнеспособная реализация
Давайте шаг за шагом разберемся с типом парсера. Во-первых, мы установили, что синтаксический анализатор — это функция от некоторого заданного ввода до того, что мы пытаемся проанализировать. В этом проекте мы будем использовать
тип Parser a = String -> a
Но этого типа недостаточно для нашего варианта использования. Прежде всего: синтаксический анализатор может дать сбой, если входные данные не соответствуют тому, что диктует наша грамматика. Нам нужно будет определить соответствующий тип для представления ошибок, и синтаксический анализатор должен включить эту возможность на уровне типа.
введите Parser a = String -> либо ParseError a
Кроме того, синтаксический анализ представляет собой последовательную операцию: для анализа объекта JSON необходимо сначала проанализировать открывающую фигурную скобку, затем проанализировать записи, а затем
проанализировать закрывающую фигурную скобку; рекурсивно, чтобы проанализировать запись, нужно сначала проанализировать ключ, затем двоеточие, а затем значение.![](/800/600/http/sprtqa.b-cdn.net/download/12850878_6d75f018c56ec98f76b8c7e43b6bb0bb_800.jpg)
Поскольку мы используем String в качестве входного потока, каждому синтаксическому анализатору достаточно просто вернуть то, что осталось от входной строки [4] . Тип Parser , который мы будем использовать, будет следующим:
Парсер нового типа a = Парсер { runParser :: String -> (String, либо ParseError a) }
Написание элементарных парсеров
Как это принято в Haskell, мы начинаем с охвата «аксиоматических» базовых случаев, а затем обобщаем/экстраполируем оттуда. Учитывая, что наш поток String , а наши отдельные токены — это просто символы указанной строки, наши два базовых случая просто соответствуют двум конструкторам списка: во входном потоке остались токены или мы достигли конца. Мы называем эти две функции any и eof соответственно:
любой :: Parser Char any = Parser $ \input -> ввод регистра -- осталось немного ввода: мы распаковываем и возвращаем первый символ (x:xs) -> (xs, справа x) -- ничего не осталось: анализатор не работает [] -> ("", Left $ ParseError "любой символ" -- ожидается "конец ввода" -- встречается ) eof::Парсер() eof = Parser $ \input -> регистр ввода -- никаких входных данных не осталось: синтаксический анализатор прошел успешно [] -> ("", Право ()) -- оставшиеся данные: синтаксический анализатор не работает (c:_) -> (ввод, Left $ ParseError "конец ввода" -- ожидается [с] -- встречается )
Это только два основных парсера которые нам нужны! Они охватывают два основных случая: у нас есть символ для разбора или нет. Все остальное можно выразить с помощью этих двух и с помощью комбинаторов.
Синтаксические анализаторы последовательности
Как упоминалось ранее, синтаксический анализ последовательный . Нам часто нужно будет выразить что-то вроде: я хочу применить какой-то парсер A , затем какой-то парсер B и использовать их результаты. Давайте посмотрим, как мы реализуем синтаксический анализатор для записи объекта JSON, например: нам нужно проанализировать строку json, затем двоеточие, затем значение json (мы будем предполагать, что эти отдельные синтаксические анализаторы уже существуют). Полученный код… неоптимален: повторяющийся, подверженный ошибкам и трудно читаемый:
jsonEntry :: Parser (String, JValue) jsonEntry = Парсер $\ввод -> -- разобрать строку json case runParser jsonString ввод (вход2, левая ошибка) -> (вход2, левая ошибка) (вход2, правая клавиша) -> -- в случае успеха: разобрать одно двоеточие case runParser (char ':') input2 of (input3, левая ошибка) -> (input3, левая ошибка) (вход3, справа _) -> -- в случае успеха: анализировать значение json case runParser jsonValue input3 of (input4, левая ошибка) -> (input4, левая ошибка) (вход4, правильное значение) -> -- в случае успеха: вернуть результат (вход4, справа (ключ, значение))
Это становится довольно громоздким как из-за того, что каждый шаг может завершиться ошибкой, так и из-за того, что каждый шаг возвращает входные данные для следующего. Кроме того, это заставляет нас знать о внутренней структуре парсера, чтобы знать, как их связать. Чтобы упростить задачу, мы можем выделить этот шаблон в отдельную функцию:
andThen :: Парсер a -> (a -> Парсер b) -> Парсер b parserA `andThen` f = Parser $ \input -> case runParser parserA ввод (restOfInput, Right a) -> runParser (f a) restOfInput (restOfInput, Left e) -> (restOfInput, Left e)
Эта функция позволяет нам переписать наш синтаксический анализатор записей JSON таким образом, что больше не требуется явного самоанализа каждого синтаксического анализатора на этом пути.
jsonEntry :: Parser (String, JValue) jsonEntry = -- разобрать строку json jsonString `andThen` \key -> -- разобрать одно двоеточие char ‘:’ `иЗатем` \_ -> -- разобрать значение JSON jsonValue `andThen` \value -> -- создать постоянный анализатор, который не потребляет входных данных constParser (ключ, значение)
Силой монад!
Некоторым читателям эта схема и затем покажется знакомой; это потому, что andThen в точности совпадает с оператором привязки Monad :
(>>=) :: Парсер a -> (a -> Парсер b) -> Парсер b
Сделав наш Parser тип экземпляром Monad , мы можем использовать мощь всех функций, которые поставляются с ним, и гораздо более удобный синтаксис нотации do для наших парсеров [5] . Наконец, нашу функцию jsonEntry можно переписать лаконично и просто:
jsonEntry :: Parser (String, JValue) jsonEntry = сделать ключ <- jsonString _ <- символ ‘:’ значение <- jsonValue возврат (ключ, значение)
В качестве примечания стоит упомянуть, что, хотя нотация do делает последовательность явной, мы также будем использовать операторы по всему коду (иначе это не был бы «настоящий Haskell» :P). В частности, следующие Functor и Applicative операторы:
(*>) :: Парсер a -> Парсер b -> Парсер b (<*) :: Парсер a -> Парсер b -> Парсер a (<$) :: a -> Парсер b -> Парсер a -- игнорировать начальные пробелы пробелы *> значение -- игнорировать конечные пробелы значение <* пробелы -- подставить значение Истина <$ строка «истина»
Резюме
Мы уже определили два наших элементарных парсера и увидели, что цепочка является монадической операцией. Все готово: наши основные потребности удовлетворены. Теперь мы можем начать создавать более интересные парсеры. Для начала представим удовлетворяет : небольшая оболочка вокруг любого , которая позволяет нам проверить, соответствует ли следующий символ во входных данных некоторому произвольному требованию.
удовлетворять :: String -> (Char -> Bool) -> Parser Char выполнить предикат описания = попытаться $ сделать с <- любой если предикат c затем вернитесь с описание else parseError [c]
Эта функция, хотя и проста, использует другие функции, с которыми мы еще не сталкивались, такие как try . Это наш первый комбинатор!
Объединение парсеров
Формально комбинатор — это функция, которая не полагается ни на что, кроме своих аргументов, например (.)
оператор композиции функций:
(ф. г) х = ф (г х)
Но неформально… комбинатор — это то, что объединяет другие вещи . И именно в этом неформальном смысле мы используем его здесь! В нашем контексте комбинаторы синтаксических анализаторов — это функции на синтаксических анализаторах : функции, которые объединяют и преобразуют синтаксические анализаторы в другие синтаксические анализаторы для обработки таких вещей, как поиск с возвратом или повторение.
Выбор, ошибки и возврат
Мы знаем, что синтаксические анализаторы могут дать сбой: именно поэтому у нас есть Либо в их типе. Но не все ошибки фатальны: от некоторых из них можно восстановиться. Представьте, например, что вы пытаетесь проанализировать значение JSON: это может быть объект, или массив, или строка… Чтобы проанализировать такое значение, вы можете попробовать проанализировать открывающие фигурные скобки объекта: если это успешно, вы можете продолжить синтаксический анализ объекта; но если это сразу не удается, вы можете вместо этого попытаться разобрать открывающую скобку массива и так далее. Именно с ошибкой и возвратом мы можем реализовать нашу первую «продвинутую» функцию: выбор.
Чтобы отличить реальную ошибку, которая произошла дальше по линии, или что-то, что просто было неправильным выбором, мы проводим различие между синтаксическими анализаторами, которые терпят неудачу без использования каких-либо входных данных , которые терпят неудачу немедленно, и парсеры, которые терпят неудачу позже: мы предполагаем что если какой-либо ввод был потреблен, то мы были на правильной «ветви». Но взгляда только на один символ вперед не всегда будет достаточно, чтобы решить, верна ли ветвь, и в этом случае нам нужна возможность возврата в случае сбоя ветки: нам нужен комбинатор возврата. вот что try делает: он превращает анализатор в анализатор с возвратом: тот, который не использует входные данные в случае сбоя, который восстанавливает состояние до того, что было. Имеет смысл использовать его для удовлетворения : если предикат не работает, мы не встретили ни одного символа, который можно было бы распаковать, и мы должны оставить входную строку без изменений.
попытка :: Парсер a -> Парсер a try p = Parser $ \state -> case runParser p состояние (_newState, левая ошибка) -> (состояние, левая ошибка) успех -> успех
В этом проекте мы называем наши комбинаторы точно так же, как Parsec. Оператор, который Parsec использует для представления выбора, совпадает с оператором, определенным классом типов Alternative : (<|>)
. Его семантика проста: если первый синтаксический анализатор дал сбой, не приняв никаких входных данных [6] , попробуйте второй; в противном случае распространите ошибку:
(<|>) :: Парсер а -> Парсер а -> Парсер а p1 <|> p2 = Parser $ \s -> case runParser p1 s из (с', левая ошибка) | s' == s -> runParser p2 s | иначе -> (s', левая ошибка) успех -> успех
С помощью этого оператора мы, наконец, можем реализовать гораздо более удобный комбинатор: выбор . Имея список синтаксических анализаторов, попробуйте их все, пока один из них не добьется успеха.
выбор :: Строка -> [Парсер а] -> Парсер а описание выбора = папка (<|>) noMatch где noMatch = описание parseError "нет совпадения"
Повторение
Последняя группа комбинаторов, которая нам понадобится для нашего проекта, — это комбинаторы повторения. Они не требуют каких-либо внутренних знаний наших Parser и являются хорошим примером того, какую высокоуровневую абстракцию мы теперь можем писать. Как обычно, мы используем те же имена, что и Parsec: many эквивалентно звездочке регулярного выражения и соответствует нулю или более вхождений данного синтаксического анализатора, а many1 эквивалентно плюсу: соответствует одному или нескольким вхождениям:
много, много1 :: Парсер а -> Парсер [а] много p = много1 p <|> вернуть [] много1 р = делать первый <- р остальные <- много р вернуться (первый: отдых)
Благодаря им мы также можем реализовать sepBy
и sepBy1
, которые сопоставляют повторяющиеся вхождения данного синтаксического анализатора с заданным разделителем между ними:
sepBy, sepBy1 :: Parser a -> Parser s -> Parser [a] sepBy p s = sepBy1 p s <|> return [] sepBy1 p s = делать первый <- р остальное <- много (s >> p) вернуться (первый: отдых)
Резюме
Вот и все: эти семь комбинаторов — все, что нам нужно для реализации синтаксического анализатора JSON с нуля; на данный момент у нас уже есть достаточно хорошая минимальная повторная реализация библиотеки комбинаторов синтаксических анализаторов, подобной Parsec!
Конечно, полноценная библиотека реализовывала бы гораздо больше: больше примитивов для обработки символов, комбинаторов для необязательных парсеров, более специфичных комбинаторов повторения, поддержки улучшенных сообщений об ошибках… Но это выходит за рамки данного упражнения.
Парсеры на практике: разбор JSON
Чтобы собрать воедино все, что мы видели до сих пор, давайте шаг за шагом рассмотрим процесс построения грамматики синтаксического анализатора JSON. Мы будем делать это снизу вверх: начнем с парсеров для отдельных символов, затем перейдем к парсерам для синтаксиса, затем для скаляров… пока, наконец, мы не сможем выразить парсер для произвольного значения JSON. Цель здесь — продемонстрировать, как на каждом этапе мы можем использовать более простые абстракции, которые мы построили, чтобы составить что-то более сложное. В этом разделе немного больше кода, но я надеюсь, что если вы до сих пор следили за всем, вам будет так же приятно читать, как и мне!
Мы будем использовать следующее представление для значений JSON, которое очень близко к тому, что определяет Aeson [7] :
данных JValue = JObject (JValue строки HashMap) | JArray [JValue] | JString Строка | Двойной номер JNumber | JBool Bool | JNull
Распознавание символов
Используя функции Data. Char , давайте начнем определение нашей грамматики с определения типа символов, которые мы хотим распознавать: это прямое использование нашего удовлетворяет функции :
char c = выполнить [c] (== c) пробел = удовлетворить "пробел" isSpace цифра = удовлетворить "цифра" isDigit
Синтаксис языка
Чтобы пойти дальше, мы можем определить удобные синтаксические функции, используя вышеупомянутые аппликационные операторы:
строка = символ пересечения пробелы = много пробелов символ s = строка s <* пробелы между открытым закрытым значением = открытым *> значением <* закрытым скобки = между (символ "[") (символ "]") фигурные скобки = между (символ "{") (символ "}")Реализация
символа здесь основана на определении Parsec лексемы (в их библиотеке определений языка), которая всегда пропускает конечные пробелы; Таким образом, каждый синтаксический анализатор может быть безопасно написан с предположением, что нет начальных пробелов, которые он должен учитывать. Благодаря такому подходу в нашей грамматике JSON очень мало явных упоминаний о пробелах.
Скаляры
Здесь наша реализация будет отличаться от стандарта JSON. Для простоты наш анализатор чисел будет сопоставлять только натуральные числа:
jsonNumber = читать <$> много1 цифра
Для логических значений мы просто сопоставляем два возможных случая:
jsonBool = выбор "JSON boolean" [ True <$ символ "true" , Ложь <$ символ "ложь" ]
Что касается строк, мы сопоставляем последовательность символов между двумя двойными кавычками, но мы должны обрабатывать возможность того, что некоторые символы могут быть экранированы. Мы обрабатываем только небольшое подмножество экранированных символов, реализация остальной части спецификации предоставляется читателю в качестве упражнения. 🙂
jsonString = между (char '"') (char '"') (много jsonChar) <* пробелов где jsonChar = выбор "Строковый символ JSON" [ попробуйте $ '\n' <$ строка "\\n" , попробуйте $ '\t' <$ строка "\\t" , попробуйте $ '"' <$ строка "\\\"" , попробуйте $ '\\' <$ строка "\\\\" , удовлетворять "не кавычки" (/= '"') ]
Массивы и объекты
Значения JSON по определению являются рекурсивными: массивы и объекты содержат другие значения JSON… Чтобы продолжить наш восходящий подход, мы будем предполагать существование верхнего уровня jsonValue парсер, который будет определен последним.
Объект JSON — это группа отдельных записей, разделенных запятыми. Сначала мы анализируем их как список, используя наш комбинатор повторений, а затем преобразуем указанный список ассоциаций в HashMap :
.jsonObject = сделать assocList <- фигурные скобки $ jsonEntry `sepBy` символ "," вернуть $ fromList asocList где jsonEntry = сделать к <- jsonString символ ":" v <- jsonValue возврат (к, в)
Наконец, массив — это просто группа значений, заключенных в скобки и разделенных запятыми:
jsonArray = скобки $ jsonValue `sepBy` символ ","
Собираем все вместе
Наконец, мы можем выразить анализатор верхнего уровня для значения JSON:
jsonValue = выбор "значение JSON" [JObject <$> jsonObject , JArray <$> jsonArray , JString <$> jsonString , JNumber <$> jsonNumber , JBool <$> jsonBool , JNull <$ символ "нуль" ]
И… все!
Завершение
Эта статья является результатом моих личных попыток понять внутреннюю работу Parsec после многих лет его использования. Я попробовал свои силы в написании небольшого синтаксического анализатора JSON с нуля, на стороне, когда я изучал, что заставляет Parsec работать, чтобы лучше понять, применив его на практике. Когда я это сделал, я был поражен тем, насколько маленьким и лаконичным был результирующий код, и насколько простой была грамматика JSON. Я искренне надеюсь, что это пошаговое руководство окажется для вас полезным и даст вам представление о красоте комбинаторов парсеров!
Весь код в этой статье можно найти в этом GitHub gist.
Более продвинутый пример использования комбинаторов синтаксических анализаторов. Если вы хотите узнать больше о том, как мы использовали эту технику для повторной реализации генерации схемы GraphQL, вы можете ознакомиться с описанием PR, в котором она представлена.
Хасура, конечно же, нанимает. Если обсуждения, подобные приведенным выше, кажутся вам убедительными, посмотрите на наши открытые роли и подайте заявку. Если вы хотите следить за тем, что строит команда, где мы говорим, и иногда получать гифки с детёнышами животных. .. Информационный бюллетень сообщества Hasura — это ежемесячная рассылка без традиционного маркетингового спама.
Спасибо за прочтение!
Личное примечание: холм, за который я готов умереть, заключается в том, что монады не сложны, но часто плохо преподаются или преподаются способом, который не предназначен должным образом для аудитории. Я серьезно рассматриваю возможность запустить какую-то программу «интуиция для монад, если вы уже знаете хотя бы один другой язык программирования, менее чем за 15 минут или ваши деньги обратно». ↩︎
Для простоты код в этой статье не будет полностью соответствовать стандарту: мы будем обрабатывать только натуральные числа и сокращенный набор экранируемых символов. ↩︎
Библиотеки комбинаторов парсера не используют напрямую String в качестве входных данных; отчасти потому, что для представления текста доступны лучшие типы, такие как Text , но также и потому, что синтаксический анализатор не всегда является первым шагом трансляции: часто, например, в случае компилятора, лексер или токенизатор уже преобразовывают вводимый текст в последовательность лексем или токенов.
Для синтаксического анализатора важно, чтобы входные данные можно было линейно повторять; Парсек называет это Stream : String представляет собой поток Char , и вывод токенизатора также будет потоком токенов. ↩︎
Большинство библиотек предпочитают передавать запись State , которая упаковывает входные потоки и несет дополнительную информацию, такую как текущая позиция, что позволяет (помимо других преимуществ) гораздо лучше отображать сообщения об ошибках. ↩︎
Хотя Parsec и его производные используют этот монадический подход, можно создавать синтаксические анализаторы, которые полагаются только на Прикладной . Это компромисс: монадические синтаксические анализаторы более мощные, поскольку синтаксический анализ может разветвляться в зависимости от того, что было проанализировано ранее, но более ограниченные аппликативные синтаксические анализаторы допускают статическую интроспекцию значений. ↩︎
Выполнение сравнения строк для проверки того, не смог ли синтаксический анализатор использовать какие-либо входные данные, крайне неэффективно и является досадным следствием нашего упрощенного дизайна.
Если вместо того, чтобы просто передавать String вместе с состоянием, мы должны использовать правильный Запись состояния , мы могли бы реализовать это более производительно, например, сравнив значение, которое представляет нашу позицию во входном потоке. ↩︎
Aeson — наиболее широко используемая библиотека Haskell для работы с JSON. ↩︎
Antoine Leblanc
Похожие статьи
Чему мы научились при переходе с CircleCI на Buildkite
23 ноября, 2021
By
Vishnu Бхарати
Создание компилятора GraphQL to SQL на Postgres, MS SQL и MySQL
29 апреля 2021 г.
By
Phil Freeman
Создание Hasura — CI/CD и история монорепозитория
22 апреля 2021 г.
By
Vishnu Bharathi
Hasura 2.0 Engineering Обзор
18 March, 2021
By
Phil Freeman
Похожие теги
Engineering 90 005
Haskell
Поделиться этой статьей
Мгновенные API GraphQL на Snowflake с Hasura
Просмотреть запись
Похожие статьи
Чему мы научились при переходе с CircleCI на Buildkite
23 ноября, 2021
By
Вишну Бхарати
Создание компилятора GraphQL в SQL на Postgres, MS SQL и MySQL
29 апреля 2021 г.