foreach (char c in range<char>('a', 'z'))
Console.Write(c);
foreach (int n in range<int>(1, 10))
Console.Write(n);
Генератор обобщённого типа.
Код:
Примерный код самого генератора:
Код:
IEnumerable<T> range<T>(T first, T last) where T : struct, IComparable<T>
{
for (T result = first; result.CompareTo(last) < 0; [COLOR="Red"]++result[/COLOR]) // error
yield return result;
}
{
for (T result = first; result.CompareTo(last) < 0; [COLOR="Red"]++result[/COLOR]) // error
yield return result;
}
Не компилируется, выдаёт ошибку:
Цитата:
Operator '++' cannot be applied to operand of type 'T'
Что указать в where? Возможно ли это вообще?
Выкрутиться можно, например, как сделано здесь. Но это весьма тяжеловесно и громоздко.
Я всё больше разочаровываюсь в Шарпе :(.
А как с этим в Nemerle?..
Код:
foreach (char c in range<char>('a', 'z', c => ++c))
Console.Write(c);
foreach (int n in range<int>(1, 10, n => ++n))
Console.Write(n);
Console.Write(c);
foreach (int n in range<int>(1, 10, n => ++n))
Console.Write(n);
Код:
IEnumerable<T> range<T>(T first, T last, Func<T, T> action) where T : struct, IComparable<T>
{
for (T result = first; result.CompareTo(last) <= 0; result = action(result))
yield return result;
}
{
for (T result = first; result.CompareTo(last) <= 0; result = action(result))
yield return result;
}
или вариант с проверкой:
Код:
foreach (var d in range<DateTime>(DateTime.Now, DateTime.Now.AddDays(10), d => d.AddDays(1)))
Console.WriteLine(d);
Console.WriteLine(d);
Код:
IEnumerable<T> range<T>(T first, T last, Func<T, T> func) where T : struct, IComparable<T>
{
T result = first;
Action<T> next = val =>
{
T newVal;
if ((newVal = func(result)).CompareTo(result) <= 0)
throw new InvalidOperationException();
result = newVal;
};
for (; result.CompareTo(last) <= 0; next(result))
yield return result;
}
{
T result = first;
Action<T> next = val =>
{
T newVal;
if ((newVal = func(result)).CompareTo(result) <= 0)
throw new InvalidOperationException();
result = newVal;
};
for (; result.CompareTo(last) <= 0; next(result))
yield return result;
}
Однако в первую очередь меня интересовало, можно ли в where задать ограничение типа по операции. Как оказалось - нет. Остаётся надеяться, что в следующих версиях шарпа такое появится. Народ в интернетах много по этому поводу пишет, высказывают аналогичное желание.
Цитата: koodeer
В общем меня интересовала возможность задать ограничение операции на тип шаблона. После курения msdn и google окончательно уяснил, что такой возможности нет.
В .NET это невозможно.
Цитата: koodeer
А как с этим в Nemerle?..
Создать генератор возможно :)
Во внешнему виду это будет похоже на решение на шаблонах C++.
Цитата: koodeer
А как с этим в Nemerle?..
Сейчас допустимо следующее использование:
Код:
foreach(x in ['a'..'z'])
WriteLine(x);
WriteLine(x);
Проблема в том, что этот код не слишком качественно компилируется - генерируется промежуточный список, что не есть хороший подход (вот его код).
Как говорится - patches are wellcome :)
Цитата: hardcase
этот код не слишком качественно компилируется
В том-то и дело, что хотелось найти простой вариант, который будет быстрым по выполнению и маленьким по объёму. Генератор - лишь пример.
Цитата: koodeer
В том-то и дело, что хотелось найти простой вариант, который будет быстрым по выполнению и маленьким по объёму. Генератор - лишь пример.
Дык, улучшить генерируемый код - плевое дело, например используя макрос можно свернуть аналог на C#. Я лишь продемонстрировал то, что уже сейчас из коробки есть в Nemerle. То, что создаваемый код не слишком высокого качества - не такая уж и проблема, генераторы подобные нужны нечасто :)
Код:
using Nemerle;
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Text;
using Nemerle.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MacroLibrary1
{
public macro range(start, end)
{
<[
RangeHelper.RangeGenerator($start, $end, fun(x)
{
mutable nextX = x;
++nextX;
nextX
})
]>
}
public module RangeHelper
{
public RangeGenerator[T](start : T, end : T, change : T -> T) : IEnumerable[T]
where T : IComparable[T]
{
for(mutable i = start; i.CompareTo(end) <= 0; i = change(i))
yield i;
}
}
}
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Text;
using Nemerle.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MacroLibrary1
{
public macro range(start, end)
{
<[
RangeHelper.RangeGenerator($start, $end, fun(x)
{
mutable nextX = x;
++nextX;
nextX
})
]>
}
public module RangeHelper
{
public RangeGenerator[T](start : T, end : T, change : T -> T) : IEnumerable[T]
where T : IComparable[T]
{
for(mutable i = start; i.CompareTo(end) <= 0; i = change(i))
yield i;
}
}
}
Использование:
Код:
using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;
using System;
using System.Collections.Generic;
using System.Console;
using System.Linq;
using MacroLibrary1;
module Program
{
Main() : void
{
foreach(x in range(1.0, 10.0))
WriteLine(x);
_ = ReadLine();
}
}
using Nemerle.Text;
using Nemerle.Utility;
using System;
using System.Collections.Generic;
using System.Console;
using System.Linq;
using MacroLibrary1;
module Program
{
Main() : void
{
foreach(x in range(1.0, 10.0))
WriteLine(x);
_ = ReadLine();
}
}
Хотелось производить вычисления с передаваемыми типами в универсальных классах, причём без лишних телодвижений, но - облом.
hardcase, демон-искуситель... уболтает перейти на Nemerle... :rolleyes:
Код:
using Nemerle;
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Text;
using Nemerle.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MacroLibrary1
{
public macro range(start, end)
{
<[
def start = $start;
def end = $end;
if(start < end)
RangeHelper.Ascending(start, end,
fun(x)
{
mutable nextX = x;
++nextX;
nextX
})
else
RangeHelper.Descending(start, end,
fun(x)
{
mutable nextX = x;
--nextX;
nextX
})
]>
}
public module RangeHelper
{
public Ascending[T](start : T, end : T, change : T -> T) : IEnumerable[T]
where T : IComparable[T]
{
for(mutable i = start; i.CompareTo(end) <= 0; i = change(i))
yield i;
}
public Descending[T](start : T, end : T, change : T -> T) : IEnumerable[T]
where T : IComparable[T]
{
for(mutable i = start; i.CompareTo(end) >= 0; i = change(i))
yield i;
}
}
}
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Text;
using Nemerle.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MacroLibrary1
{
public macro range(start, end)
{
<[
def start = $start;
def end = $end;
if(start < end)
RangeHelper.Ascending(start, end,
fun(x)
{
mutable nextX = x;
++nextX;
nextX
})
else
RangeHelper.Descending(start, end,
fun(x)
{
mutable nextX = x;
--nextX;
nextX
})
]>
}
public module RangeHelper
{
public Ascending[T](start : T, end : T, change : T -> T) : IEnumerable[T]
where T : IComparable[T]
{
for(mutable i = start; i.CompareTo(end) <= 0; i = change(i))
yield i;
}
public Descending[T](start : T, end : T, change : T -> T) : IEnumerable[T]
where T : IComparable[T]
{
for(mutable i = start; i.CompareTo(end) >= 0; i = change(i))
yield i;
}
}
}
Замечу, что этот код поддерживает как константы так и выражения, вычисляемые в рантайме.
Довести до релизного качества его можно добавив проверки на то, что наши выражения - константы и убрав одну из веток if-а, оставив в коде лишь одну лямбду.
З.Ы. Скорее всего этим решением я в ближайшее время заменю непроизводительный код в транке Nemerle (мне кажется в багтреке была запись по этому поводу). :)