Реализация связанных расчетов
Аццко. 10 WTF/min, вспоминая единственно верную метрику
Как вы реализовываете подобного рода расчеты в своих проектах?пытаемся свести к следующему:
Expression<decimal> PartTotalповерх которого уже строится dataflow
{
get {return Quantity * Price;}
}
пытаемся свести к следующему:Похоже, вы идете тем же путем что и проекты:
Continuous LINQ/Bindable LINQ/Obtics/LiveLinq (см. codeplex.com)
То есть через парсинг expression tree.
Я отказался от этого еще на ранней стадии. Expression tree приносят слишком жесткие ограничения. В моем подходе все инструменты языка C# остаются доступными, я это отметил в документации:
Замечательной особенностью проекта Reactive Relation является то, что он не приносит собой никаких ограничений. Все инструменты языка C# остаются доступными – вычисления можно выделять в отдельные методы или классы. Фактически Reactive Relation реализует механизм выноса абстрактной логики пересчета с учетом абстрактной реляционной структуры данных. Указанный механизм реализован с помощь обычных средств языка без анализа дерева выражений (expression tree). Дерево выражений используется только в качестве ссылки на свойство (property expression).
http://propertyexpression.codeplex.com/Project/Download/File...
Аццко. 10 WTF/min, вспоминая единственно верную метрикуПо поводу синтаксиса см. 4-ый раздел документации проекта http://propertyexpression.codeplex.com/Project/Download/File...
Expression tree приносят слишком жесткие ограничениякакие, например?
какие, например?например, не работает обычный extract method
Я отказался от этого еще на ранней стадии.т.е. у тебя недо-dataflow, который не пересчитывает выходы при изменении входов?
binder[@Price] = it => it[@Quote][@PartSubtotal].Percent(it[@PartPercent]) + it[@Quote][@LaborSubtotal].Percent(it[@LaborPercent]) + it[@Fix];
дать возможность написать
engine.add("Price = (Quote.PartSubtotal * PartPercent + Quote.LaborSubtotal * (LaborPercent + Fix) / 100");
При регистрации формулы парсить и переводить уже в твой вид инъекцией кода. Таким образом, это хотя и не compile-time check, но startup check (не путать с runtime check).
Достоинство этого улучшения еще и в том, что все формулы одного flow можно собрать в одном очень компактном текстовом файле или фрагменте так, что вся логика будет легко видна и модифицируема.
т.е. у тебя недо-dataflow, который не пересчитывает выходы при изменении входов?по-видимому, не совсем понимаю тебя. Формулки, конечно, пересчитываются при изменении аргументов.
При регистрации формулы парсить и переводить уже в твой вид инъекцией кода. Таким образом, это хотя и не compile-time check, но startup check (не путать с runtime check).
8. ПП должен легко модифицироваться (из п.2а)
...
b) ПП должен быть достаточно формализовано (должна оставаться возможность автоматического\автоматизированного рефакторинга)
по-видимому, не совсем понимаю тебя. Формулки, конечно, пересчитываются при изменении аргументов.а как ты понимаешь, что ее надо пересчитать?
(должна оставаться возможность автоматического\автоматизированного рефакторинга)Find/Replace?
Какие еще рефакторинги формул, кроме переименования аргументов известны? Почему в Excel люди сидят и не страдают от отсутствия рефакторинга в этом месте?
При регистрации формулы парсить и переводить уже в твой вид инъекцией кода. Таким образом, это хотя и не compile-time check, но startup check (не путать с runtime check).т.е. ты предлагаешь свой язычок выражений? Таких решений уже много, например, вот http://flee.codeplex.com/
Но хочется оставаться "внутри" основного языка, в частности, C#.
все те же самые, которые доступны для кода (выделение метода, замена аргументов, перенос метода между классами и т.д.)
> Почему в Excel люди сидят и не страдают от отсутствия рефакторинга в этом месте?
во-первых, откуда известно что они не страдают?
во-вторых, а они не знают что можно по другому.
в-третьих, а что на Excel-е уже делают хорошие, стабильные, масштабируемые решения?
Find/Replace?На самом деле, от Excel-я есть существенное отличие. Rective Relation поддерживает проходы по связям один-ко-многим в обе стороны.
Какие еще рефакторинги формул, кроме переименования аргументов известны? Почему в Excel люди сидят и не страдают от отсутствия рефакторинга в этом месте?
> Почему в Excel люди сидят и не страдают от отсутствия рефакторинга в этом месте?да, под этим тоже подписываюсь. Вообще, аргумент Красина по поводу Excel-я какой-то странный... Могу объяснить это только тем, что человек далек от проблематики.
во-первых, откуда известно что они не страдают?
во-вторых, а они не знают что можно по другому.
в-третьих, а что на Excel-е уже делают хорошие, стабильные, масштабируемые решения?
а как ты понимаешь, что ее надо пересчитать?при регистрации формула говорит от чего она зависит.
при регистрации формула говорит от чего она зависит.т.е. это надо говорить руками?
т.е. это надо говорить руками?нет, конечно, иначе ради чего это все.
Все что надо сделать это написать формулы как тут
В сорцах проекта этот пример есть называется Demo. И unit-тесты с пересчетом есть.
например, не работает обычный extract methodчто значит не работает?
был код
static int X;
static Expression<Func<int>> X2
{
get
{
return => X + 5;
}
}
static Expression<Func<int>> Y
{
get
{
return => X + Call(X2) * 10;
}
}
static T Call<T>(Expression<Func<T>> t)
{
return t.Compile;
}
хотим вынести X2 * 10 как отдельный метод - жмем extract method
static Expression<Func<int>> Y
{
get
{
return => X + NewMethod;
}
}
private static int NewMethod
{
return Call(X2) * 10;
}
заменяем руками int на Expression<Func<int>>, а NewMethod на Call(NewMethod
можно чуток потрахаться со студией, и сделать чтобы это делалось по какой-нибудь кнопке
Все что надо сделать это написать формулы как тутт.е. все входные переменные должны прогонятся через шаманство it => it._
но это же легко забыть...
и фиг это автоматически проверишь...
с expression-ами можно требовать чтобы все члены были оформлены как expression-ы (допустим, за исключением специально помеченных)
скорость кстати тестил?
т.е. все входные переменные должны прогонятся через шаманство it => it._не забудешь, по другому ни интелесен ни компилятор не дадут.
но это же легко забыть...
и фиг это автоматически проверишь...
скорость кстати тестил?нет. Пефоманс я отложил, но там можно абсолютно все кешировать на уровне приложения, т.е. фактически все сведется к поиску по хештейблу и прямым вызовам методов.
X + Call(X2) * 10;вот, видишь, ты тут не можешь просто написать "X2" надо писать "Call(X2)"
заменяем руками int на Expression<Func<int>>, а NewMethod на Call(NewMethod
можно чуток потрахаться со студией, и сделать чтобы это делалось по какой-нибудь кнопке
Т.е. при extract method я должен держать в голове что это Expression-ы, и у них свои правила. А могу просто забыть сделать вот эти вставки. Все сбилдиться. А найти такую ошибку чрезвычайно сложно, поскольку надо пройти тест, где меняется X2.
B тут опять Call надо вставлять.
На первый взгляд, вот такое смешивание обычного кода и Expression<D> запутывает.
А когда эти экспрешены обрабатываются? Т.е. можешь описать вкраце общий цикл?
На первый взгляд, вот такое смешивание обычного кода и Expression<D> запутывает.добавление it => it., _ => _, Mult вместо * и т.д. - запутывает еще больше.
А когда эти экспрешены обрабатываются? Т.е. можешь описать вкраце общий цикл?окончательного решения еще нет, но что-то типа следующего
abstract class A
{
public int B {get;set;}
public int C {get;set;}
public abstract int D {get;}
}
public static class AHlp
{
public static Expression<int> D(this A a)
{
return => a.b + a.c;
}
}
void Main
{
//DataFlow создает наследника через emit, и через emit же добавляет метод D
var dataflow = DataFlow.Create<A,AHlp>
A a = dataflow.GetAs<A>
var value = dataflow.GetValueA a) => a.D);
var changeableValue = dataflow.GetChangeableValueA a) => a.D);
}
завязка на совпадение имен методов D?
добавление it => it., _ => _, Mult вместо * и т.д. - запутывает еще больше.с этим путаницы никакой нет, правило тупое и простое — вызов свойства идет через синтаксис "_=>_.", ни о чем другом думать не надо. Соблюдение этого правила компилятор и интелесенс проверяют.
завязка на совпадение имен методов D?в этом примере - да.
но в окончательном решении еще не знаю
в этом примере - датак же там нет контроля того, что метод AHlp.D принимает параметр типа A. Еще лучше, чтобы тип параметра выводился, а не указывался ручками
public static IPropertyValue<decimal> PartTotal(this IPropertyHolder<QuotePart> it)
{
return it._(_ => _.PartTotal);
}
public static void PartTotal(
this IBinder<IEmpty, QuotePart> binder,
Func<IPropertyBindContext<IEmpty, QuotePart, decimal>, IValue<decimal>> func)
{
binder._(_ => _.PartTotal, func);
}
Тогда формулы для примера из первого поста будут выглядеть следующим образом.
Теперь необычно смотрятся только методы вместо свойств и методы вместо операторов “+”, “*”. Но джависты же живут без этих вкусностей . На самом деле, идеальный синтаксис не является самоцелью, но об этом чуть позже.
Пока это в статусе эксперимента. Код находится в ветке DevBranches.
что значит не работает?
под термином "extract method" я в первую очередь имел ввиду обычный ручной прием выделения общего куска кода в отдельный метод.
Ты привел слишком простой пример — метод без параметров. Как у тебя будет работать вот такое введение метода NewMethod в формуле из моего примера
Price = NewMethod(this.Quote, this)
+ ...
decimal NewMethod(Qoute qoute, QuoteOtherExpense expense)
{
return qoute.PartSubtotal * expense.PartPercent / 100;
}
При анализе экспрешена, имхо, не отличишь параметр метода от локальной переменой. Или у тебя нельзя внутри метода использовать локальные переменные?
Но его замечание относительно типизированного датабайдинга, действительно, оказалось полезным, и проект стал лучше. За что -у большое спасибо!
Оставить комментарий
6yrop
Начну с примера. Пример из 2-x сущностей и 3-х формул будет не так ярко высвечивать проблему, поэтому приведу более объемный пример из 4-x сущностей и 10-и формул. Ссылка на pdf-файл с примером.Как вы реализовываете подобного рода расчеты в своих проектах?
Хочется просто писать формулы в произвольном порядке и больше ни о чем не думать.
В computer science это известно под термином dataflow.
Моя реализация примера. Ссылка на сам проект Reactive Relation.
Наконец-то, C# перестает уступать Excel-ю в плане выполнения расчетов .