Streams.js: отложенные (ленивые) вычисления в Javascript

    Javascript-библиотека stream.js вводит «новую»1 структуру числовых данных: поток (stream). Это контейнер, который похож на массив (array) и связный список (linked list), но содержит неограниченное количество элементов, реализованное методом отложенных вычислений.

    var s = Stream.range( 10, 20 );  
    s.print(); // prints the numbers from 10 to 20

    Для аргумента Stream.range( low, high ) можно указать только начальную границу диапазона Stream.range( low ), тогда поток будет состоять из неограниченного количества натуральных чисел. По умолчанию Stream.range() начинается с 1.

    Представление о якобы «бесконечном» диапазоне упрощает программирование. Например, таким образом выводится список чётных и нечётных чисел.

    var naturalNumbers = Stream.range(); // naturalNumbers is now 1, 2, 3, ...  
    var evenNumbers = naturalNumbers.map( function ( x ) {  
        return 2 * x;  
    } ); // evenNumbers is now 2, 4, 6, ...  
    var oddNumbers = naturalNumbers.filter( function ( x ) {  
        return x % 2 != 0;  
    } ); // oddNumbers is now 1, 3, 5, ...  
    evenNumbers.take( 3 ).print(); // prints 2, 4, 6  
    oddNumbers.take( 3 ).print(); // prints 1, 3, 5

    Создание собственных потоков с заданными параметрами возможно с помощью new Stream( head, functionReturningTail ). Например, вот лаконичный способ для списка натуральных чисел.

    function ones() {  
        return new Stream( 1, ones );  
    }  
    function naturalNumbers() {  
        return new Stream(  
            // the natural numbers are the stream whose first element is 1...  
            1,  
            function () {  
                // and the rest are the natural numbers all incremented by one  
                // which is obtained by adding the stream of natural numbers...  
                // 1, 2, 3, 4, 5, ...  
                // to the infinite stream of ones...  
                // 1, 1, 1, 1, 1, ...  
                // yielding...  
                // 2, 3, 4, 5, 6, ...  
                // which indeed are the REST of the natural numbers after one  
                return ones().add( naturalNumbers() );  
            }   
        );  
    }  
    naturalNumbers().take( 5 ).print(); // prints 1, 2, 3, 4, 5

    1 P.S. Аналогичная концепция отложенных вычислений, но с другим синтаксисом, реализована в linq.js и node-lazy, так что со стороны автора не совсем корректно называть списки «новой структурой данных» для JavaScript.

    Библиотека streams.js на CoffeeScript: coffeestream.
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 11

      +1
      Мне кажется, слова «ленивый» и «LINQ» в заголовке сделали бы намного яснее суть поста.
        0
        Добавил "(ленивые)" в хедлайн.
        0
        Просветите пожалуйста, какое практическое применение всего этого (желательно на простом примере). Спасибо.
          0
          Такой логический Range можно пихать в алгоритмы, заточенные на приём обычного Range (например массива). В случае с логическим Range нам не нужна память для хранения исходного массива, плюс он может быть бесконечным.

          Бесконечность: например бесконечный range задаёт карту высот для некоторой двумерной поверхности в игре, мы можем промотать карту на сколько угодно вправо и влево и отрисовать поверхность, с минимальным расходом памяти.
            0
            Ага, вроде дошло. Но при этом нам же придется каждый раз вычислять значение нового элемента. И чем сложнее функция, тем больше это будет влиять на производительность. Или нет?
              0
              Ничем, это так называемый «синтаксический сахар». Повышается читаемость кода.
              Ну плюс еще можно «закэшировать» часть данных «последовательности» (если напишите соответствующую реализацию).
                0
                Да, придётся. Но иногда это не критично, а ингда и вовсе не важно.
                Не важно, когда исходный массив требуется ровно один раз, например нам нужен массив содержащий квадраты всех чисел от 1 до 5, мы можем записать:

                var arr1 = new Array();

                for( var i = 1; i <= 5; ++i )
                {
                arr[i-1] = i*i;
                }

                // или

                var arr2 = Stream.range( 1, 5 ).map( function ( x ) { return x * x } );

                // выше, правда, создаётся объект типа range (если я правильно понял)
                // но наверное есть способ просто сконвертировать его в массив.


                Плюсы такого подхода, как правильно написал smashercosmo: синтаксический сахар и возможность подсунуть свою последовательность алгоритмам, которые принимают на вход массивы.
            0
            Стоит также посмотреть на streamer.
              0
              Еще есть довольно популярная либа — wu.js
                0
                банальный генератор. что тут нового?
                habrahabr.ru/blogs/javascript/122620/
                  0
                  Думаю хорошим примером использования бесконечных массивов был бы список простых чисел генерируемый динамически на основе решета Эратосфена. Либа такое позволяет?

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

                  Самое читаемое