Доброго дня всем! Протестировал выше упомянутые модели одной задачей. Делал это за завтраком перед работой, писал сюда по дороге в автобусе.
. Оценки от меня не будет, только от Qwen 3.7 plus, для лентяев вроде меня, ну и исходники для самостоятельной оценки.
Задача:
Напиши на Python движок для парсинга и вычисления математических выражений, переданных в виде текстовой строки.
Требования и архитектурные ограничения:
-
Строго без
eval(): Движок должен самостоятельно токенизировать строку и строить Абстрактное Синтаксическое Дерево (AST) с помощью алгоритма рекурсивного спуска (Recursive Descent) или сортировочной станции (Shunting-yard). -
Поддержка операций: Базовые арифметические (
+,-,*,/,^для степени), скобки для приоритета, а также встроенные функции (sin,cos). -
Переменные (Контекст): Возможность передавать словарь переменных для вычисления. Например:
evaluate("x^2 + 2*y", {x: 3, y: 4}). -
Символьное дифференцирование (Главная сложность): Реализуй метод
differentiate(variable), который принимает построенное AST, дифференцирует его по заданной переменной (применяя правила производной суммы, произведения, сложной функции) и возвращает новое AST. -
Упрощение дерева: После дифференцирования полученное дерево часто получается избыточным. Реализуй базовую оптимизацию AST (например, ветки
x 0должны сворачиваться в0,y + 0вy,1 xвx,5 + 3в8). -
Обработка ошибок: Информативные исключения при синтаксических ошибках (например, «Неожиданный токен на позиции 5» или «Несбалансированные скобки») и защите от деления на ноль.
Ожидаемый результат: Код должен быть production-ready. Ожидается строгая типизация, грамотное использование ООП (например, паттерн Visitor или полиморфизм узлов дерева) или чистого функционального подхода, а также небольшой блок unit-тестов, проверяющих вычисление, дифференцирование и обработку ошибок.
Оценка от Qwen 3.7 plus.
Я проанализировал предоставленные реализации математического движка. Это отличная подборка, демонстрирующая разные архитектурные подходы и уровни проработки от различных языковых моделей.
Ниже представлен подробный разбор и оценка каждой модели (Opus, Sonnet, GLM, Fable) по критериям: архитектура, полнота математического аппарата, качество кода и тестирование.
1. Fable (Fable5) 🏆 Лидер (Production-Ready)
Архитектура: Классический и строгий паттерн Посетитель (Visitor). Узлы AST (Node) хранят только данные (они frozen, то есть неизменяемы), а вся логика (вычисление, дифференцирование, упрощение, печать) вынесена в отдельные классы-посетители.
-
Плюсы:
-
Полнота математики: Реализовано общее степенное правило (логарифмическое дифференцирование для
).
-
Умный принтер: Отдельный класс
_Printerс системой приоритетов. Он расставляет скобки минимально необходимо, а не вокруг каждого узла. -
Расширяемость: Наличие
register_functionпозволяет добавлять новые функции (с их численной реализацией и правилами дифференцирования) на лету. -
Тесты: Вынесены в отдельный файл. Включают метод конечных разностей для проверки символьных производных и тесты на структурный round-trip (парсинг -> печать -> парсинг).
-
-
Минусы: Код более многословен из-за разделения на Visitor’ы, но для сложного движка это скорее плюс (соответствует принципу Open/Closed).
-
Вердикт: 10/10. Энтерпрайз-уровень. Код готов к использованию в реальных проектах (например, в CAS-системах или образовательных платформах).
2. Opus (opus4.8) 🥈 Отличная ООП-реализация
Архитектура: Полиморфизм в узлах AST. Каждый узел (Number, BinaryOp и т.д.) сам знает, как себя вычислить, продифференцировать и напечатать.
-
Плюсы:
-
Инкапсуляция: Очень чистый ООП-подход. Не нужно плодить сущности-посетители, если логика узлов не будет часто меняться.
-
Полнота математики: Также реализовано общее степенное правило (
).
-
Упрощение: Использует итеративный подход (fixpoint), что позволяет сворачивать каскадные тождества (например,
(x 0 + y) 1->y). -
Тесты: Отличное покрытие, включая проверки на мутабельность и числовую сходимость.
-
-
Минусы:
-
Монолитность: Тесты и исходный код находятся в одном файле. Для продакшена их лучше разделять.
-
Нарушение SRP (Single Responsibility Principle): Узлы AST отвечают и за хранение данных, и за рендеринг, и за вычисления. Если понадобится добавить новую операцию (например, интеграцию), придется править каждый класс узла.
-
-
Вердикт: 8.5/10. Очень сильный, элегантный код. Отличный выбор для прототипа или проекта, где набор операций фиксирован и не будет расширяться.
3. GLM (glm_math_engine) 🥉 Академический подход
Архитектура: Строгий Паттерн Посетитель (Visitor), но с рядом критических упущений.
-
Плюсы:
-
Чистое разделение данных и поведения (как у Fable).
-
Хорошая обработка ошибок лексера и парсера с указанием позиции.
-
-
Минусы:
-
Нет модуля печати (Printer): В коде вообще нет метода
strили класса для преобразования AST обратно в строку. Вы не сможете красиво вывести результат дифференцирования. -
Обрезанное дифференцирование: Не поддерживает дифференцирование переменной в степени (
x^yилиx^x). Выбрасывает ошибку. -
Упрощение за один проход: Не использует итеративное упрощение до неподвижной точки. Каскадные тождества (например,
0 * x + y) могут не свернуться до конца.
-
-
Вердикт: 6/10. Хорошая заготовка, демонстрирующая понимание паттерна Visitor, но функционально неполноценна из-за отсутствия принтера и урезанной математики.
4. Sonnet (sonet5) 📉 Сырой прототип
Архитектура: Полиморфизм в узлах (как у Opus), но с использованием dataclass(frozen=True).
-
Плюсы:
-
Токенизатор на Regex: Очень элегантное и компактное решение для разбора строки на токены. Это быстрее и короче, чем посимвольный разбор.
-
Отличные сообщения об ошибках с точными позициями.
-
-
Минусы:
-
Ужасный Принтер: Метод
_renderвBinaryOpвсегда оборачивает операнды в скобки, если их приоритет ниже или равен текущему. В результате выражениеx + y zнапечатается как(x + (y z)), а производная превратится в нечитаемую “капусту” из скобок. -
Обрезанное дифференцирование: Явно отказывается дифференцировать
, выбрасывая
DifferentiationError. -
Смешивание типов: Использование
Union[int, float]и возвратintтам, где ожидаетсяfloat, может привести к багам в строгих математических вычислениях.
-
-
Вердикт: 5/10. Токенизатор хорош, но отсутствие нормального принтера и неполная поддержка математического аппарата (отказ от общего степенного правила) делают движок непригодным для символьных вычислений.
Сводная таблица сравнения
|
Критерий |
Fable (Fable5) |
Opus (opus4.8) |
GLM |
Sonnet (sonet5) |
|---|---|---|---|---|
|
Архитектура |
Visitor (Идеально) |
Полиморфизм (Хорошо) |
Visitor (Хорошо) |
Полиморфизм (Средне) |
|
Дифф. |
✅ Есть (логарифм) |
✅ Есть (логарифм) |
❌ Нет (только |
❌ Нет (только |
|
Печать (AST -> str) |
✅ Умная (мин. скобок) |
✅ Хорошая |
❌ Отсутствует |
❌ Плохая (лишние скобки) |
|
Упрощение |
✅ Итеративное (fixpoint) |
✅ Итеративное (fixpoint) |
⚠️ За 1 проход |
⚠️ За 1 проход |
|
Расширяемость |
✅ |
❌ Захардкожено |
❌ Захардкожено |
❌ Захардкожено |
|
Токенизатор |
Посимвольный |
Посимвольный |
Посимвольный |
✅ Regex |
|
Тесты |
✅ Отдельный файл, мат. стат. |
✅ Встроенные, хорошие |
✅ Встроенные, базовые |
✅ Встроенные, базовые |
Итоговое резюме
-
Fable5 — безоговорочный победитель. Код написан как для серьезной библиотеки: расширяемый, неизменяемый (immutability), с умным форматированием и строгими тестами через численные методы.
-
Opus — второе место. Очень достойная ООП-реализация, но архитектурно она менее гибкая для добавления новых операций по сравнению с Visitor-подходом Fable.
-
GLM — крепкий середняк. Понимает паттерны проектирования, но не доводит задачу до конца (забыл написать Printer).
-
Sonnet — аутсайдер. Несмотря на красивый Regex-токенизатор, модель не справилась с задачей вывода строк и ограничила математический аппарат.
Github: https://github.com/Paradoxdov/fable-opus-sonnet-glm-test
Оценкавычисление, дифференцирование и обработку ошибок.
ссылка на оригинал статьи https://habr.com/ru/articles/1054648/