В C# 4.0 у expression tree появились новые узлы, соотвествующие стейтментам, а не только выражениям, как раньше. Это раскрывает новые возможности. Но путь к этим возможностям тернист. Дело в том, что "цитирование" для них не работает. (далее примеры отсюда)
Т.е написать вот так все еще нельзя:
Expression<Func<int, List<int>>> getPrimes = to =>
{
var res = new List<int>();
for (int n = 2; n <= to; n++)
{
bool found = false;
for (int d = 2; d <= Math.Sqrt(n); d++)
{
if (n % d == 0)
{
found = true;
break;
}
}
if (!found)
res.Add(n);
}
return res;
};
Обходной путь — составлять такие деревья вручную, что будет выглядеть так:
Скрытый текст
var to = Expression.Parameter(typeof(int), "to");
var res = Expression.Variable(typeof(List<int>), "res");
var n = Expression.Variable(typeof(int), "n");
var found = Expression.Variable(typeof(bool), "found");
var d = Expression.Variable(typeof(int), "d");
var breakOuter = Expression.Label();
var breakInner = Expression.Label();
var getPrimes =
Expression.Lambda<Func<int, List<int>>>(
// {
Expression.Block(
// List<int> res;new [] { res },
// res = new List<int>();
Expression.Assign(
res,
Expression.New(typeof(List<int>))
),
// {
Expression.Block(
// int n;new [] { n },
// n = 2;
Expression.Assign(
n,
Expression.Constant(2)
),
// while (true)
Expression.Loop(
// {
Expression.Block(
// if
Expression.IfThen(
// (!
Expression.Not(
// (n <= to)
Expression.LessThanOrEqual(
n,
to
)
// )
),
// break;
Expression.Break(breakOuter)
),
// {
Expression.Block(
// bool found;new[] { found },
// found = false;
Expression.Assign(
found,
Expression.Constant(false)
),
// {
Expression.Block(
// int d;new [] { d },
// d = 2;
Expression.Assign(
d,
Expression.Constant(2)
),
// while (true)
Expression.Loop(
// {
Expression.Block(
// if
Expression.IfThen(
// (!
Expression.Not(
// d <= Math.Sqrt(n)
Expression.LessThanOrEqual(
d,
Expression.Convert(
Expression.Call(
null,
typeof(Math).GetMethod("Sqrt"),
Expression.Convert(
n,
typeof(double)
)
),
typeof(int)
)
)
// )
),
// break;
Expression.Break(breakInner)
),
// {
Expression.Block(
// if (n % d == 0)
Expression.IfThen(
Expression.Equal(
Expression.Modulo(
n,
d
),
Expression.Constant(0)
),
// {
Expression.Block(
// found = true;
Expression.Assign(
found,
Expression.Constant(true)
),
// break;
Expression.Break(breakInner)
// }
)
)
// }
),
// d++;
Expression.PostIncrementAssign(d)
// }
),
breakInner
)
),
// if
Expression.IfThen(
// (!found)
Expression.Not(found),
// res.Add(n);
Expression.Call(
res,
typeof(List<int>).GetMethod("Add"),
n
)
)
),
// n++;
Expression.PostIncrementAssign(n)
// }
),
breakOuter
)
),
res
),
to
// }
)
Очевидно, что писать такие вещи — не самое приятное занятие, которое можно себе найти. Поэтому, я сделал для себя встроенный язык их построения, который пока называется Crutch — название рабочее, возможно, что позже я придумаю другое, похуже. Вернее, пока это только пруф-оф-концепт. Вот пример:
//Создаем ф-ю. Захватываем переменную to и конструируем списокvar getPrimes = M.Fun(M.Let(() => to, () => new List<int>()).In( //область видимости - выражение, возвращающее значение.
(max, res) => //именуем их для этой области видимости.
M.For(2.Const(), max, n => //цикл создает для своего скоупа переменную n, меняющуюся от 2.Const() до max.
M.Let(() => false).In( //создаем и переменную found.
//Apply подставляет выражения в лямбду. В данном случае n вместо x. В общем, Apply - он и есть Apply.
found => M.Let(n.Apply(x => (int)Math.Sqrt(x))).In(
max2 => M.Block( //блок - выражение типа Expr<Void> - ничего не возвращает.
M.For(2.Const(), max2, (d, breakLoop) => //этот цикл подает с свою область видимости ф-ю breakLoop.
M.If(n.Apply((a, b) => a % b == 0, d)) //условие If Then - ничего не возвращает.
.Then(found.Assign(true), breakLoop())), //присваиваем значение, выходим из цикла.
M.If(found.Apply(p => !p))
.Then(res.Apply((l,e) => l.Add(e),n))))))
.Result(res))); //блок, который ничего не возвращает можно сделать возвращающим значение с помощью Result.
Страшно, как атомная война, но есть некоторые явные преимущества перед использованием ручного построения.
Краткость (относительная).
Проверки компилятора.
Автодополнение.
Вывод типов.
Так что, если кому интересно — я причешу код и выложу. До более-менее работающего состояния я все равно планирую это доводить. В следующих сериях, помимо конструирования деревьев будет и разбор.
Подозреваю, кстати, что подобное кто-то уже делал, но я не нашел.
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re: [ANN] Crutch: недометапрограммирование в C# 4.0
K>//Создаем ф-ю. Захватываем переменную to и конструируем список
K>var getPrimes = M.Fun(M.Let(() => to, () => new List<int>()).In( //область видимости - выражение, возвращающее значение.
K> (max, res) => //именуем их для этой области видимости.
K> M.For(2.Const(), max, n => //цикл создает для своего скоупа переменную n, меняющуюся от 2.Const() до max.
K> M.Let(() => false).In( //создаем и переменную found.
K> //Apply подставляет выражения в лямбду. В данном случае n вместо x. В общем, Apply - он и есть Apply.
K> found => M.Let(n.Apply(x => (int)Math.Sqrt(x))).In(
K> max2 => M.Block( //блок - выражение типа Expr<Void> - ничего не возвращает.
K> M.For(2.Const(), max2, (d, breakLoop) => //этот цикл подает с свою область видимости ф-ю breakLoop.
K> M.If(n.Apply((a, b) => a % b == 0, d)) //условие If Then - ничего не возвращает.
K> .Then(found.Assign(true), breakLoop())), //присваиваем значение, выходим из цикла.
K> M.If(found.Apply(p => !p))
K> .Then(res.Apply((l,e) => l.Add(e),n))))))
K> .Result(res))); //блок, который ничего не возвращает можно сделать возвращающим значение с помощью Result.
K>
Это конечно лучше чем конструировать Expression вручную, но уж лучше на мой взгляд использовать F#.
Можно поинтересоваться Вы для каких целей используете метапрограммирование?
Re[2]: [ANN] Crutch: недометапрограммирование в C# 4.0
От:
Аноним
Дата:
08.07.10 09:36
Оценка:
Здравствуйте, Visor2004, Вы писали:
V>А зачем это все?
Поддерживаю вопрос — для чего все это?
Re: [ANN] Crutch: недометапрограммирование в C# 4.0
K>Так что, если кому интересно — я причешу код и выложу. До более-менее работающего состояния я все равно планирую это доводить. В следующих сериях, помимо конструирования деревьев будет и разбор.
А вот и код. FusionArray — это пример использования. Склеивание цепочки мапов в один цикл.
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[2]: [ANN] Crutch: недометапрограммирование в C# 4.0
Здравствуйте, Visor2004, Вы писали:
V>А зачем это все?
Ну а зачем вообще метапрограммирование? Для борьбы с boilerplate code в частности и сокращения объема кода вообще. И для оптимизаций. В данном случае и оптимизаций с учетом информации, получаемой во время выполнения.
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[2]: [ANN] Crutch: недометапрограммирование в C# 4.0
Здравствуйте, achmed, Вы писали:
A>Это конечно лучше чем конструировать Expression вручную, но уж лучше на мой взгляд использовать F#.
F#, конечно, во многих случаях лучше, но не настолько, чтобы я стал его проталкивать и популяризировать на работе. И не настолько интересен, чтобы играться с ним в свободное время.
Самые серьезные, на мой взгляд, проблемы у него вообще с С# общие.
Кроме того, я ML недолюбливаю, хотя по этим моим Let In и Fun выше такого и не скажешь. Они там не от хорошей жизни (как и в ML, впрочем).
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[3]: [ANN] Crutch: недометапрограммирование в C# 4.0
Здравствуйте, Klapaucius, Вы писали:
K>Ну а зачем вообще метапрограммирование? Для борьбы с boilerplate code в частности и сокращения объема кода вообще. И для оптимизаций. В данном случае и оптимизаций с учетом информации, получаемой во время выполнения.
Можете привести несколько (3-х вполне будет достаточно) более конкретных примеров из жизни в которых ваше решение поможет сократить кол-во кода и будет оптимальнее чем аналогичное решение но реализованное без Expression?
Мы были здесь. Но пора идти дальше. (с) Дуглас Коупленд, Рабы "Микрософт"
Re[4]: [ANN] Crutch: недометапрограммирование в C# 4.0
C>Можете привести несколько (3-х вполне будет достаточно) более конкретных примеров из жизни в которых ваше решение поможет сократить кол-во кода и будет оптимальнее чем аналогичное решение но реализованное без Expression?
Выше была ссылка на fusion array, наколько я понял аналог этого
Здравствуйте, Klapaucius, Вы писали:
K>F#, конечно, во многих случаях лучше, но не настолько, чтобы я стал его проталкивать и популяризировать на работе. И не настолько интересен, чтобы играться с ним в свободное время. K>Самые серьезные, на мой взгляд, проблемы у него вообще с С# общие.
интересно какие? K>Кроме того, я ML недолюбливаю, хотя по этим моим Let In и Fun выше такого и не скажешь. Они там не от хорошей жизни (как и в ML, впрочем).
чем не угодили let in в ML ?
Re[4]: [ANN] Crutch: недометапрограммирование в C# 4.0
, например.
K>>Кроме того, я ML недолюбливаю, хотя по этим моим Let In и Fun выше такого и не скажешь. Они там не от хорошей жизни (как и в ML, впрочем). A>чем не угодили let in в ML ?
Я в принципе, ничего не имею против let in как выражения и допускаю, что для него есть своя область применения, если, конечно, меня не заставляют пользоваться им постоянно, вместо более вменяемой альтернативы where.
Дело в том, что объявление перед использованием заставляет при чтении кода продираться через массу второстепенных деталей, которые вообще можно было бы и не читать, прежде чем удается добраться до главного.
За деревьями не видно леса:
let лес =
let лиственноеДерево = "береза"in
let хвойноеДерево = "сосна"in
лиственноеДерево + хвойноеДерево;;
Здравствуйте, Klapaucius, Вы писали: K>Зачем все эти let rec
Если я правильно помню, то let rec — это первый костыль(тм) типизированного лямбда-исчисления (в котором не бывает знакомых по нетипизированному Y-комбинаторов), который, видимо, в ML решили подчеркнуть. Ленивость тут ни при чем. Не?
Re[6]: [ANN] Crutch: недометапрограммирование в C# 4.0
Здравствуйте, Mr.Cat, Вы писали:
MC>Если я правильно помню, то let rec — это первый костыль(тм) типизированного лямбда-исчисления (в котором не бывает знакомых по нетипизированному Y-комбинаторов), который, видимо, в ML решили подчеркнуть. Ленивость тут ни при чем. Не?
Не совсем.
--костыль костылем, но с типами это обходитсяnewtype Rec a = Rec { unrec :: Rec a -> a }
y f = (\x -> f ((unrec x) x)) (Rec (\x -> f ((unrec x) x)))
fact = y $ \fact' n ->
case n of
0 -> 1
otherwise -> n * fact' (n - 1)
--fact 2
--2
Работает.
Другое дело тут:
type 'a Rec = Rec of ('a Rec -> 'a)
let unrec = fun (Rec x) -> x
let y f = (fun x -> f ((unrec x) x)) (Rec (fun x -> f ((unrec x) x)))
let fact = y (fun fact' n ->
match n with
| 0 -> 1
| _ -> n * fact' (n - 1))
(*>fact 2
ждите ответа-ждите ответа-ждите ответа*)
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re: [ANN] Crutch: недометапрограммирование в C# 4.0
Здравствуйте, Sinclair, Вы писали:
S>Мнэ-э-э, вот тут смотрел?
Конечно. Эта ссылка, кстати сказать, есть в моем посте, на который вы сейчас отвечаете. Там описано применение Expression.Invoke, которое и я использую для типизированного вызова методов, например. Ничего из того, что мне приходится тут самому делать: типизированного дерева, вывода типов, объявления переменных в области видимости вроде:
M.Let(() => new List<int>()).In(
list =>
//используем инициализированную переменную.
)
там нет. В комментариях тоже, на первый взгляд, и по ссылкам нет. Может я был недостаточно внимателен?
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re: [ANN] Crutch: недометапрограммирование в C# 4.0
Здравствуйте, Klapaucius, Вы писали:
K>Очевидно, что писать такие вещи — не самое приятное занятие, которое можно себе найти. Поэтому, я сделал для себя встроенный язык их построения, который пока называется Crutch — название рабочее, возможно, что позже я придумаю другое, похуже. Вернее, пока это только пруф-оф-концепт.