J для смертных. Массивы

Предыдущая статья цикла J для смертных. Тацитное программирование

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

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

Массивы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

> /: 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 случайных вещественных чисел, затем к результату применяется хук из глаголов «{~» и «/:».

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

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

> ]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-го элемента каждой строки левого операнда.

Следующая статья цикла J для смертных. Коробки и циклы

19 октября 2013