...

воскресенье, 20 октября 2013 г.

Язык программирования J. Взгляд любителя. Часть 3. Массивы

Предыдущая статья цикла Язык программирования J. Взгляд любителя. Часть 2. Тацитное программирование

«Я не думаю, что он нам подходит. Я рассказал ему, чем мы занимаемся, и он не стал спорить. Он просто слушал.»

Кен Айверсон после одного из собеседований



1. Массивы


J – язык для обработки массивов. Для создания массивов в J есть множество способов. Например:



  • «$» — этот глагол возвращает массив, размерность которого указывается в левом операнде, а содержимое — в правом. Создадим массив заданной размерности, все элементы которого одинаковы:

    3 $ 1 NB. создаем вектор с тремя элементами, каждый из которых = 1
    1 1 1
    2 3 $ 2 NB. создаем матрицу из 2 строк и 3 столбцов, все элементы которой = 2
    2 2 2
    2 2 2






    Можно также правым операндом указывать произвольный вектор, элементы которого будут последовательно копироваться в результирующий массив:

    2 3 $ 1 2 3
    1 2 3
    1 2 3
    2 3 $ 1 2
    1 2 1
    2 1 2
    2 3 $ 1 2 3 4
    1 2 3
    4 1 2




  • «#» — в диадном варианте глагол копирования. Копирует i-й элемент правого операнда столько раз, сколько указано в i-м элементе левого операнда. Таким образом, длина результирующего массива равна сумме элементов x. Пример:

    1 2 0 3 # 1 2 3 4
    1 2 2 4 4 4
    4 # 1
    1 1 1 1




  • «i.» создает перечисления и таблицы. В монадном вызове возвращает массив, составленный из целых чисел (начиная с нуля), каждое из которых больше предыдущего на единицу. Длина такого массива задается правым операндом. Если значение операнда отрицательно, то числа в результирующем массиве идут в обратном порядке:

    i.4 NB. пробел между глаголом и операндом необязателен
    0 1 2 3
    i._4
    3 2 1 0
    i.2 3
    0 1 2
    3 4 5







Обратите внимание на последний пример — глагол «i.»возвратил нам двумерный массив, т.к. переданный ему операнд был вектором. Первый элемент операнда указывает на число строк, второй — столбцов. Впрочем, с помощью этого глагола можно получить и n-мерный массив. Например, трехмерный:

i.2 _2 3
3 4 5
0 1 2
9 10 11
6 7 8


Строки в результирующем массиве идут в обратном порядке, т.к. вторая размерность задана отрицательным числом. Подробней о многомерных массивах мы поговорим в следующем разделе.


В связи с тем, что многие стандартные глаголы J способны обрабатывать массивы данных, можно расширить описанные ранее глаголы на случай массивов.



- 1 2 3
_1 _2 _3


Диадный случай:



1 2 3 - 3 2 1
_2 0 2


Кроме того, допустимы и операции над разноранговыми значениями:



1 2 3 - 1
0 1 2


Тот же результат можно получить и с помощью глагола <:



<: 1 2 3
0 1 2


Допустима и обратная операция:



1 - 1 2 3
0 _1 _2


Напомним, что последнее выражение можно записать и как «1 2 3 -~ 1».


Кроме стандартных арифметических глаголов в нашей работе пригодится глагол-генератор псевдослучайных чисел «?». Будучи вызванным с одним операндом «?» возвращает:



  • случайное число с плавающей точкой, если операнд равен нулю;

  • случайное целое число в промежутке от нуля до y, если операнд равен y.



? 0 NB. конечно, у вас результат будет отличаться
0.622471
? 3 NB. вернет случайное число на отрезке [0;2]
2
? 3
0


Установить датчик псевдослучайных чисел можно с помощью вызова



9!:1 y


Начальное значение seed = 7^5.


Как и во всех примерах ранее в этом разделе, глагол «?» можно вызывать с операндом-массивом — результатом будет массив той же размерности, каждым i-м элементом которого будет случайное число на интервале, заданным i-м элементом операнда:



? 0 10 100
0.429769 7 95


Глагол «?», разумеется, работает не только с векторами, но и, например, с матрицами:



? (2 2 $ 0 10)
0.084712 4
0.840877 1


2. Части речи для работы с массивами


Как мы уже знаем, J — язык для обработки массивов. Выражается это, не в последнюю очередь, в том, что при работе с массивами вам практически не придется пользоваться явными итерационными процедурами.


Предположим, вам необходимо найти сумму всех элементов последовательности на подмножестве языка Python. Обобщим эту задачу до стандартной функции свертки:



def reduce(xs, f, acc = 0):
"""Пример запуска:
>>> reduce([1,2,3], lambda acc,x: acc + x)
6"""
for x in xs:
acc = f(acc,x)
return acc


Для решения таких задач в J есть специальное наречие «/», называемое «между». Действительно, наша функция на питоне эквивалентна следующему выражению «x0 f x1 f … f xN». В терминах J это записывается как «f/ xs», где xs — существительное(вектор), f — глагол, который вставляется «между» элементами существительного xs, «/» — наречие, которое, собственно и осуществляет такое преобразование. Приведем пример:



+/ 1 2 3 NB. аналогично «1 + 2 + 3»
6
-/ i.3 NB. аналогично 0 - (1 - 2)
1


А что, если нам надо возвратить в результате свертки не только конечный результат вычислений, но и все промежуточные результаты (в контексте исходного кода на Python — все промежуточные значения переменной «acc»)? Т.е., например, для вектора «1 2 3 4» после применения глагола «+»«между» ожидается получить «1 3 6 10».


В J для этой цели есть специальное наречие «\»:



+/\ 1 2 3 4 NB. эквивалентно выражению: (1) , (1+2), (1+2+3), (1+2+3+4)
1 3 6 10
-/\ 0 1 2 NB. эквивалентно выражению: (0), (0-1), (0-(1-2))
0 _1 1


Другие необходимые глаголы — это «/:» и «\:», которые сортируют переданный вектор по возрастанию и по убыванию соответственно. Причем результатом сортировки является вектор из индексов отсортированных элементов:



/: 1 3 2
0 2 1
\: 1 3 2
1 2 0


Для того, чтобы получить по указанным индексам элементы массива воспользуемся глаголом «{», который извлекает элементы из массива (правый операнд) по указанным индексам (левый операнд). Например:



1 0 1 2 { 11 22 33 44
22 11 22 33


Другими глаголами для «ручного» индексирования элементов массива являются



  • «}.» возвращает «хвост»массива, т.е. все элементы кроме первого.

  • «{.» возвращает «голову»массива, т.е. первый элемент массива.

  • «{:» возвращает последний элемент массива.

  • «}:» возвращает все элементы массива кроме последнего.


Вспомним наречие «~», которое меняет в вызове правый и левый операнд местами, и приведем несколько более сложный пример:



({~ /:) (? (5 $ 0))
0.221507 0.293786 0.691701 0.72826 0.839186


В данном примере генерируется последовательность из 5 случайных вещественных чисел, затем к результату применяется хук из глаголов «{~» и «/:».


3. Многомерные массивы и ранги




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

]x =: i.2 3 NB. глагол ] возвращает свой правый операнд, т.е. в данном случае переменную x
0 1 2
3 4 5
x + 10 20 30
|length error
x + 10 20
10 11 12
23 24 25


Как мы видим, если выполнить стандартный глагол над матрицей и вектором, то по умолчанию будет выполняться действие «по столбцам». В примере «10» прибавляется к элементу на первой строке каждого столбца, а «20» — на второй строке. Размерность массива в примере 2 на 3. Будем говорить тогда, что первый ранг этого массива равен 2, а второй ранг равен 3. Раз по умолчанию глагол применяется к столбцам матрицы, то можно сказать, что он применяется по второму рангу.


Это общее правило для J — глаголы по умолчанию применяются к крайнему рангу массива. Для того, чтобы явно указать ранг глагола используется специальный союз «"» (двойные кавычки), который левым операндом принимает глагол, правым — ранг (целое число). Например:



(i.2 3) + 10 20
10 11 12
23 24 25
(i.2 3) +"2 (10 20)
10 11 12
23 24 25


Как видим, эти два выражения эквивалентны. Обратите внимание на скобки вокруг вектора (10 20). Если их не поставить, то транслятор J будет считать, что ранг глагола равен «2 10 20», а правый операнд у глагола не указан. Чтобы явно не указывать ранг глагола, рекомендуется использовать знак бесконечности «_»:



(i.2 3) +"_ (10 20)
10 11 12
23 24 25


Результат от этого не изменится. Если же поменять ранг глагола на 1:



(i.2 3) +"1 (10 20)
|length error


Ошибка данного выражения заключается в том, что мы пытаемся применить глагол суммирования к вектору длиной 2 (правый операнд) и к вектору-строке левого операнда длиной 3. Изменим немного наш пример:



(i.2 3) +"1 (10 20 30)
10 21 32
13 24 35


Результат соответствует последовательному суммированию i-го элемента правого операнда и каждого i-го элемента каждой строки левого операнда.


Продолжение следует...


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий