Хочется добавить поддержку формул в наше spreadsheet-based приложение, как бы это поэлегантнее и с наименьшими усилиями сделать? Известны имена колонок и их типы, например:
С этим мы бы еще могли жить, пробуя компилировать сгенерированный код со всеми типами результатов, которые поддерживаются нашим приложением (к счастью, их всего пара десятков), но нам всё равно придётся убирать параметры-колонки, не участвующие в выражении, по соображениям производительности (накладно доставать все данные, кол-во колонок может достигать десятков тысяч), так что я начинаю задумыватся о предварительном парсинге выражения... хотя, возможно, будет проще анализировать скомпилированный код рефлекшоном и потом компилировать выражение второй раз, убрав неиспользуемые параметры.
Есть ли какие-то другие идеи? Задача выглядит довольно общей, и я уверен, что кто-то уже сталкивался с ней ранее.
P.S. FW 3.5
Re: Вывод типа результата по выражению
От:
Аноним
Дата:
09.07.09 11:52
Оценка:
Здравствуйте, Andy77, Вы писали:
A>Хочется добавить поддержку формул в наше spreadsheet-based приложение, как бы это поэлегантнее и с наименьшими усилиями сделать? Известны имена колонок и их типы, например:
A>owner — string A>row — int64 A>col — int64 A>volume — double
Не вполне понял задачу. Если типы колонок заранее известны — это больше похоже на таблицу в базе данных, а не на электронную таблицу. Spreadsheet, как правило, подразумевает, что тип значения, хранящегося в ячейке, будет меняться. И тогда само собой напрашивается решение — использовать в качестве возвращаемого типа object.
Далее. Не понятно, к какому именно значению идет обращение по имени, если имя присвоено всему столбцу? Вероятно, подразумевается существование понятия "текущая строка"? Как вы тогда обращаетесь к данным, находящимся не в текущей строке?
Мне кажется, что (в предположении, что я правильно понял задачу) лучшим решением будет следующее:
Объект SpreadSheetRowObjectType должен иметь по свойству на каждую колонку, его тоже можно генерировать на лету для каждого листа.
Такое решение обладает рядом преимуществ:
1. Явно отличаются "переменные листа" от всех других имен (переменных, констант и пр.). Такой синтаксис — более прозрачен.
2. Гибкость. Наверное, со временем вы захотите использовать переменные с других листов, или из другой строки того же листа — у вас уже будет готовая синтаксическая конструкция (в дополнение к предопределенному объекту spreadSheetRow вы введете nextRow, sheets[] и т.п.
Здравствуйте, Аноним, Вы писали:
А>Не вполне понял задачу. Если типы колонок заранее известны — это больше похоже на таблицу в базе данных, а не на электронную таблицу. Spreadsheet, как правило, подразумевает, что тип значения, хранящегося в ячейке, будет меняться. И тогда само собой напрашивается решение — использовать в качестве возвращаемого типа object.
Да, это похоже на таблицу в базе данных, но на самом деле это строго типизированный spreadsheet, что даёт кучу преимуществ.
А>Далее. Не понятно, к какому именно значению идет обращение по имени, если имя присвоено всему столбцу? Вероятно, подразумевается существование понятия "текущая строка"? Как вы тогда обращаетесь к данным, находящимся не в текущей строке?
Выражение будет вычисляться для каждой строки отдельно (функциональность будет называться "add dependent column").
А>Мне кажется, что (в предположении, что я правильно понял задачу) лучшим решением будет следующее:
А>Предлагаю вместо А>
Слишком длинно, на мой вкус.
А>Объект SpreadSheetRowObjectType должен иметь по свойству на каждую колонку, его тоже можно генерировать на лету для каждого листа. А>Такое решение обладает рядом преимуществ: А>1. Явно отличаются "переменные листа" от всех других имен (переменных, констант и пр.). Такой синтаксис — более прозрачен. А>2. Гибкость. Наверное, со временем вы захотите использовать переменные с других листов, или из другой строки того же листа — у вас уже будет готовая синтаксическая конструкция (в дополнение к предопределенному объекту spreadSheetRow вы введете nextRow, sheets[] и т.п.
В 99% случаев ни переменные, ни константы, ни доступ к данным, находящимся извне данной строки не нужны; для таких случаев у нас есть полноценный скриптинг, позволяющий использовать всю объектную модель приложения. Сейчас меня интересует именно вычисления значения на основании данной строки, хочется сделать это как можно проще для пользователя.
В принципе, идея со свойствами неплоха, нужно будет её обмозговать. Действительно, почему бы не генерировать такой класс —
class RowBasedCalculator
{
public Row _r;
int row { get { return (int)_r["row"]; } }
int col { get { return (int)_r["col"]; } }
double volume { get { return (int)_r["col"]; } }
string owner { get { return (string)_r["owner"]; } }
// выделенный кусок - ввод пользователяpublic Func<double> calc = () => volume * row;
}
Правда, изначальный вопрос — как определять тип результата по выражению — так и остался нерешенным. Хотя перебор поддерживаемых типов колонок и попытка скомпилировать код с каждым из них, в принципе, сработает.
Здравствуйте, Andy77, Вы писали:
A>Хочется добавить поддержку формул в наше spreadsheet-based приложение, как бы это поэлегантнее и с наименьшими усилиями сделать?
Уверен, что написать граматику+парсер будет дороже (и по стоимости реализации и по стоимости выполнения)? На сколько "навороченным" может быть систаксис формул?
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Уверен, что написать граматику+парсер будет дороже (и по стоимости реализации и по стоимости выполнения)? На сколько "навороченным" может быть систаксис формул?
Ага, своя грамматика и парсер у нас была в старых версиях, сейчас же хочется просто воспользоваться возможностями, предоставляемыми .NET (писать формулы на C#). Единственная проблема была в том, что неохота заставлять пользователя указывать тип результата; в идеале пользователь вводит просто "volume * concentration", а программа сама определяет, что раз volume и concentration являются double, то и тип колонки-результата тоже будет double. Этого, в принципе, можно достичь, попробовав скомпилировать формулу со всеми поддерживаемыми типами колонок, так что, в принципе, проблема решена — не самым элегантным образом, но работать будет.
Здравствуйте, Andy77, Вы писали:
A>Единственная проблема была в том, что неохота заставлять пользователя указывать тип результата;
Хорошо, тогда можно попробовать компилить не "код на шарпе", а "код на питоне", например, то есть на языке, в котором можно пользоваться статически не типизированным кодом (DLR).
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Хорошо, тогда можно попробовать компилить не "код на шарпе", а "код на питоне", например, то есть на языке, в котором можно пользоваться статически не типизированным кодом (DLR).
А чем нам это поможет? Я как раз за строгую типизацию — тем более, что все равно нужно создавать колонку правильного типа.
Здравствуйте, Andy77, Вы писали:
_FR>>Хорошо, тогда можно попробовать компилить не "код на шарпе", а "код на питоне", например, то есть на языке, в котором можно пользоваться статически не типизированным кодом (DLR).
A>А чем нам это поможет? Я как раз за строгую типизацию — тем более, что все равно нужно создавать колонку правильного типа.
А как вы собираетесь защититься от неправильных формул
Тип object в качестве колонки нашим приложением не поддерживается, поэтому такая формула не скомпилируется
_FR>Может просто, заключать всю формулу в Convert.ToString(…) и считать результат строкой?
Нет, так не пойдет, результат нам нужен правильного типа.
Здравствуйте, Andy77, Вы писали:
A>Тип object в качестве колонки нашим приложением не поддерживается, поэтому такая формула не скомпилируется
Как так не скомпилируется? Чем не скомпилируется? Почему не скомпилируется? Если я правильно понял workflow, то компирировать вы собираетесь компилятором шарпа, а он такой код скомпилирует. Если же у вас есть свой компилятор, то проблема оптеределия типа выражения стоять не должна
_FR>>Может просто, заключать всю формулу в Convert.ToString(…) и считать результат строкой? A>Нет, так не пойдет, результат нам нужен правильного типа.
А что есть "правильный тип"? Как и кто это будет\должен проверять? В общем, что-то не до конца получается описана задача
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, Andy77, Вы писали:
A>Если бы тип возвращаемого результата был заранее известен, то проблема решалась бы очень просто генерацией и компиляцией следущего кода: A>Задача выглядит довольно общей, и я уверен, что кто-то уже сталкивался с ней ранее.
Хм. А типы исходных колонок уже известны?
Тогда можно попробовать скомпилировать вот такой вот код:
class Program
{
static void Main(string[] args)
{
Expression<Action<double, int>> calc = (conc, volume) => Test(conc * volume);
var mce = calc.Body as MethodCallExpression;
Console.WriteLine(mce.Arguments[0].Type);
}
private static void Test<T>(T p)
{
throw new NotImplementedException("This is a placeholder method only");
}
}
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.