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

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

ссылка на оригинал статьи http://habrahabr.ru/post/198228/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *