Как стать автором
Обновить

Комментарии 5

Очень интересно. Спасибо за вашу работу.
Осмысленные подсказки по коду это значительный шаг вперед.


Впрочем у людей-преподавателей все еще есть преимущество.


Преподаватели объясняют ученикам, в чем заключаются их ошибки и на что стоит обратить внимание, в то время как автоматическая проверяющая система может лишь проверить корректность решения.

Некоторые ошибки не нужно объяснять. Лучше когда человек сам проходит путь к пониманию ошибки, а преподаватель только помогает наводящими вопросами.


Однако оказалось, что одну и ту же ошибку можно исправлять множеством способов. Чтобы стандартизировать способ исправления ошибки, было решено искать разницу не с правильным решением из той же сессии, а со всеми известными правильными решениями и использовать самый короткий из получившихся сценариев редактирования.

Самый короткий сценарий редактирования далеко не всегда — оптимальный с точки зрения обучения. Иногда нужно сначала написать большую простыню с очевидным текстом, а потом ее отрефакторить.
Проиллюстрирую на простом примере. При изучении циклов от человека требуется вывести:


Введение
Глава 1
Глава 2
Глава 3
Глава 4
Глава 5
Глава 6
Глава 7
Заключение

А он пишет


int i = 0;
while (i < 7) {
    Console.WriteLine("Введение")
    Console.WriteLine("Глава " + i);
    i = i + 1;
    Console.WriteLine("Заключение");
}

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


Console.WriteLine("Введение");
Console.WriteLine("Глава 1");
Console.WriteLine("Глава 2");
Console.WriteLine("Глава 3");
Console.WriteLine("Глава 4");
Console.WriteLine("Глава 5");
Console.WriteLine("Глава 6");
Console.WriteLine("Глава 7");
Console.WriteLine("Заключение");

И говорю: В цикл мы помещаем действия, которые должны повторяться. Какие действия в моем фрагменте повторяются? Какие действия повторяются в твоем фрагменте?

Лучше когда человек сам проходит путь к пониманию ошибки, а преподаватель только помогает наводящими вопросами.

Да, мы тоже так считаем! Идея как раз в том, чтобы оставить составление подсказки за преподавателем. В момент разметки кластеров можно написать как явную подсказку, так и наводящий вопрос.
Самый короткий сценарий редактирования далеко не всегда — оптимальный с точки зрения обучения.

Мы не используем сценарии редактирования для непосредственной генерации подсказок — они используются только для понимания содержащейся в решении ошибки. Если два решения можно исправить похожим сценарием, то, скорее всего, и ошибки в них были похожие. Использование кратчайшего сценария здесь для другого: указанное вами решение можно исправить очевидным образом, вынеся вывод из цикла, но можно и, например, вот так:
int i = 0;
while (i < 7) {
    if (i == 0) {
        Console.WriteLine("Введение")
    }
    Console.WriteLine("Глава " + i);
    i = i + 1;
    if (i == 7) {
        Console.WriteLine("Заключение");
    }
}

Использование кратчайшего скрипта редактирования позволит получать одинаковые скрипты редактирования в таких случаях.

Посмотрел внимательнее, вы указываете на такие ошибки, которые ловит статический анализатор вроде сонара.
Исследовали ли вы случаи, когда решение студентов не компилировалось?
Ну или когда все было написано правильно с точки зрения использования конструкций, но при этом не решало задачу?

1) Кажется, что в отрыве от задачи ошибки «Неправильная обработка пустого потока» (не вызывается метод accept), «Повторный вызов метода accept», «Путаница в операторах сравнения» найти нельзя.

2) Рассматривались все решения, для которых удавалось построить AST. Т.е. если нарушена грамматика языка, тот тут уже сложно что-то анализировать. Однако, если компиляция не возможна т.к. вызывается несуществующий метод, то тут мы можем попробовать помочь.

В целом пытались отлавливать все ошибки — лишнее всегда можно отфильтровать. Однако писать подсказки к ошибкам, проверяемым статическим анализатором, тоже может быть полезно т.к. можно дать более конкретные, развёрнутые объяснения. Например, дать ссылку на урок, где поднималась эта тема.

Прикинул, как бы я подсказывал в ваших случаях.


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

public static <T> void findMinMax(
       Stream<? extends T> stream,
       Comparator<? super T> order,
       BiConsumer<? super T, ? super T> minMaxConsumer) {

   if (stream.count() == 0) {
       minMaxConsumer.accept(null, null);
   } else {
       T min = stream.min(order).get();
       T max = stream.max(order).get();
       minMaxConsumer.accept(min, max);
   }
}

Я бы предложил поменять местами использование стримов и сравнить в отладчике результаты обоих вариантов на примере стрима "9, 3, 5, 1". Человек бы увидел что в варианте


T min = stream.min(order).get();
T max = stream.max(order).get();

в min попадает 1, а в max — null (?) я тут не уверен, потому что в голове такое не скомпилирую. Но точно не число 9.
А в варианте


T min = stream.min(order).get();
T max = stream.max(order).get();

наоборот — в max попадает 9, а в min — неожиданное значение, не 1.
ну и поспрашивал бы, чем например стрим отличается от коллекции. Может человек вообще концепцию не понимает, а мы ему правильный ответ советуем.

Зарегистрируйтесь на Хабре , чтобы оставить комментарий