Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Sinclair, Вы писали:
S>>Но теперь, получается, надо заново применять вторую фазу, т.к. из той же таблицы фактов мы знаем, что {h} >= {0}, и мы должны свернуть выражение в {true}.
IT>Тебе скорее всего нужен метод, в котором ты сам будешь управлять необходимостью повторных трансформаций, т.к. это напрямую зависит от самих трансформаций.
В целом — да. Но пример из https://github.com/igor-tkachev/Linq.Expressions.Deconstruct/blob/master/Tests/Tests.cs#L156-L183, например, делает неполный фолдинг.
Например, i + 1 — 1 + 2 — i свёрнут не будет — нет правил для перегруппировки.
А как только мы введём такие правила, их придётся гонять по кругу.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, vmpire, Вы писали: V>А потом автор метода решил их поменять местами. При компиляции ничего не развалилось, при выполнении будет неправильный результат.
Да, в дорешарперные эпохи так и было. А сейчас всё делает IDE — вот и с классом также будет.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
V>>А потом автор метода решил их поменять местами. При компиляции ничего не развалилось, при выполнении будет неправильный результат. S>Да, в дорешарперные эпохи так и было. А сейчас всё делает IDE — вот и с классом также будет.
"Всё делает IDE" только если у вас маленькие простые проекты.
А если метод, который изменился, находится в библиотеке, которая в отдельном солюшне и используется другой командой, которая её только через месяц в виде пакета подтянет в свой проект, то начинаются очень интересные вещи.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Sinclair, Вы писали: S>>Да, давненько я не перечитывал Аппеля. S>Короче, теперь остро не хватает шейпов или вроде того. Потому что все правила с константами приходится повторять для всех числовых типов. S>Либо надо допиливать как-то так: S>
S>Multiply(Constant(Type t, var x) e, _) when t.IsNumeric && Convert.ToDouble(x) == 0.0 => e, // 0 * e => 0
S>
Не очень понятно, что у тебя за калькулятор, но если допустимо считать всё в Double, не проще изначально пробежаться по всему дереву и заменить все константы int/byte/... на соответствующие константы Double. И в оптимизаторе уже матчить всё на Double
Здравствуйте, Sinclair, Вы писали:
S>Ты имеешь в виду Add(Constant(T x), Constant(T y)) => Constant(new LambdaExpression(expr).Compile()())?
Что-то типа вот этого:
[Test]
public void ConstantFoldingExTest2()
{
Expression<Func<int,int>> f = i => i + 1 - 1 + 2 - i;
Expr Transform<T>(Expr ex, Func<T,T,T> multiply, Func<T,T,T> divide, Func<T,T,T> add, Func<T,T,T> subst)
{
return ex switch
{
Multiply(Constant(T x), Constant(T y)) => Constant(multiply(x, y)), // x * y => e
Divide (Constant(T x), Constant(T y)) => Constant(divide (x, y)), // x / y => e
Add (Constant(T x), Constant(T y)) => Constant(add (x, y)), // x + y => e
Subtract(Constant(T x), Constant(T y)) => Constant(subst (x, y)), // x - y => e
_ => null
};
};
var f1 = f.TransformEx(ex => ex switch
{
Multiply(Constant(0) e, _) => e, // 0 * e => 0
Multiply(_, Constant(0) e) => e, // e * 0 => 0
Multiply(Constant(1), var e) => e, // 1 * e => e
Multiply(var e, Constant(1)) => e, // e * 1 => e
Divide (Constant(0) e, _) => e, // 0 / e => 0
Divide (var e, Constant(1)) => e, // e / 1 => e
Add (Constant(0), var e) => e, // 0 + e => e
Add (var e, Constant(0)) => e, // e + 0 => e
Subtract(Constant(0), var e) => Negate(e), // 0 - e => -e
Subtract(var e, Constant(0)) => e, // e - 0 => e
_ when Transform<int> (ex, (x,y) => x * y, (x, y) => x / y, (x, y) => x + y, (x, y) => x - y) is Expr e => e,
_ when Transform<long> (ex, (x,y) => x * y, (x, y) => x / y, (x, y) => x + y, (x, y) => x - y) is Expr e => e,
_ when Transform<double>(ex, (x,y) => x * y, (x, y) => x / y, (x, y) => x + y, (x, y) => x - y) is Expr e => e,
_ => ex
});
Console.WriteLine(f);
Console.WriteLine(f1);
Assert.IsTrue(f1.EqualsTo(i => 2));
}
Только вместо лямбд, если уж заниматься этим серьёзно, следует залудить какой-нибудь интерфейс или базовый класс и сделать все арифметические операции в нём.
Либо сгенерировать всю эту хрень.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Jack128, Вы писали:
J>Не очень понятно, что у тебя за калькулятор, но если допустимо считать всё в Double, не проще изначально пробежаться по всему дереву и заменить все константы int/byte/... на соответствующие константы Double.
Всё — нет, недопустимо. Потом запаришься обратно приводить — потому что there is no built-in operator Add defined for the types [byte] and [Double].
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
J>>Не очень понятно, что у тебя за калькулятор, но если допустимо считать всё в Double, не проще изначально пробежаться по всему дереву и заменить все константы int/byte/... на соответствующие константы Double. S>Всё — нет, недопустимо. Потом запаришься обратно приводить — потому что there is no built-in operator Add defined for the types [byte] and [Double].
Кстати, эту идею стоит помусолить. Например, ввести наряду с Constant что-нибудь типа Numerical, которая будет такой же константой, но любого числового типа. Количество явно используемых констант используемых в паттернах, вроде 0,1 должно быть ограниченным и можно смело использовать целые числа.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Кстати, эту идею стоит помусолить. Например, ввести наряду с Constant что-нибудь типа Numerical, которая будет такой же константой, но любого числового типа. Количество явно используемых констант используемых в паттернах, вроде 0,1 должно быть ограниченным и можно смело использовать целые числа.
Даже ещё проще. Нужен ещё один деконструктор для Constant, который будет заточен исключительно на числа и сравнивать их значения с целым.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Sinclair, Вы писали:
S>А как только мы введём такие правила, их придётся гонять по кругу.
Вот вариант с частичным гонянием по кругу:
public static T Fold<T>(T expr)
where T : notnull, LambdaExpression
{
return expr.TransformEx<T>(FoldExpr);
}
public static Expression? Fold(Expression? expr)
{
return expr.TransformEx(FoldExpr);
}
static Expr FoldExpr(Expr ex)
{
return ex switch
{
Multiply(Constant(0) e, _) => e, // 0 * e => 0
Multiply(_, Constant(0) e) => e, // e * 0 => 0
Multiply(Constant(1), var e) => e, // 1 * e => e
Multiply(var e, Constant(1)) => e, // e * 1 => e
Divide (Constant(0) e, _) => e, // 0 / e => 0
Divide (var e, Constant(1)) => e, // e / 1 => e
Add (Constant(0), var e) => e, // 0 + e => e
Add (var e, Constant(0)) => e, // e + 0 => e
Subtract(Constant(0), var e) => Negate(e)!, // 0 - e => -e
Subtract(var e, Constant(0)) => e, // e - 0 => e
Multiply(Constant(int x), Constant(int y)) => Constant(x * y)!, // x * y => e
Divide (Constant(int x), Constant(int y)) => Constant(x / y)!, // x / y => e
Add (Constant(int x), Constant(int y)) => Constant(x + y)!, // x + y => e
Subtract(Constant(int x), Constant(int y)) => Constant(x - y)!, // x - y => e
Add (Add (Constant(int c1), var e), Constant(int c2)) => Fold(Add (Constant(c1 + c2), e)), // (c1 + e) + c2 = (c1 + e2) + e
Multiply(Multiply(Constant(int c1), var e), Constant(int c2)) => Fold(Multiply(Constant(c1 * c2), e)), // (c1 + e) + c2 = (c1 + e2) + e
Add (var e, Constant c) => Add (c, e), // e + c => c + e
Multiply(var e, Constant c) => Multiply(c, e), // e * c => c * e
Subtract(var e, Constant(int x)) => Add (Constant(-x), e), // e - c => (-c) + e
Add (var e1, Add (var e2, var e3)) => Fold(Add (Add (e1, e2), e3)), // e1 + (e2 + e3) = (e1 + e2) + e3
Multiply(var e1, Multiply(var e2, var e3)) => Fold(Multiply(Multiply(e1, e2), e3)), // e1 + (e2 + e3) = (e1 + e2) + e3
Subtract(Add(var e1, var e2), var e3) when e2 == e3 => e1, // (e1 + e2) - e2 => e1
_ => ex
};
}
Последнее правило тупо захардкожено, но можно либо расширить, либо сделать полную имплементацию аналогичную константам.
ЗЫ. Насчёт типов (шейпов) тоже есть идея как решить проблему.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, VladD2, Вы писали:
VD>>Причем новое лючевое слово все равно вводят. Можно было бы тупо писать "record" вместо "data class"
НС>А вместо data struct?
UPD. Правда, зачем мутабельными их сделали — это загадка, конечно. Как фсигда: вроде в целом неплохо, но обязательно что-то должно быть через очко. А у сектантов всегда есть оправдание любому говну.
Здравствуйте, AlexRK, Вы писали: ARK>UPD. Правда, зачем мутабельными их сделали — это загадка, конечно. Как фсигда: вроде в целом неплохо, но обязательно что-то должно быть через очко. А у сектантов всегда есть оправдание любому говну.
Почему мутабельными-то???
Records are immutable by default.
Как фсигда: разобраться времени нет, но надо обязательно покритиковать.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, AlexRK, Вы писали:
ARK>UPD. Правда, зачем мутабельными их сделали — это загадка, конечно. Как фсигда: вроде в целом неплохо, но обязательно что-то должно быть через очко. А у сектантов всегда есть оправдание любому говну.
Records are specialized classes
As I just covered, the record and the class variants of LoginResource are almost identical. The class definition is a semantically identical subset of the record. The record provides more, specialized, behavior.
Just so we’re on the same page, the following comparison is between a record, and a class that uses init instead of set for properties, as demonstrated earlier.
What’s the same?
Construction
Immutability
Copy semantics (records are classes under the hood)
What’s different?
Record equality is based on content. Class equality based on object identity.
Records provide a GetHashCode() implementation is that is based on record content.
Records provide an IEquatable<T> implementation. It uses the unique GetHashCode() behavior as the mechanism to provide the content-based equality semantic for records.
Record ToString() is overridden to print record content.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, AlexRK, Вы писали: ARK>Не надо никаких by default. Уже есть классы и структуры.
Отличная тактика. Если спорол чушь, то не надо признавать это — надо тут же спороть другую чушь, чтобы отвлечь собеседников от чуши предыдущей.
Продолжайте, мне сейчас курьер пакет попкорна должен привезти.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.