[Перевод] Языковая модель GPT-3 умеет объяснять код — рассказываем, как это можно использовать

0e165768e2d9bc9378c29d4e3bb6fcd9.png

За два года с момента релиза GPT-3 эту языковую модель использовали в множестве интересных задач — например, для сочинения поэзии, написания футурологических эссе и подготовки научных статей. Но как алгоритм обработки естественного языка может быть полезен программистам?

На этот вопрос в своей новой статье отвечает британский разработчик Саймон Уилисон* — директор по архитектуре Eventbrite и один из создателей веб-фреймворка Django. Среди различных вариантов применения языковой модели GPT-3 Уилисон особенно подчеркивает ее способность объяснять, что делает код. По словам специалиста, в этом GPT-3 поразительно эффективна, поскольку явно обучалась на огромном количество исходного кода.

*Обращаем ваше внимание, что позиция автора не всегда может совпадать с мнением МойОфис.

Объясняя Python

На днях разработчик Нэд Бэтчелдер поделился прекрасным фрагментом кода:

import re

TOKEN_RX = r"""(?xm)
    (?P ".*?"|'.*?'             )| 
    (?P  \d*(\d\.|\.\d)\d*       )|
    (?P    \d+                     )|
    (?P     [_a-zA-Z][_a-zA-Z0-9]*  )|
    (?P  [(){}:\[\]=.,+*/-]      )|
    (           \#.*$                   )|
    (           \s+                     )
    """

def tokens(text):
    for match in re.finditer(TOKEN_RX, text):
        if match.lastgroup:
            yield (match.lastgroup, match[0])

TEXT = """
    x = 123 + "hello #99"  # ignore me!
    print(hello.bye[0] + 3.14, 'single')
    """

for kind, text in tokens(TEXT):
    print(f"{kind:7}: {text=}")

Я добавил код в GTP-3 (вот инструкция к песочнице) и начал задавать ей вопросы.

При этом я использовал функцию GTP-3, которая позволяет после выдачи ответа ввести новый текст и в итоге получить дополнительные пояснения.

Все, что выделено ниже жирным шрифтом, — это ответы GPT-3. Остальное — мой текст.

Объясни этот код:

import re

TOKEN_RX = r"""(?xm)
    (?P ".*?"|'.*?'             )| 
    (?P  \d*(\d\.|\.\d)\d*       )|
    (?P    \d+                     )|
    (?P     [_a-zA-Z][_a-zA-Z0-9]*  )|
    (?P  [(){}:\[\]=.,+*/-]      )|
    (           \#.*$                   )|
    (           \s+                     )
    """

def tokens(text):
    for match in re.finditer(TOKEN_RX, text):
        if match.lastgroup:
            yield (match.lastgroup, match[0])

TEXT = """
    x = 123 + "hello #99"  # ignore me!
    print(hello.bye[0] + 3.14, 'single')
    """

for kind, text in tokens(TEXT):
    print(f"{kind:7}: {text=}") 

Этот код определяет регулярное выражение, которое будет соответствовать различным типам токенов в текстовой строке. Различные группы, определенные в регулярном выражении, будут соответствовать различным типам токенов, таким как строки, числа с плавающей запятой, целые числа, идентификаторы, знаки препинания и комментарии. Затем код будет перебирать совпадения и выдавать тип токена и текст, который ему соответствует.

Объясни регулярное выражение:

Регулярное выражение состоит из нескольких разных групп, каждая из которых соответствует определенному типу токена. Первая группа, «string», соответствует любому тексту, заключенному в двойные или одинарные кавычки. Вторая группа, «float», соответствует любому тексту, который похож на число с плавающей запятой. Третья группа, «int», соответствует любому тексту, который выглядит как целое число. Четвертая группа, «id», соответствует любому тексту, похожему на имя переменной. Пятая группа, «punct», соответствует любому тексту, похожему на пунктуацию. Шестая группа »#.*$» соответствует любому тексту, похожему на комментарий. Седьмая группа,»\s+», соответствует любому тексту, который выглядит как пробел.

Что делает (? xm)?

Нотация (? xm) — это модификатор, который позволяет регулярному выражению размещаться на нескольких строках и содержать комментарии.

Почему он использует match.lastgroup?

Переменная match.lastgroup содержит имя последней группы, найденной с помощью регулярного выражения. Это используется для определения типа сопоставленного токена.

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

Объясняя JavaScript

Разработчик Крис Хейнс опубликовал блестящую демонстрацию того, как новый элемент Web Component Declarative Shadow DOM, , (описан здесь), можно использовать для создания страницы, которая передает контент с сервера, заполняя различные области страницы по мере поступления контента — без использования JavaScript!

Демо доступно здесь; у меня оно запускается только в Chrome, для других браузеров существует версия с полифиллом.

Код полифилла можно увидеть на этой странице. Я не понимал, как он работает, и спросил у GPT-3.

Объясни этот код:

class Enamel extends HTMLElement {
  attemptPolyfillDSD() {
    const dsd = this.querySelector('template[shadowroot]');

    if (dsd?.content) {
      const mode = dsd.getAttribute('shadowroot');
      this.attachShadow({ mode });
      this.shadowRoot.appendChild(dsd.content);

      dsd.remove();

      return true;
    }

    return false;
  }

  connectedCallback() {
    if (
      !HTMLTemplateElement.prototype.hasOwnProperty('shadowRoot') &&
      !this.attemptPolyfillDSD()
    ) {
      const _observer = new MutationObserver(() => {
        if (this.attemptPolyfillDSD()) {
          _observer.disconnect();
        }
      });

      _observer.observe(this, {
        childList: true,
      });
    }
  }
}

export default Enamel;

Этот код подменяет API Shadow DOM для старых браузеров, которые его не поддерживают.

Что ж, звучит убедительно, но информации недостаточно. Дополняю вопрос:

Как он делает это?

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