
Комментарии 3
Простая, постоянно требующя решения задача подмены параметра в теле лямбды превращается в армагеддон. Да она решается созданием одного ReplacingExpressionVisitor (такой себе класик)
И такого вызова:
var newParam = Expression.Parameter(someType);
var newBody = ReplacingExpressionVisitor.Replace(lambda.Paramters[0], newParam, lambda.Body);А еще она решаться может и вот так:
var newParam = Expression.Parameter(someType);
var newBody = lambda.Body.Transform(e => e == lambda.Paramters[0] ? newParam : e);Вроде одинаково, но гипотетически усложним задачу, мало того, что надо подменить параметр, так и прибавить единички ко всем целочисленным константам. Тут уже надо городить новые классы или даже пропускать через несколько визиторов, а мы в лямбде для Transform просто усложняем условие. Это может быть ощутимо заметно на гигантском дереве, такие иногда получаются когда запрос к базе содержит монструидальный Where, созданный динамичиски какой-то UI библиотекой.
Минус Transform в том что если вы придумываете кастомные Expression, тут уже сильно не потрансформируешь, или придется расширять метод.
Если интересна реализация, ее можно найти в библиотеке CodeJam или в нашей ORM linq2db.
В защиту данного подхода скажу так, за 5 лет мне ни разу не пришлось писать новый Visitor.
Каждый, кто хоть раз делил на ноль в коде, знает это чувство: sin(x)/x при x=0 → 1 1/(x-2) при x=2 → ∞ или ошибка 0/0 → "undefined behaviour"
Мы привыкли прятать эти проблемы. А что если вместо этого их индексировать и сохранять?
Я назвал этот подход RICIS (Recursive Indexed Calculus of Identity and Singularity) и реализовал его на чистом C# с помощью System.Linq.Expressions.
Что получилось
C#
// Классика
sin(x)/x при x=0 → 1
// RICIS
sin(x)/x при x=0 → ∞_{(Sin(x)/x)}Не 1. Не NaN. А индексированная форма с сохранением исходного выражения.
Ещё примеры:
1/(x²-4) → Monolith { ∞_1 при x=2, ∞_1 при x=-2 }
(x²-25)/(x-5) → x+5 (но с сохранением истории)
1/(T-t) (модель blow-up в Навье-Стоксе) → ∞_{1/(T-t)}
Как это работает
Всё построено на дереве выражений и нескольких фазах:
Phase 1 (Clean First) — алгебраическое сокращение
Phase 2 (RICIS Transform) — поиск сингулярностей и создание ∞F / ∞{F/G}
Monolith — когда сингулярностей несколько
Никаких внешних CAS. Только System.Linq.Expressions.
А теперь главный вопрос
Это просто красивая обёртка над 0/0 или действительно новый способ работать с сингулярностями?
Я утверждаю, что RICIS позволяет решать задачи, где классическая математика и традиционные CAS пасуют или теряют информацию.
Но я могу и ошибаться.
Что думаете вы?
Это полезный инструмент или очередной математический велосипед?
Стоит ли сохранять структуру выражения при сингулярностях?
Есть ли смысл в индексированных бесконечностях?
Особенно интересно мнение тех, кто работает с моделированием, управлением, физикой и компьютерной алгеброй.
Ссылки на код и DOI будут в комментариях после первых обсуждений.
Деревья выражений в C# на примере нахождения производной (Expression Tree Visitor vs Pattern matching)