Здравствуйте, Sinclair, Вы писали:
S>Есть искушение, к примеру, задавить на первом проходе все Subtract в Add(a, Negate(b)), чтобы сократить объём правил. S>И так же LessThan в Not(GreaterThanOrEqual).
Можно еще попробовать сделать реассоциацию выражений, в приниципе, ты это и предложил только еще с перестановкой, чтобы b + a ==> a + b и b < a ==> в a > b, чтобы сократить количество правил, чтобы вот такое ((a + c1) + b) + c2 превратить в такое c2 + c1 + a + b, что обнаружится при constant folding
S>Кстати, чего я не понял — зачем такой огромный switch в TransformInternal. S>Там же можно просто по типам свитчится, а не по NodeType.
Наверное можно было бы, но свитч по типам он последовательный, а свитч на enum по таблице переходов работает, там 22-23 ветки, хотя да код подсократился бы
Здравствуйте, Ночной Смотрящий, Вы писали:
R>>Хотелось бы, чтобы смелее смотрели на то, что вокруг сделано и как.
НС>А с чего ты взял что они не смотрят? Language design process у шарпа сейчас более менее открытый.
Ключевое слово смелее. А так да, хорошо что не стоят на месте
НС>А делать из него экспериментальный никто не будет, не то назначение у языка.
Сейчас они и так экпериментируют, те же шейпы взять
Здравствуйте, rameel, Вы писали:
R>Можно еще попробовать сделать реассоциацию выражений, в приниципе, ты это и предложил только еще с перестановкой, чтобы b + a ==> a + b и b < a ==> в a > b, чтобы сократить количество правил, чтобы вот такое ((a + c1) + b) + c2 превратить в такое c2 + c1 + a + b, что обнаружится при constant folding
Вот тут я пока не очень понимаю, как это сделать. Ну, то есть я могу выделять константы — правило про (e1 + c1) + (e2 + c2) => (e1 + e2) + {c1+c2} и подобные есть.
А вот для ((a + c1) + b) + c2 правила нету, и такие выражения остаются как есть. S>>Кстати, чего я не понял — зачем такой огромный switch в TransformInternal. S>>Там же можно просто по типам свитчится, а не по NodeType.
R>Наверное можно было бы, но свитч по типам он последовательный, а свитч на enum по таблице переходов работает, там 22-23 ветки, хотя да код подсократился бы
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, rameel, Вы писали:
НС>>А делать из него экспериментальный никто не будет, не то назначение у языка. R>Сейчас они и так экпериментируют, те же шейпы взять
Те же шейпы из релиза убрали, потому что дизайн их недостаточно готов для релиза. В этом ключевое отличие от Немерли, где каждый тащит в язык что хочет.
R1: c1 + c2 = c1+c2
R3: c1 * c2 = c1*c2
R5: c1 - c2 = c1-c2
R2: t + c = c + t
R4: t * c = c * t
R6: t - c = (-c) + t
R7: t1 + (t2 + t3) = (t1 + t2) + t3
R8: t1 * (t2 * t3) = (t1 * t2) * t3
R9: (c1 + t) + c2 = (c1+c2) + t
R10: (c1 * t) * c2 = (c1*c2) * t
Выражения слева матчатся, соответственно, правилами R2 и R4; константы уезжают налево: c2 ~ (c1 ~ t); дальше срабатывают правила R7 и R8, получая как раз (c2 ~ c1) ~ t, оттуда по правилам R1/R3 получаем финальное выражение из константы и нашего терма.
Хорошо, что такие вещи вылавливает сразу компилятор C#.
R>Google book: Advanced Compiler Design Implementation
Да, давненько я не перечитывал Аппеля.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали: S>Да, давненько я не перечитывал Аппеля.
Короче, теперь остро не хватает шейпов или вроде того. Потому что все правила с константами приходится повторять для всех числовых типов.
Либо надо допиливать как-то так:
Multiply(Constant(Type t, var x) e, _) when t.IsNumeric && Convert.ToDouble(x) == 0.0 => e, // 0 * e => 0
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали: R>>Google book: Advanced Compiler Design Implementation S>Да, давненько я не перечитывал Аппеля.
В общем, с range checking как-то пока не выходит.
Проблемы с выражениями типа j-1 < w, если известно, что j < w.
Арифметические реассоциации превращают это дело в -1 + j < w
И вот тут начинаются приключения, потому что я хочу одновременно
оставить слева только одну j, чтобы увидеть там ограниченный параметр, и попробовать подставить в это выражение maxJ. То есть нужны правила, приводящие то, что сверху, к j < (1 + w).
Уже не вполне понятно, как именно. Правила "параметры едут налево" приведут к j + -w < 1. Правила "константы едут направо" могут помочь, но неясно, как это всё сработает в более общем случае.
когда я подставляю туда maxJ, то получаем (w-1)<(1+w). Арифметические реассоциации трансформируют его в
-1 + w < 1 + w
И вот дальше непонятно, что c этим делать — если утаскивать константы направо, то получим w < (2 + w) (опять же, после реассоциации и constant folding в правой части).
Надо как-то из этого сделать true.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>В общем, с range checking как-то пока не выходит. S>Проблемы с выражениями типа j-1 < w, если известно, что j < w. S>Арифметические реассоциации превращают это дело в -1 + j < w S>Надо как-то из этого сделать true.
Можно хранить свойства отношений больше/меньше/равно для каждой переменной. То есть если известно, что j < w то для j заводится информация об этом в отношении w также как для w в отношении j. Далее, если отбросить переполнение типов, то исходить от истинности условий, что j — c < w равно как и j < w + c. Остается прописать правила истинности и проверять это отдельным проходом.
Здравствуйте, VladD2, Вы писали:
VD>Им надо меня с Хардкейсом на работу взять и дать нам человек 5 в подчинение. Будут тогда супер-макросы через год-два.
Боюсь вы опять улетите в дальние дали. Не надо. Пусть C# живёт.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, vmpire, Вы писали:
V>Про функциональные языки не скажу, но что будет если поменять порядок объявлений в классе? Всё же поедет. А если типы данных поменянных членов совпадут, то ошибка ещё и не сразу вылезет.
Что будет если поменять порядок объявления параметров в методе?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, rameel, Вы писали:
R>Можно хранить свойства отношений больше/меньше/равно для каждой переменной. То есть если известно, что j < w то для j заводится информация об этом в отношении w также как для w в отношении j.
Это уже сделано. R>Далее, если отбросить переполнение типов, то исходить от истинности условий, что j — c < w равно как и j < w + c. Остается прописать правила истинности и проверять это отдельным проходом.
Вот непонятно, что значит "исходить из истинности условий". И что такое "правила истинности". В моём воображении всё работает примерно так: есть правила редукции. В конце концов мы должны прийти к выражению из одних констант, которое свернётся в true. Для range мы подставляем в сравнение минимальное значение — если даже оно заведомо не меньше правой части (редукция в false), то и всё выражение false.
Если при подстановке максимального значения получаем true, то и всё выражение true. Если же нам не удаётся редуцировать границы диапазона таким образом, то, значит, придётся честно вычислять выражение в рантайме.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Итого — либо мы гоняем цикл
Изначально этот код был выдернут из linq2db, где он работал в обратном направлении. Можно сделать ещё один метод (или два) для любых других направлений. Всё в ваших руках. Могу дать доступ к репе.
ЗЫ. Код был написан на коленке за недельку в качестве изучения новых возможностей ПМ. Так что строго не судите.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Sinclair, Вы писали:
S>Но теперь, получается, надо заново применять вторую фазу, т.к. из той же таблицы фактов мы знаем, что {h} >= {0}, и мы должны свернуть выражение в {true}.
Тебе скорее всего нужен метод, в котором ты сам будешь управлять необходимостью повторных трансформаций, т.к. это напрямую зависит от самих трансформаций.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, rameel, Вы писали: S>Тут меня смущает потенциальная непроизводительность — эти трансформации работают в основном на уровне листьев, поэтому перетрансформация отдельной веточки интуитивно дешевле, чем полный проход по дереву.
В linq2db работает версия, которая позволяет останавливать трансформацию вообще. Т.е. в таких вот непонятных случаях мы текущую трансформацию останавливаем, но перед этим запускаем новую с нужного места.
S>С другой стороны, может тут экономиS>Кстати, чего я не понял — зачем такой огромный switch в TransformInternal.
Стырено из linq2db.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Sinclair, Вы писали:
S>Короче, теперь остро не хватает шейпов или вроде того. Потому что все правила с константами приходится повторять для всех числовых типов.
Сделай gemeric метод для трансформаций с этими типами и гоняй по кругу. Ну это так, чтоб совсем не мучиться
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
V>>Про функциональные языки не скажу, но что будет если поменять порядок объявлений в классе? Всё же поедет. А если типы данных поменянных членов совпадут, то ошибка ещё и не сразу вылезет.
IT>Что будет если поменять порядок объявления параметров в методе?
Очевидно, будет ошибка. Представьте, например, метод с двумя строковыми параметрами: строка формата и форматируемое значение.
А потои автор метода решил их поменять местами. При компиляции ничего не развалилось, при выполнении будет неправильный результат.
Здравствуйте, IT, Вы писали: IT>Сделай gemeric метод для трансформаций с этими типами и гоняй по кругу. Ну это так, чтоб совсем не мучиться
Ну так возможность сделать генерик-метод как раз ограничена отсутствием шейпов
Мне же надо что-то вроде
Мысль, которую думал ночью: сделать CalculateBinary(ExpressionType type, MethodInfo method, Constant left, Constant right), в который и делегировать все Binary операции c константами.
Там же надо аккуратно — если есть метод, то вызвать его (все же знают, что "Hello, "+ "world" — это ExpressionType.Add c Method == string.Concat?).
Если нет метода — то уже свитчиться по типу выражения и вычислять его. Если пренебречь checked, то можно просто конвертировать обе константы в дабл, а потом приводить результат обратно к node.Type при помощи Convert.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, IT, Вы писали:
IT>В linq2db работает версия, которая позволяет останавливать трансформацию вообще. Т.е. в таких вот непонятных случаях мы текущую трансформацию останавливаем, но перед этим запускаем новую с нужного места.
Ну, мне в итоге пришлось таки сделать трансформацию в цикле. Иначе приходится писать в разы больше правил для обеспечения протаскивания трансформации вверх.
И трансформацию отдельных подветочек тоже пришлось сделать — лобовой подход с range checking не проходит. Он навешивает на каждое сравнение параметра два дополнительных сравнения, из которых не более одного можно свернуть в константу, в итоге дерево неконтролируемо растёт.
Так что окончательный вариант проверялки диапазона выглядит так:
Expression CheckRange(Expression node, ParameterExpression left, Expression right)
{
var range = variableRanges[left];
if (range.maxVal != null && Arithmetic.Simplify(MakeBinary(node.NodeType, range.maxVal, right), variableRanges) is ConstantExpression rangeMatch1 && (bool)rangeMatch1.Value == true)
return rangeMatch1;
if (range.minVal != null && Arithmetic.Simplify(MakeBinary(node.NodeType, range.minVal, right), variableRanges) is ConstantExpression rangeMatch2 && (bool)rangeMatch2.Value == false)
return rangeMatch2;
return node;
}
Вызывается он вот так:
LessThan(Parameter pe, var e) when variableRanges.ContainsKey(pe) => CheckRange(expr, pe, e),
LessThanOrEqual(Parameter pe, var e) when variableRanges.ContainsKey(pe) => CheckRange(expr, pe, e),
S>Кстати, чего я не понял — зачем такой огромный switch в TransformInternal. IT>Стырено из linq2db.
А, ок.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.