1

Тема: Синтаксис итераторов

Раз итератор -- пошаговая функция, ее запись должна иметь конструкцию для записи шагов -- итераций, синтаксис которых и будет одной из альтернатив циклам.

Общий синтаксис итератора:

итератор = выражение [блок-from] [блок-where]

блок-from = "from" выражение "to"|"downto" выражение ["by" выражение]
блок-where = "where" условия

На данный момент блок с from считается синтаксическим сахаром к общей форме с where, содержащим выражения с prior, но использование ключевого слова prior для обозначения линейных итераций вызывает у меня сомнение. Слово prior взято из SQL, где оно обозначает уровни при построении дерева, и в этой роли я считаю его наиболее употребительным с точки зрения носителя английского языка.

Конкретный смысл конструкции from нужно еще расписать, для чего вначале обдумать. Задавайте вопросы, предлагайте толкования.

Добавлено 01.07.2015 20:14

В рамках этой темы выходит, что ключевое слово for исключается из языка.

2

Re: Синтаксис итераторов

Разность подхода к итераторам в C++ и Rust

В черновиках блога найден хороший пример разности подхода к итераторам в C++ и Rust, взятый из комментария на Хабре. Итератор на C++:

template <class Category, class T, class Distance = ptrdiff_t,
          class Pointer = T*, class Reference = T&>
  struct iterator {
    typedef T         value_type;
    typedef Distance  difference_type;
    typedef Pointer   pointer;
    typedef Reference reference;
    typedef Category  iterator_category;
  };

Типаж с итераторами на Rust (приведен частично, в хабракомментарии он гораздо длиннее):

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;

    fn size_hint(&self) -> (usize, Option<usize>) { ... }
    fn count(self) -> usize { ... }
    fn last(self) -> Option<Self::Item> { ... }
    fn nth(&mut self, n: usize) -> Option<Self::Item> { ... }
    fn chain<U>(self, other: U) -> Chain<Self, U::IntoIter> where U: IntoIterator<Item=Self::Item> { ... }
    fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter> where U: IntoIterator { ... }
    fn map<B, F>(self, f: F) -> Map<Self, F> where F: FnMut(Self::Item) -> B { ... }
    fn filter<P>(self, predicate: P) -> Filter<Self, P> where P: FnMut(&Self::Item) -> bool { ... }
    fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F> where F: FnMut(Self::Item) -> Option<B> { ... }
    fn enumerate(self) -> Enumerate<Self> { ... }
    fn peekable(self) -> Peekable<Self> { ... }
}

В этом примере четко видна разница в подходах: ориентация на цикл for в традиционном C++ и использование охраны в более современном Rust. Подход Кантора ближе к Rust. Эту тему хорошо объяснить голосом.

3

Re: Синтаксис итераторов

Развивать тему рано или поздно нужно, и, видимо, лучше это сделать на примерах. Мне понравился вывод в консоль по примеру Rust, так что теперь буду чередовать его с чисто функциональным подходом.

Начнем с самого простого примера:

with
  console = new Platform:Console;
do
  console.WriteLine(from 1 to 10);
end;

Легко догадаться, что этот код должен напечатать числа от 1 до 10.

Конструкция "from 1 to 10" -- анонимный итератор, последовательно возвращающий значения от 1 до 10, а сама конструкция является выражением объектной алгебры и аналогична оператору yield в некоторых других языках программирования.

В Канторе нет ссылок на код, поэтому родную функцию нельзя передать как обратный вызов или делегат. Вместо этого Кантор предлагает концепцию, называмую объектной алгеброй. По большому счету, это части оператора select SQL, вытащенные из окружения РСУБД и погруженные в объектную среду языка общего назначения.

Итератор, записанный при помощи from..to, представляет собой виртуальный список: объекта типа "список" в коде нет, а значения его есть. Порождаемый им скрытый цикл -- аналог nested loops в плане SQL.

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

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

4 (изменено: Freeman, 09.06.2016 в 17:17)

Re: Синтаксис итераторов

Небольшая статья в тему — «Способы генерации числовой последовательности (данных) в MySQL».

Для наглядности дублирую сюда наиболее значимые в контексте темы примеры для PostgreSQL и MySQL
postgres=# SELECT * FROM generate_series(1,10);
postgres=# insert into test select * from generate_series(1,10);

MariaDB [metemplate]> SELECT * FROM seq_1_to_5;
MariaDB [metemplate]> SELECT * FROM seq_1_to_15_step_2;

Функциональность не ограничивается только генерацией. Можно делать объединения, работать как с нормальными обычными таблицами.

MariaDB [metemplate]> select example.a, example.b from example inner join (select seq from seq_1_to_15) as generate on generate.seq = example.a;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
|    4 |    1 |
|    2 |    7 |
|    9 |    9 |
|    1 |   19 |
|   11 |   12 |
+------+------+
6 rows in set (0.00 sec)

В комментарии к статье есть ссылка на SQL.ru с другим примером для MySQL, используемым для нумерации строк:

SELECT @i := @i + 1 AS row_number, your_table.*
FROM your_table, (select @i:=0);

Этот синтаксис MySQL похож на запись агрегатных функций в Канторе.

5

Re: Синтаксис итераторов

Близкое к объектной алгебре понятие называется списковым включением, в Википедии даже есть пример вложенных итераций по переменным на Питоне:

[(x, y) for x in range(1, 10) for y in range(1, 10) if x % y == 0]

Не знаю, допустима ли здесь в Питоне блочная запись, но таком виде смотрится не очень читабельно, для понимания нужно вглядываться и вникать. Проблема та же, что и с SQL, записанным в одну строчку.

В качестве реализации списковых включений в Википедии упоминается и LINQ.