Итерации
От: _nn_ www.nemerleweb.com
Дата: 05.08.10 11:50
Оценка:
Берем простую программу на C#:
namespace qq
{
    static class Program
    {
        static void Main()
        {
            for (var i = 1; i <= 10; ++i)
                Console.WriteLine(i);
        }
    }
}


После компиляции убеждаемся рефлектором, что генерируемый код идентичен оригиналу.

А теперь берем Nemerle в разных вариантах:
module Program
{
  Mutable() : void
  {
    for (mutable i = 1; i <= 10; ++i)
      Console.WriteLine(i);
  }
  
  ForeachList() : void
  {
    foreach (i in [1..10])
      Console.WriteLine(i);
  }
  
  ForeachGenList() : void
  {
    foreach (i in $[1..10])
      Console.WriteLine(i);
  }

  ForeachWithList() : void
  {
    def l = $[1..10];
    foreach (i in l)
      Console.WriteLine(i);
  }
  
  Main() : void
  {
    Mutable();
    ForeachList();
    ForeachGenList();
    ForeachWithList();
  }
}


Результат:
  Mutable
private static void Mutable()
{
    int i = 0;
    i = 1;
    while (true)
    {
        if (i > 10)
        {
            break;
        }
        Console.WriteLine(i);
        i++;
    }
}


  ForeachList
private static void ForeachList()
{
    int num = 0;
    bool flag = false;
    int num3 = 0;
    num = 1;
    int num2 = 10;
    flag = num <= num2;
    num3 = num2 - 1;
    bool flag2 = num3 > num2;
    while (flag)
    {
        num2 = num;
        if (flag2)
        {
            flag = num >= num3;
        }
        else
        {
            flag = num <= num3;
        }
        num++;
        Console.WriteLine(num2);
    }
}


  ForeachGenList
private static void ForeachGenList()
{
    int num = 0;
    bool flag = false;
    int num3 = 0;
    num = 1;
    int num2 = 10;
    flag = num <= num2;
    num3 = num2 - 1;
    bool flag2 = num3 > num2;
    while (flag)
    {
        num2 = num;
        if (flag2)
        {
            flag = num >= num3;
        }
        else
        {
            flag = num <= num3;
        }
        num++;
        Console.WriteLine(num2);
    }
}


  ForeachWithList
private static void ForeachWithList()
{
    int num = 0;
    list<int>.Cons cons = null;
    list<int>.Cons cons3 = null;
    bool flag = false;
    int num3 = 0;
    cons = null;
    cons3 = null;
    num = 1;
    int hd = 10;
    flag = num <= hd;
    num3 = hd - 1;
    bool flag2 = num3 > hd;
    while (true)
    {
        if (!flag)
        {
            break;
        }
        hd = num;
        if (flag2)
        {
            flag = num >= num3;
        }
        else
        {
            flag = num <= num3;
        }
        num++;
        list<int>.Cons cons2 = new list<int>.Cons(hd, list<int>.Nil._N_constant_object);
        if (cons == null)
        {
            cons = cons2;
            cons3 = cons2;
        }
        else
        {
            cons3.tl = cons2;
            cons3 = cons2;
        }
    }
    list<int> tl = (cons != null) ? ((list<int>) cons) : ((list<int>) list<int>.Nil._N_constant_object);
    tl = tl;
    while (true)
    {
        if (!(tl is list<int>.Cons))
        {
            break;
        }
        num = ((list<int>.Cons) tl).hd;
        tl = ((list<int>.Cons) tl).tl;
        Console.WriteLine(num);
        tl = tl;
    }
}


Выводы:
  • for в Nemerle не гененерирует то же код как и for в C#.
  • foreach (i in [1..10]) идетичен foreach (i in $[1..10]).
  • Генерация списков просто отвратительна. Лучше вместо создания списка создать энумератор.
  • http://rsdn.nemerleweb.com
    http://nemerleweb.com
    Re: Итерации
    От: catbert  
    Дата: 05.08.10 18:54
    Оценка:
    Здравствуйте, _nn_, Вы писали:

    __>Выводы:

    __>for в Nemerle не гененерирует то же код как и for в C#.
    Интересно бы измерить производительность обоих вариантов.

    __>foreach (i in [1..10]) идетичен foreach (i in $[1..10]).

    Более того, выражения [comprehension] и $[comprehension] идентичны и обрабатываются тем же макросом.

    __>Генерация списков просто отвратительна. Лучше вместо создания списка создать энумератор.

    100% согласен. Мне почему-то вообще казалось, что comprehensions генерируют ленивый код.
    Re[2]: Итерации
    От: hardcase Пират http://nemerle.org
    Дата: 05.08.10 19:42
    Оценка:
    Здравствуйте, catbert, Вы писали:

    C>Здравствуйте, _nn_, Вы писали:


    __>>Выводы:

    __>>for в Nemerle не гененерирует то же код как и for в C#.
    C>Интересно бы измерить производительность обоих вариантов.

    Посмотри проектик тут.
    /* иЗвиНите зА неРовнЫй поЧерК */
    Re: Итерации
    От: VladD2 Российская Империя www.nemerle.org
    Дата: 09.08.10 03:15
    Оценка:
    Здравствуйте, _nn_, Вы писали:

    __>После компиляции убеждаемся рефлектором, что генерируемый код идентичен оригиналу.

    ...
    __>Выводы:
    __>
  • for в Nemerle не гененерирует то же код как и for в C#.

    Ничего страшного. Это просто рефлектор не умеет его как следует декомпилировать.
    Хардкейс дал правильную ссылку. Он занимался этой проблемой и сделал вывод, что с точки зрения скорости наш for (а точнее функция с концевой рекурсией) не отличается от того что генерирует компилятор C#.

    __>
  • foreach (i in [1..10]) идетичен foreach (i in $[1..10]).

    Ну, дык один код используется.

    __>
  • Генерация списков просто отвратительна. Лучше вместо создания списка создать энумератор.

    Это лист компрешеншон. Потому списки и генерируются. Хочешь сделать IEnumerable компрешеншон — сделай. Но в принципе уже есть более универсальное решение ComputationExpressions с помощью которых в том числе и аналог IEnumerable компрешеншон реализуется.

    В общем, я не вижу смысла в том чтобы что-то менять или доделывать. И так есть много задач которые нужно успеть решить.
  • Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
    Re[2]: Итерации
    От: _nn_ www.nemerleweb.com
    Дата: 09.08.10 07:36
    Оценка:
    Здравствуйте, VladD2, Вы писали:

    > foreach (i in [1..10]) идетичен foreach (i in $[1..10]).


    VD>Ну, дык один код используется.


    Т.к. этот код используется довольно часто, хотелось бы немного его улучшить.
    Судя по коду это вызывает ListComprehensionHelper.ExpandRange ( http://code.google.com/p/nemerle/source/browse/nemerle/trunk/macros/Util.n#520 ).

    Есть следующий код
        [Nemerle.Macros.Hygienic]
        public ExpandRange (inrange : PExpr, acc : PExpr) : option [PExpr]
        {
          match (inrange) {
             // ...
            | <[ $pat in $[$first .. $last] ]>
            | <[ $pat in [$first .. $last] ]> =>
             // ...
          }
        }


    Как в выделеном фрагменте узнать если передаются литералы или переменные ?
    Т.е. для код foreach (i in [1..10]) или foreach(i in [10..0]) можно смело создавать обычный for.
    Было бы еще хорошо создавать for если заданы переменные, но значения их известны в макросе.
    def x = 1;
    def y = 2;
    foreach (i in [x..y]){ }


    А если не передается литерал, то тогда шаг +1 или надо указать.
    Например в таком коде:
    foreach (i in [(int.Parse(Console.ReadLine())) .. (int.Parse(Console.ReadLine()))]){ }
    http://rsdn.nemerleweb.com
    http://nemerleweb.com
    Re[3]: Итерации
    От: hardcase Пират http://nemerle.org
    Дата: 09.08.10 10:24
    Оценка:
    Здравствуйте, _nn_, Вы писали:

    __>Здравствуйте, VladD2, Вы писали:


    >> foreach (i in [1..10]) идетичен foreach (i in $[1..10]).


    VD>>Ну, дык один код используется.


    __>Т.к. этот код используется довольно часто, хотелось бы немного его улучшить.

    __>Судя по коду это вызывает ListComprehensionHelper.ExpandRange ( http://code.google.com/p/nemerle/source/browse/nemerle/trunk/macros/Util.n#520 ).

    __>Есть следующий код

    __>
    __>    [Nemerle.Macros.Hygienic]
    __>    public ExpandRange (inrange : PExpr, acc : PExpr) : option [PExpr]
    __>    {
    __>      match (inrange) {
    __>         // ...
    __>        | <[ $pat in $[$first .. $last] ]>
    __>        | <[ $pat in [$first .. $last] ]> =>
    __>         // ...
    __>      }
    __>    }
    __>


    __>Как в выделеном фрагменте узнать если передаются литералы или переменные ?


    Литералы описываются PExpr.Literal, в которой лежит значение этого литерала.
    К сожалению сейчас компилятор (класс ConstsntFolder) не умеет сворачивать в литералы код вида:
    def a = 1;
    def b = 2;
    
    def c = a + b; // компилятор сгенерирует код для честного сложения чисел
    /* иЗвиНите зА неРовнЫй поЧерК */
    Re[3]: Итерации
    От: VladD2 Российская Империя www.nemerle.org
    Дата: 09.08.10 17:35
    Оценка:
    Здравствуйте, _nn_, Вы писали:

    __>Есть следующий код

    __>
    __>    [Nemerle.Macros.Hygienic]
    __>    public ExpandRange (inrange : PExpr, acc : PExpr) : option [PExpr]
    __>    {
    __>      match (inrange) {
    __>         // ...
    __>        | <[ $pat in $[$first .. $last] ]>
    __>        | <[ $pat in [$first .. $last] ]> =>
    __>         // ...
    __>      }
    __>    }
    __>


    __>Как в выделеном фрагменте узнать если передаются литералы или переменные ?


    Добавить анализ (паттерн-матчингом) first и last. Что-то типа
    match (inrange)
    {
      | <[ $pat in $[$first .. $last] ]>
        match (first)
        {
          | PExpr.Literal(...) => ...
          | PExpr.Ref(name)    => ...
        }


    __>Т.е. для код foreach (i in [1..10]) или foreach(i in [10..0]) можно смело создавать обычный for.


    Надо понимать, что диапазоны вроде [1..10] и [10..0] используются в основном для баловства. В реальной жизни код выглядит как-то так:
    foreach (i in [0 .. ary.Length - 1]) 
      ...

    и эта оптимизация не пройдет.

    __>Было бы еще хорошо создавать for если заданы переменные, но значения их известны в макросе.

    __>
    __>def x = 1;
    __>def y = 2;
    __>foreach (i in [x..y]){ }
    __>


    А тут проблема. Никто не гарантирует что x <= y. Потому макрос и генерирует такой витиеватый код. Все что мы можем сделать — это переписать макрос так, чтобы он генерировал два варианта цикла и if (в начале) который бы вызывал бы одну или другую версию. Причем и этом может привести к проблемам, так как сейчас можно динамически менять значения выражений, скажем так:
    foreach (i in [f1() .. f2()]) // где f1() и f2() меняют возвращаемое значение во время итерации.
    ...
    [/Nemerle]
    Если же ввести такую оптимизацию, то значения будут вычисляться один раз, что может быть не применимо в некоторых (хотя весьма экзотических) случаях.

    __>А если не передается литерал, то тогда шаг +1 или надо указать.

    __>Например в таком коде:
    __>
    foreach (i in [(int.Parse(Console.ReadLine())) .. (int.Parse(Console.ReadLine()))]){ }


    Тебе не кажется, что все это смахивает на борьбу с ветряными мельницами? В реальной жизни в не тривиальных случаях проще использовать for. Случаи эти так редки, что тратить на них время просто нецелесообразно, на мой взгляд.

    Нам нужно сосредоточиться на устранении багов и доведении продукта до версии 1.0. А потом заняться второй версией. Там будет много вкусного, так что мелочи вроде этих покажутся просто смешными.
    Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
    Re[4]: Итерации
    От: _nn_ www.nemerleweb.com
    Дата: 10.08.10 05:21
    Оценка:
    Здравствуйте, VladD2, Вы писали:

    <skip/>

    VD>Надо понимать, что диапазоны вроде [1..10] и [10..0] используются в основном для баловства. В реальной жизни код выглядит как-то так

    VD>
    VD>foreach (i in [0 .. ary.Length - 1]) 
    VD>  ...
    VD>

    VD>и эта оптимизация не пройдет.
    В данном варианте можно проверить тип ary и если это массив, то генерировать for.

    __>>Было бы еще хорошо создавать for если заданы переменные, но значения их известны в макросе.

    __>>
    __>>def x = 1;
    __>>def y = 2;
    __>>foreach (i in [x..y]){ }
    __>>


    VD>А тут проблема. Никто не гарантирует что x <= y. Потому макрос и генерирует такой витиеватый код.

    Почему не гарантирует ?
    Ведь известно что x = 1 , y = 2 и эти значения не изменятся.

    VD>Все что мы можем сделать — это переписать макрос так, чтобы он генерировал два варианта цикла и if (в начале) который бы вызывал бы одну или другую версию. Причем и этом может привести к проблемам, так как сейчас можно динамически менять значения выражений, скажем так:

    VD>foreach (i in [f1() .. f2()]) // где f1() и f2() меняют возвращаемое значение во время итерации.
    VD> ...
    VD>[/Nemerle]
    VD>Если же ввести такую оптимизацию, то значения будут вычисляться один раз, что может быть не применимо в некоторых (хотя весьма экзотических) случаях.
    Сегодня значения тоже вычисляются только один раз, так что нет никаких экзотических случаев:
    module A
    {
      mutable _x : int = 0;
      X() : int
      {
        Console.WriteLine("X");
        ++_x;
        _x;
      }
      
      mutable _y : int = 3;
      Y() : int
      {
        Console.WriteLine("Y");
        --_y;
        _y;
      }
      
      Main() : void
      {
        foreach (i in [X() .. Y()])
        {
          Console.WriteLine(i);
        }
      }
    }


    X
    Y
    1
    2


    VD>Тебе не кажется, что все это смахивает на борьбу с ветряными мельницами? В реальной жизни в не тривиальных случаях проще использовать for. Случаи эти так редки, что тратить на них время просто нецелесообразно, на мой взгляд.

    В большинстве случаев да редки, но некоторые как [1..arr.Length-1] могли бы быть более оптимальными.

    VD>Нам нужно сосредоточиться на устранении багов и доведении продукта до версии 1.0. А потом заняться второй версией. Там будет много вкусного, так что мелочи вроде этих покажутся просто смешными.

    Надеюсь во второй версии это можно будет сделать еще проще
    http://rsdn.nemerleweb.com
    http://nemerleweb.com
    Re[5]: Итерации
    От: VladD2 Российская Империя www.nemerle.org
    Дата: 17.08.10 23:57
    Оценка:
    Здравствуйте, _nn_, Вы писали:

    VD>>
    VD>>foreach (i in [0 .. ary.Length - 1]) 
    VD>>  ...
    VD>>

    VD>>и эта оптимизация не пройдет.
    __>В данном варианте можно проверить тип ary и если это массив, то генерировать for.

    То есть, ты предлагаешь поверять сделать закладку на конкретный паттерн приведенный выше? В принципе это даже разуно. Но это как-то не вяжется с концепцией универсальности. Ведь на все случаи закладок не сделать.

    __>Почему не гарантирует ?

    __>Ведь известно что x = 1 , y = 2 и эти значения не изменятся.

    На самом деле о том, что есть какие-то x и y, и уж тем более о том, что у них есть какие-то конкретные значения мы знать не можем. Мы ведь работаем на стадии компиляции. Макросу может быть передан абстрактный код. Например вместо y может быть функция значение которой будет известно только в рантайме. И что при этом прикажешь делать? Вот и генерируется универсальный код который может и не супер пушистый, но зато будет корректно работтать в любом случае. В общем, поставь себя на место того кто пишет этот макрос...

    __>Сегодня значения тоже вычисляются только один раз, так что нет никаких экзотических случаев:...


    Вот это странно. Надо глубже разобраться в коде.

    Но в любом случае, о том какие значения будут у X() и Y() мы узнаем только в рантайме, а значит мы не сможем предсказать будет ли Y() больше X() или нет. Все что мы можем сделать — это закэшиовать эти значения перед выполнением цикла и вызвать одну из версий. Но это приведет к генерации сразу двух тел циклов (дублирование кода).

    VD>>Тебе не кажется, что все это смахивает на борьбу с ветряными мельницами? В реальной жизни в не тривиальных случаях проще использовать for. Случаи эти так редки, что тратить на них время просто нецелесообразно, на мой взгляд.

    __>В большинстве случаев да редки, но некоторые как [1..arr.Length-1] могли бы быть более оптимальными.

    Откровенно говоря я скорее воспользуюсь foreach с whith и индексом. Но если хочешь сделай специальную закладку на описанный случай. Увидишь сколько сил придется потратить на казалось бы простую оптимизацию и сам решишь стоит ли овчинка выделки.
    Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.