пятница, 24 января 2014 г.

ToDo. Написать про массивы, списки и итераторы

Есть два базовых понятия - "массив" и "итератор".

"Массив" - это структура данных.
"Итератор" - это императивная конструкция для перебора элементов массива.

Простейший пример:

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
FOR A WriteLn
// - простейший итератор по массиву.
//   Принимает ДВА параметра - собственно массив и подитеративную функцию.
// - в данном примере на устройство вывода будут выведены числа 1 2 3 4 5

revert - принимает в качестве параметра массив и возвращает его "перевёрнутое отображение".

Именно - ОТОБРАЖЕНИЕ, а не КОПИЮ.

Пример:

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
FOR revert A WriteLn
// - в данном примере на устройство вывода будут выведены числа 5 4 3 2 1

join - принимает в качестве параметров ДВА массива и возвращает их "отображение, как если бы они были склеены".

Опять же - именно ОТОБРАЖЕНИЕ, а не копию.

Пример:

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
FOR join A A WriteLn
// - в данном примере на устройство вывода будут выведены числа 1 2 3 4 5 1 2 3 4 5

Ещё пример:

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
FOR join A join A A WriteLn
// - в данном примере на устройство вывода будут выведены числа 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5

И ещё пример:

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
ARRAY VAR В := [ 6 7 8 9 10 ]
// - Объявление массива и его инициализация
FOR join A B WriteLn
// - в данном примере на устройство вывода будут выведены числа 1 2 3 4 5 6 7 8 9 10

removeduplicates - принимает в качестве параметра массив и возвращает его "отображение с удалёнными дубликатами".

Пример:

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
FOR removeduplicates join A A WriteLn
// - в данном примере на устройство вывода будут выведены числа 1 2 3 4 5, 
//   как если бы мы работали с просто A, а не с join A A

И ещё пример:

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
ARRAY VAR В := [ 2 6 3 7 10 ]
// - Объявление массива и его инициализация
FOR removeduplicates join A B WriteLn
// - в данном примере на устройство вывода будут выведены числа 1 2 3 4 5 6 7 10

filter - принимает ДВА параметра - функтор и массив и возвращает "отображение массива в котором все элементы удовлетворяют значению функтора".

Пример:

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
FOR filter BOOLEAN ( IN anItem Result := anItem != 2 ) A WriteLn
// - в данном примере на устройство вывода будут выведены числа 1 3 4 5

Частными случаями "массивов" являются файлы.

Пример:

FILE VAR F := OpenRead 'C:\somefile.txt'
// - Объявление файла и его инициализация
FOR chars F Write
// - в данном примере на устройство вывода будет выведено содержимое файла посимвольно

Ещё пример:

FILE VAR F := OpenRead 'C:\somefile.txt'
// - Объявление файла и его инициализация
FOR lines F WriteLn
// - в данном примере на устройство вывода будет выведено содержимое файла построчно

Возвращаемся к массивам.

sort - принимает в качестве параметров - функтор сортировки и массив и возвращает "отображение массива, сортированное в соответствии с функтором".

Пример:

ARRAY VAR A := [ 1 3 4 2 5 ]
// - Объявление массива и его инициализация
FOR sort < A WriteLn
// - в данном примере на устройство вывода будут выведены числа 1 2 3 4 5

Ещё пример (делает то же самое, что предыдущий, но иллюстрирует тот факт, что в функторе может быть написан "любой код", а не обязательно оператор из аксиоматики):

ARRAY VAR A := [ 1 3 4 2 5 ]
// - Объявление массива и его инициализация
FOR sort BOOLEAN ( IN anItem1 IN anItem2 Result := anItem1 < anItem2 ) A WriteLn
// - в данном примере на устройство вывода будут выведены числа 1 2 3 4 5

Свёртка массивов:

Пример:

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
INTEGER VAR X := ( 0 FOR A + )
// - в переменной X будет значение 0 + 1 + 2 + 3 + 4 + 5

Ещё пример:

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
INTEGER VAR X := ( 1 FOR A * )
// - в переменной X будет значение 1 * 1 * 2 * 3 * 4 * 5

И ещё пример (иллюстрирующий, что может быть подан ЛЮБОЙ пользовательский функтор, а не только оператор аксиоматики):

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
INTEGER VAR X := ( 0 FOR A AUTO ( IN anItem1 IN anItem2 Result := anItem1 + anItem2 ) )
// - в переменной X будет значение 0 + 1 + 2 + 3 + 4 + 5

И ещё пример:

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
INTEGER VAR X := ( 0 FOR A AUTO ( IN anItem1 IN anItem2 Result := anItem1 + anItem2 + 10 ) )
// - в переменной X будет значение 0 + 1 + 10 + 2 + 10 + 3 + 10 + 4 + 10 + 5 + 10

Встраивание "дополнительного функтора".

process - принимает два параметра - функтор обработки элемента массива и собственно массив.

Возвращает "отображение массива с применённым функтором".

Пример:

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
FOR process AUTO ( IN anItem Result := anItem + 10 ) A WriteLn
// - в данном примере на устройство вывода будут выведены числа 11 12 13 14 15

Ещё пример (комбинация process и revert):

ARRAY VAR A := [ 1 2 3 4 5 ]
// - Объявление массива и его инициализация
FOR revert process AUTO ( IN anItem Result := anItem + 10 ) A WriteLn
// - в данном примере на устройство вывода будут выведены числа 15 14 13 12 11

Да! Забыл сказать.

Модификатор AUTO - означает, что тип выражения будет выведен "автоматически" (как auto в C++11).

Итерация по скомпилированному коду.

codearray - принимает ОДИН параметр - ссылку на скомпилированный код и возвращает его в качестве "отображения массива".

ЛЮБОЙ скомпилированный код скриптовой машины можно получить в качестве "массива".

Пример:

PROCEDURE P1
 ( 1 + 2 )
 ( 3 + 4 )
 ( 5 + 6 )
;
// - эта процедура содержит ТРИ атомарных выражения - 1 + 2, 3 + 4, 5 + 6
//   Проитерируем эти выражения:

FOR codearray @ P1 ( IN anItem anItem DO WriteLn )
// - в приведённом примере будут выведены числа 1 + 2 3 + 4 5 + 6

!!! Конструкция DO - выполняет слово, ссылка на которое ей передана.

Ещё пример (комбинация codearray и process):

PROCEDURE P1
 ( 1 + 2 )
 ( 3 + 4 )
 ( 5 + 6 )
;
// - эта процедура содержит ТРИ атомарных выражения - 1 + 2, 3 + 4, 5 + 6
//   Проитерируем эти выражения:

FOR process ( IN anItem anItem DO ) codearray @ P1 WriteLn
// - в приведённом примере будут выведены числа 1 + 2 3 + 4 5 + 6

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

Проиллюстируем это ещё таким примером:

ARRAY FUNCTION A
// - функция, возвращающая массив

 PROCEDURE P1
 // - P1 - это вложенная процедура в функцию A и явно она нигде не вызывается, 
 //   используется только ССЫЛКА на неё (ниже)
  ( 1 + 2 )
  ( 3 + 4 )
  ( 5 + 6 )
 ;

 Result := process ( IN anItem anItem DO ) codearray @ P1
; // A

FOR A WriteLn
// - в приведённом примере будут выведены числа 1 + 2 3 + 4 5 + 6
//   Делается, всё то же самое, что и в предыдущем примере, 
//   только массив получаем как результат работы функции A

!!! @ - этот оператор возвращает ссылку на слово, которое стоит ЗА ним.

Ещё пример (то же самое, но с анонимным кодом):

ARRAY FUNCTION A
// - функция, возвращающая массив

 Result := process ( IN anItem anItem DO ) codearray @ ( 
  ( 1 + 2 )
  ( 3 + 4 )
  ( 5 + 6 )
 )
; // A

FOR A WriteLn
// - в приведённом примере будут выведены числа 1 + 2 3 + 4 5 + 6
//   Делается, всё то же самое, что и в предыдущем примере, 
//   только массив получаем как результат работы функции A, 
//   которая в свою очередь возвращает его 
//   как "отображение кода анонимной функции"

Приведённые конструкции в общем-то - аналог списков в Python'е.

Итерация по вложенным словам (или аналог RTTI).

membersarray - принимает один параметр - ссылку на слово и возвращает "отображение массива вложенных элементов".

Пример:

PROCEDURE P1

 PROCEDURE P1.1 
  1 + 2
 ; // P1.1

 PROCEDURE P1.2 
  3 + 4
 ; // P1.2

 PROCEDURE P1.3 
  5 + 6
 ; // P1.3
 
; // P1

FOR membersarray @ P1 ( IN anItem anItem DO WriteLn )
// - в приведённом примере будут выведены числа 1 + 2 3 + 4 5 + 6

Ещё пример (комбинация membersarray и process):

PROCEDURE P1

 PROCEDURE P1.1 
  1 + 2
 ; // P1.1

 PROCEDURE P1.2 
  3 + 4
 ; // P1.2

 PROCEDURE P1.3 
  5 + 6
 ; // P1.3
 
; // P1

FOR process ( IN anItem anItem DO ) membersarray @ P1 WriteLn
// - в приведённом примере будут выведены числа 1 + 2 3 + 4 5 + 6

Ну и пример (с функцией возвращающей массив):

ARRAY FUNCTION A

 PROCEDURE P1

  PROCEDURE P1.1 
   1 + 2
  ; // P.1.

  PROCEDURE P1.2 
   3 + 4
  ; // P1.2

  PROCEDURE P1.3 
   5 + 6
  ; // P1.3

 ; // P1

 Result := process ( IN anItem anItem DO ) membersarray @ P1
;

FOR A WriteLn
// - в приведённом примере будут выведены числа 1 + 2 3 + 4 5 + 6

А причём тут RTTI?

А вот причём: 

wordname - слово принимающее ссылку на слово и возвращающее его имя.

Пример:

PROCEDURE P1

 PROCEDURE P1.1 
  1 + 2
 ; // P1.1

 PROCEDURE P1.2 
  3 + 4
 ; // P1.2

 PROCEDURE P1.3 
  5 + 6
 ; // P1.3
 
; // P1

FOR membersarray @ P1 ( IN anItem wordname anItem WriteLn )
// - в приведённом примере будут выведены строки P1.1 P1.2 P1.3

Ещё пример:

PROCEDURE P1

 PROCEDURE P1.1 
  1 + 2
 ; // P1.1

 PROCEDURE P1.2 
  3 + 4
 ; // P1.2

 PROCEDURE P1.3 
  5 + 6
 ; // P1.3
 
; // P1

FOR process ( IN anItem wordname anItem ) membersarray @ P1 WriteLn
// - в приведённом примере будут выведены строки P1.1 P1.2 P1.3

Да!!! Слово IN - определяет ВХОДНОЙ параметр.

А скобки - () - определяют "анонимное слово". Собственным пространством имён.

Далее про RTTI:

wordparent - получает ссылку на слово и возвращает ссылку на его родителя.

Пример:

PROCEDURE P1

 PROCEDURE P1.1 
  1 + 2
 ; // P1.1

 PROCEDURE P1.2 
  3 + 4
 ; // P1.2

 PROCEDURE P1.3 
  5 + 6
 ; // P1.3
 
; // P1

FOR process ( IN anItem wordname wordparent anItem ) membersarray @ P1 WriteLn
// - в приведённом примере будут выведены строки P1 P1 P1

wordproducer - получает ссылку на слово и возвращает ссылку на слово, которое его породило.

Пример:

PROCEDURE P1

 PROCEDURE P1.1 
  1 + 2
 ; // P1.1

 PROCEDURE P1.2 
  3 + 4
 ; // P1.2

 PROCEDURE P1.3 
  5 + 6
 ; // P1.3
 
; // P1

FOR process ( IN anItem wordname wordproducer anItem ) membersarray @ P1 WriteLn
// - в приведённом примере будут выведены строки PROCEDURE PROCEDURE PROCEDURE

Аналогично можно перебирать входные параметры и результирующие значения.

А также - много чего ещё.

Понятно причём тут RTTI?

Кстати map-reduce (http://ru.wikipedia.org/wiki/MapReduce) и Свёртка списка (http://ru.wikipedia.org/wiki/%D0%A1%D0%B2%D1%91%D1%80%D1%82%D0%BA%D0%B0_%D1%81%D0%BF%D0%B8%D1%81%D0%BA%D0%B0) - не из той же ли серии, что я тут всё понаписал?

Ну и напоследок:

"Пользовательский массив":

FunctorToArray - принимает один параметр - функтор итерации, возвращает "отображение массива".

Пример:

ARRAY FUNCTION A
 Result := FunctorToArray ( FOR [ 1 2 3 4 5 6 7] YIELD )
;

FOR A WriteLn
// - будут напечатаны числа 1 2 3 4 5 6 7

Ну да... ну да... http://habrahabr.ru/post/132554/

Цитата:
"

Ваш лучший друг Itertools


Модуль itertools содержит специальные функции для работы с итерируемыми объектами. Желаете продублировать генератор? Соединить два генератора последовательно? Сгруппировать значения вложенных списков в одну строчку? Применить map или zip без создания ещё одного списка?

Просто добавьте import itertools."

:-)

Как всегда - "всё придумано до нас".
----------------------------------------------------------------------

(+) http://habrahabr.ru/post/85238/

(+)
numbers = [1,2,3,4,5]
squares = [number*number for number in numbers]

-- так я тоже могу.

Вот:
numbers = [ 1 2 3 4 ]
squares = [ for numbers (IN anItem anItem * anItem) ]

(+) http://ninjaside.info/blog/ru/funkcii-map-i-zip-i-lambda-python/

5 комментариев:

  1. map (* 2) [1.. 5]
    аналогичное отображение возможно?
    Вообще - не очень понятно, зачем всё это нужно. Зачем наворачивать скрипт, когда можно сделать функцию на delphi. Чисто спортивный интерес, или это на практике используется?

    ОтветитьУдалить
    Ответы
    1. FOR process ( IN anItem Result := anItem * 2 ) [ 1 2 3 4 5 ] WriteLn
      если я правильно всё понимаю

      конечно - не чисто спортивный, на "чисто спорт" - у меня нет времени

      Удалить
    2. должно получиться "отображение, как если бы элементы списка были удвоены". Консольный вывод не нужен, только отображение [Int] -> [Int]

      Удалить
    3. А подумать?

      ARRAY VAR B = process ( IN anItem Result := anItem * 2 ) [ 1 2 3 4 5 ]
      - отображение

      ARRAY VAR B = [ FOR process ( IN anItem Result := anItem * 2 ) [ 1 2 3 4 5 ] ( IN anItem anItem ) ]
      - копия

      Удалить
    4. пожалуй надо будет process в map переименовать

      Удалить