Skip to content

Instantly share code, notes, and snippets.

@madwareru
Last active November 4, 2023 16:29
Show Gist options
  • Save madwareru/f8acf0df1d47b033d3ab9ce76ca3380a to your computer and use it in GitHub Desktop.
Save madwareru/f8acf0df1d47b033d3ab9ce76ca3380a to your computer and use it in GitHub Desktop.
пост-отзыв по книге Essentials Of Programming Languages после первой неполной недели чтения

Итак, пост-отзыв по EOPL3 после первой неполной недели чтения.

Из двух глав осилил пока только первую, где в основном столкнулся с проблематикой задания различных наборов данных через индукцию, как и было обещано в названии 🙂

Обычно, множества предлагается задавать через т.н. list comprehensions, которые есть во многих ФП языках типа хаскеля, а так же есть в python, C#, Kotlin и т.д.

Примеры ниже (на python, C# и Kotlin соответственно):

[3 * n for n in natural_numbers()]
from n in NaturalNumbers() select 3 * n;
//  или аналогичное, но без Expression Syntax:
NaturalNumbers().Select(n => n * 3);
naturalNumbers().map { n -> n * 3 }

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

Причём можно определить это двумя способами:

in_s (0) :- true.
in_s (n) :- in_s (n - 3).

либо

in_s (0) :- true.
in_s (n + 3) :- in_s(n).

Первый способ задаёт множество сверху-вниз. В этом случае, для того, чтобы узнать, входит ли некое натуральное число в множество, нам нужно либо уже знать о факте его наличия в множесте, либо вывести этот факт из другого числа. Можно прочитать это как:

n входит в множество S тогда и только тогда, если 
1. n = 0, 
2. n - 3 входит в множество S

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

1. 0 входит в множество S,
2. если n входит в множество S, то n + 3 тоже в него входит

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

После некоторого ряда примеров, предлагается ещё одит способ задания множеств, в этом случае через грамматики. Грамматики пишутся с использованием подобия BNF в подобном виде:

<List-of-Int> ::= ()
<List-of-Int> ::= ( <Int> . <List-of-Int> )

Тут <List-of-Int> является синтаксической категорией, и задаётся через две продукции.

Первая задаёт конечное условие о том, что пустой список это тоже список, причём пустой список записывается двумя терминальными символами ( и ).

Вторая продукция указывает, что <List-of-Int> является парой из другой синтаксической категории <Int>, являющейся головой списка и, собственно, другого <List-of-Int>, являющегося хвостом. При этом такой список опоясан парными терминальными символами ( и ), а голова и хвост разделены терминальным символом .

Далее в книге вводятся способы проверять структуры данных на соответствие грамматике через так называемые syntatic derivations, где мы последовательно применяем замену нетерминалов в грамматике на терминалы. Если в итоге остались только терминальные символы, структура соответствует грамматике. Переписывать примеры из книги не вижу особого смысла, так что двигаемся дальше.

Далее идут описания через грамматику упрощённых s-выражений, бинарных деревьев и лямбда-варажений, и попутно вводятся новые способы записывать грамматику более коротким способом. Для бинарных деревьев приводится пример, как можно по индукции доказывать теоремы о наборах данных, заданных грамматиками. Так, приводится доказательство, что у бинарного дерева всегда нечётное количество нод. Так же есть упражнение на доказательство теоремы о парности скобок в лямбда-выражении.

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment