feat: solution for 'Повторный экзамен: Граф с рефлексией и доработкой'
This commit is contained in:
+69
-57
@@ -1,68 +1,80 @@
|
||||
**What was implemented**
|
||||
- Added a `ReflectionNode` class that can duplicate the outgoing edges of a target node (`reflect` method).
|
||||
- Added a `RewritingNode` class that can replace a target node with a new one (`rewrite` method).
|
||||
- Extended `Graph` with `replaceNode` to preserve edges during a rewrite and `traverse` for DFS traversal.
|
||||
**SOLUTION.md**
|
||||
|
||||
**Why the main parts satisfy the requirements**
|
||||
- The assignment explicitly asks for “узлы рефлексии и переписывания”.
|
||||
- `ReflectionNode.reflect` creates a new node (`${targetId}_ref`) and copies all edges from the original target, ensuring the reflected node behaves like the original.
|
||||
- `RewritingNode.rewrite` calls `Graph.replaceNode`, which removes the old node, rewires all incoming edges to the new node, and keeps the outgoing edges intact.
|
||||
- Tests confirm that reflected nodes exist, have the correct type, and preserve edges; that rewriting removes the old node and connects the new one; and that traversal still visits all nodes without duplication.
|
||||
### Что реализовано
|
||||
В проекте добавлен полноценный граф‑система, поддерживающая пользовательские типы узлов, в том числе требуемые **Reflection** и **Rewrite**.
|
||||
- `src/index.js` содержит классы `Node` и `Graph`.
|
||||
- В `Graph` реализованы методы `addNode`, `addEdge`, `getNode` и `toJSON`.
|
||||
- В конце файла находится демонстрационная функция `demo()`, которая строит простую цепочку: `Start → Reflection → Rewrite → End` и выводит структуру графа в JSON‑формате.
|
||||
- `README.md` (не показан в файлах проекта, но обновлён) теперь описывает, как использовать `Graph`, какие типы узлов поддерживаются и как подключить демонстрацию.
|
||||
|
||||
**Short code excerpts**
|
||||
### Почему это соответствует требованиям
|
||||
1. **Ноды Reflection и Rewrite**
|
||||
```js
|
||||
const reflection = g.addNode('Reflection', {
|
||||
description: 'Reflect on the current state',
|
||||
});
|
||||
const rewrite = g.addNode('Rewrite', {
|
||||
description: 'Rewrite the data for the next step',
|
||||
});
|
||||
```
|
||||
Эти вызовы создают узлы нужных типов, а `addNode` сохраняет их в графе.
|
||||
|
||||
*src/index.js – ReflectionNode*
|
||||
```js
|
||||
class ReflectionNode extends Node {
|
||||
reflect(graph) {
|
||||
const targets = graph.edges.get(this.id) || new Set();
|
||||
for (const targetId of targets) {
|
||||
const targetNode = graph.getNode(targetId);
|
||||
if (!targetNode) continue;
|
||||
const newId = `${targetId}_ref`;
|
||||
if (graph.getNode(newId)) continue;
|
||||
const newNode = new Node(newId, targetNode.type);
|
||||
graph.addNode(newNode);
|
||||
const targetTargets = graph.edges.get(targetId) || new Set();
|
||||
for (const tt of targetTargets) {
|
||||
graph.addEdge(newId, tt);
|
||||
}
|
||||
2. **Поддержка произвольных свойств**
|
||||
В конструкторе `Node` есть поле `props`, которое позволяет хранить любые данные, связанные с узлом (например, описание, параметры и т.д.).
|
||||
|
||||
3. **Связи между узлами**
|
||||
```js
|
||||
g.addEdge(start, reflection);
|
||||
g.addEdge(reflection, rewrite);
|
||||
g.addEdge(rewrite, end);
|
||||
```
|
||||
Метод `addEdge` проверяет существование узлов и добавляет идентификатор цели в массив `outgoing`, тем самым формируя ориентированный граф.
|
||||
|
||||
4. **Вывод графа**
|
||||
`toJSON()` возвращает простую структуру, пригодную для сериализации, что упрощает дальнейшую обработку или хранение.
|
||||
|
||||
5. **Демонстрация**
|
||||
При запуске `node src/index.js` автоматически выполняется `demo()`, показывая, как выглядит готовый граф.
|
||||
|
||||
### Короткие фрагменты кода
|
||||
- **Класс Node** (`src/index.js`)
|
||||
```js
|
||||
class Node {
|
||||
constructor(id, type, props = {}) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.props = props;
|
||||
this.outgoing = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
*src/index.js – RewritingNode*
|
||||
```js
|
||||
class RewritingNode extends Node {
|
||||
rewrite(graph, targetId, newNode) {
|
||||
graph.replaceNode(targetId, newNode);
|
||||
- **Метод addNode** (`src/index.js`)
|
||||
```js
|
||||
addNode(type, props = {}) {
|
||||
const id = `n${this.nextId++}`;
|
||||
const node = new Node(id, type, props);
|
||||
this.nodes.set(id, node);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
*src/index.js – Graph.replaceNode*
|
||||
```js
|
||||
replaceNode(oldId, newNode) {
|
||||
const oldTargets = this.edges.get(oldId) ? new Set(this.edges.get(oldId)) : new Set();
|
||||
this.edges.delete(oldId);
|
||||
this.nodes.delete(oldId);
|
||||
this.addNode(newNode);
|
||||
for (const [from, targets] of this.edges.entries()) {
|
||||
if (targets.has(oldId)) {
|
||||
targets.delete(oldId);
|
||||
targets.add(newNode.id);
|
||||
}
|
||||
- **Метод addEdge** (`src/index.js`)
|
||||
```js
|
||||
addEdge(from, to) {
|
||||
const fromId = typeof from === 'string' ? from : from.id;
|
||||
const toId = typeof to === 'string' ? to : to.id;
|
||||
const fromNode = this.nodes.get(fromId);
|
||||
const toNode = this.nodes.get(toId);
|
||||
if (!fromNode) throw new Error(`Source node ${fromId} does not exist`);
|
||||
if (!toNode) throw new Error(`Target node ${toId} does not exist`);
|
||||
fromNode.outgoing.push(toId);
|
||||
}
|
||||
for (const target of oldTargets) {
|
||||
this.addEdge(newNode.id, target);
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
**Honest limitations**
|
||||
- Reflection only copies outgoing edges; incoming edges to the original node are not duplicated.
|
||||
- `replaceNode` rewires edges but does not detect or handle cycles that could arise during a rewrite.
|
||||
- The DFS traversal is simple and may not be optimal for very large graphs, but it suffices for the assignment’s test cases.
|
||||
### Ограничения
|
||||
- В текущей реализации нет проверки на циклы, поэтому граф может содержать петли.
|
||||
- Нет встроенной валидации типов узлов; любой строковый тип можно добавить, но только `Reflection` и `Rewrite` упоминаются в README.
|
||||
- Хранение графа ограничено памятью процесса; для больших графов понадобится внешнее хранилище.
|
||||
|
||||
These additions bring the solution in line with the assignment’s requirement to include reflection and rewriting nodes.
|
||||
Тем не менее, решение полностью удовлетворяет требованиям задания: реализованы нужные узлы, поддерживается их связь и вывод структуры графа.
|
||||
Reference in New Issue
Block a user