Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Генератор обобщённого типа.

297
29 октября 2010 года
koodeer
1.2K / / 02.05.2009
Пробую сделать генератор целых чисел любого типа и символов, чтобы можно было пользоваться примерно так:
 
Код:
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;
}

Не компилируется, выдаёт ошибку:
Цитата:
Operator '++' cannot be applied to operand of type 'T'


Что указать в where? Возможно ли это вообще?

297
30 октября 2010 года
koodeer
1.2K / / 02.05.2009
В общем меня интересовала возможность задать ограничение операции на тип шаблона. После курения msdn и google окончательно уяснил, что такой возможности нет.

Выкрутиться можно, например, как сделано здесь. Но это весьма тяжеловесно и громоздко.

Я всё больше разочаровываюсь в Шарпе :(.
А как с этим в Nemerle?..
8.2K
30 октября 2010 года
bagie2
299 / / 26.10.2008
а если так сделать?

 
Код:
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);


 
Код:
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;
        }


или вариант с проверкой:

 
Код:
foreach (var d in range<DateTime>(DateTime.Now, DateTime.Now.AddDays(10), d => d.AddDays(1)))
                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;
        }
297
30 октября 2010 года
koodeer
1.2K / / 02.05.2009
bagie2, хорошие варианты, благодарю.
Однако в первую очередь меня интересовало, можно ли в where задать ограничение типа по операции. Как оказалось - нет. Остаётся надеяться, что в следующих версиях шарпа такое появится. Народ в интернетах много по этому поводу пишет, высказывают аналогичное желание.
5
30 октября 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: koodeer
В общем меня интересовала возможность задать ограничение операции на тип шаблона. После курения msdn и google окончательно уяснил, что такой возможности нет.


В .NET это невозможно.

Цитата: koodeer
А как с этим в Nemerle?..


Создать генератор возможно :)
Во внешнему виду это будет похоже на решение на шаблонах C++.

5
30 октября 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: koodeer

А как с этим в Nemerle?..


Сейчас допустимо следующее использование:

 
Код:
foreach(x in ['a'..'z'])
  WriteLine(x);

Проблема в том, что этот код не слишком качественно компилируется - генерируется промежуточный список, что не есть хороший подход (вот его код).
Как говорится - patches are wellcome :)
297
31 октября 2010 года
koodeer
1.2K / / 02.05.2009
Цитата: hardcase
этот код не слишком качественно компилируется


В том-то и дело, что хотелось найти простой вариант, который будет быстрым по выполнению и маленьким по объёму. Генератор - лишь пример.

5
31 октября 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: 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.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();
  }
}
297
31 октября 2010 года
koodeer
1.2K / / 02.05.2009
Это всё замечательно, но в шарпе-то как быть? :)
Хотелось производить вычисления с передаваемыми типами в универсальных классах, причём без лишних телодвижений, но - облом.

hardcase, демон-искуситель... уболтает перейти на Nemerle... :rolleyes:
5
31 октября 2010 года
hardcase
4.5K / / 09.08.2005
Следующий шаг - добавление поддержки как возрастающих так и убывающих последовательностей:
Код:
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;
    }
  }
}



Замечу, что этот код поддерживает как константы так и выражения, вычисляемые в рантайме.
Довести до релизного качества его можно добавив проверки на то, что наши выражения - константы и убрав одну из веток if-а, оставив в коде лишь одну лямбду.


З.Ы. Скорее всего этим решением я в ближайшее время заменю непроизводительный код в транке Nemerle (мне кажется в багтреке была запись по этому поводу). :)
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог