За последние пару недель я читал о развитии навыков и разбивке навыков на более управляемые куски, и недавно у меня была возможность разбить навыки, необходимые для обучения циклу.
Сначала я набросал схему развития навыка, но быстро понял, что нарисовал график зависимости, и подумал, что его включение в Neo4j упростит ситуацию.
Я начал с общей цели катания на велосипеде, которая заключалась в том, чтобы «иметь возможность ездить на велосипеде через общественный парк»:
MERGE (:Goal:Task {name: "Be able to cycle through a public park"})
Эта цель проста для тех, кто уже научился ездить на велосипеде, но если мы начинаем с нуля, это немного утомительно, поэтому нам нужно разбить ее на более простой навык, который мы можем практиковать.
Мини-алгоритм, который мы собираемся использовать для разбивки задач:
- Можем ли мы выполнить данную задачу сейчас?
- Разбейте задачу на что-то более простое и вернитесь к 1.
Одна из вещей, которую нужно иметь в виду, это то, что мы не получим идеальный разрыв в первый раз, поэтому нам, возможно, придется его изменить. Для диаграммы, нарисованной на листе бумаги, это будет раздражать, но в Neo4j это просто более простой рефакторинг.
Возвращаясь к велоспорту. Поскольку цель еще не достижима, нам нужно разбить ее на что-то более простое. Давайте начнем с чего-то действительно простого:
MERGE (task:Task {name: "Take a few steps forward while standing over the bike"})
WITH task
MATCH (goal:Goal:Task {name: "Be able to cycle through a public park"})
MERGE (goal)-[:DEPENDS_ON]->(task)
В первой строке мы создаем нашу новую задачу, а затем связываем ее с нашей целью, которую мы создали ранее.
После того, как мы привыкли ходить на велосипеде, мы хотим освоиться с ездой вперед на несколько оборотов, когда сидим на велосипеде, но для этого нам нужно иметь возможность начать движение велосипеда с места стоя. У нас также может быть еще один шаг, когда мы будем ездить на велосипеде вперед, стоя на велосипеде, так как это может быть немного проще.
Давайте обновим наш график:
// First let's get rid of the relationship between our initial task and the goal
MATCH (initialTask:Task {name: "Take a few steps forward while standing over the bike"})
MATCH (goal:Goal {name: "Be able to cycle through a public park"})
MATCH (goal)-[rel:DEPENDS_ON]->(initialTask)
DELETE rel
WITH initialTask, goal, ["Get bike moving from standing start", "Cycle forward while standing", "Cycle forward while sitting"] AS newTasks
// Create some nodes for our new tasks
UNWIND newTasks AS newTask
MERGE (t:Task {name: newTask})
WITH initialTask, goal, COLLECT(t) AS newTasks
WITH initialTask, goal, newTasks, newTasks[0] AS firstTask, newTasks[-1] AS lastTask
// Connect the last task to the goal
MERGE (goal)-[:DEPENDS_ON]->(lastTask)
// And the first task to our initial task
MERGE (firstTask)-[:DEPENDS_ON]->(initialTask)
// And all the tasks to each other
FOREACH(i in RANGE(0, length(newTasks) - 2) |
FOREACH(t1 in [newTasks[i]] | FOREACH(t2 in [newTasks[i+1]] |
MERGE (t2)-[:DEPENDS_ON]->(t1)
)))
Строго говоря, нам не нужно учиться ездить на велосипеде, стоя — мы могли бы просто перейти от движения велосипеда к велосипеду вперед, сидя. Давайте обновим график, чтобы отразить это:
MATCH (sitting:Task {name: "Cycle forward while sitting"})
MATCH (moving:Task {name: "Get bike moving from standing start"})
MERGE (sitting)-[:DEPENDS_ON]->(moving)
Как только мы разберемся с этими задачами, давайте добавим еще несколько, чтобы приблизить нас к нашей цели:
WITH [
{skill: "Controlled stop using brakes/feet", dependsOn: "Cycle forward while sitting"},
{skill: "Steer around stationary objects", dependsOn: "Controlled stop using brakes/feet"},
{skill: "Steer around people", dependsOn: "Steer around stationary objects"},
{skill: "Navigate a small circular circuit", dependsOn: "Steer around stationary objects"},
{skill: "Navigate a loop of a section of the park", dependsOn: "Navigate a small circular circuit"},
{skill: "Navigate a loop of a section of the park", dependsOn: "Steer around people"},
{skill: "Be able to cycle through a public park", dependsOn: "Navigate a loop of a section of the park"}
] AS newTasks
FOREACH(newTask in newTasks |
MERGE (t1:Task {name: newTask.skill})
MERGE (t2:Task {name: newTask.dependsOn})
MERGE (t1)-[:DEPENDS_ON]->(t2)
)
Наконец, давайте избавимся от отношения с нашей целью «Цикл вперед, сидя», так как мы заменили это на несколько промежуточных шагов:
MATCH (task:Task {name: "Cycle forward while sitting"})
WITH task
MATCH (goal:Goal:Task {name: "Be able to cycle through a public park"})
MERGE (goal)-[rel:DEPENDS_ON]->(task)
DELETE rel
И вот как выглядит окончательный граф зависимостей:
Хотя я поместил это в Neo4j, чтобы визуализировать зависимости, теперь мы можем также запрашивать данные. Например, скажем, я знаю, как ездить на велосипеде вперед, сидя на велосипеде. Какие шаги есть между мной и возможностью кататься на велосипеде вокруг парка?
MATCH (t:Task {name: "Cycle forward while sitting"}),
(g:Goal {name: "Be able to cycle through a public park"}),
path = shortestpath((g)-[:DEPENDS_ON*]->(t))
RETURN path
Или, если мы хотим получить список задач, которые нам нужно выполнить, мы могли бы немного реструктурировать запрос:
MATCH (t:Task {name: "Cycle forward while sitting"}),
(g:Goal {name: "Be able to cycle through a public park"}),
path = shortestpath((t)<-[:DEPENDS_ON*]->(g))
WITH [n in nodes(path) | n.name] AS tasks
UNWIND tasks AS task
RETURN task
==> +--------------------------------------------+
==> | task |
==> +--------------------------------------------+
==> | "Cycle forward while sitting" |
==> | "Controlled stop using brakes/feet" |
==> | "Steer around stationary objects" |
==> | "Steer around people" |
==> | "Navigate a loop of a section of the park" |
==> | "Be able to cycle through a public park" |
==> +--------------------------------------------+
==> 6 rows
Пока это все, но я думаю, что это интересный способ отследить, как вы изучаете навык. Я пытаюсь подобный подход для некоторых статистических тем, о которых я узнаю, но я обнаружил, что порядок задач там не такой линейный — интересно гораздо больше графика, чем дерева.