S023: Массивы
--------------------------------------------------------------------------------
Массив -- структура данных, состоящая из множества переменных. С помощью массива
описываются величины, получаемые с датчиков, например измерения температуры,
выполненные через фиксированный интервал времени:
C(++)
float temp[]={ +20.4, +18.6, +10.3, +5.0, +1.2, -0.3, -2.7 };
int undef[12]; // неинициализирвоанный массив из 13 элементов (0..12)
Python
temp = [ +20.4, +18.6, +10.3, +5.0, +1.2, -0.3, -2.7 ]
print temp[3]
Это примеры одномерных массивов, или векторов.
Каждый элемент вектора адресуется целым неотрицательным числом от 0 до длина_массива-1
-- индексом массива.
Массив поддерживает операции чтения и записи элемента:
C(++)
printf("T(3)=%f\n",temp[3]); // чтение элемента массива
T[5]=+12.6; // запись элемента массива
Python
print temp[3]
t[5]=+12.6
Массивы могут быть и многомерными, например распределение температуры по поверхности
(измерения в узлах прямоугольной сетки):
C(++)
float temp[4][4]={
{ 1, 2, 3, 4 },
{ 1, 2, 4, 4 },
{ 1, 3, 4, 6 },
{ 1, 3, 6, 7 }
};
Python
temp=[
[ 1, 2, 3, 4 ],
[ 1, 2, 4, 4 ],
[ 1, 3, 4, 6 ],
[ 1, 3, 6, 7 ]
]
Форт
Стандартный Форт не поддерживает массивов, и если вы хотите работать с ними в
Форте, вам будет необходимо расширить язык, определив свои слова для работы с
массивами.
Прежде всего нужно решить, как вы будете хранить массивы в памяти. Например,
матрицы 4x4 с float элементами размером 32 бита:
a00 a10 a20 a30
...
a03 a13 a23 a33
будем хранить в памяти линейно:
a00 a10 a20 a30 ... a03 a13 a23 a33
направление увеличения адресов -->
Один floating элемент имеет размер в байтах
4 CONSTANT FCELL
Для операций с плавающей точкой используем отдельный аппаратный стек сопроцессора
287..487 или модуль FPU процессора Pentium.
Для загрузки чисел из памяти на стек FPU (F-стек) и обратно используем слова,
определенные на ассемблере:
F@ (D: addr -- ) (F: -- f )
читает 32-битное float из памяти форт-системы с адреса addr на стек FPU
F! (D: addr -- ) (F: f -- )
записывает float с вершины стека FPU в память с адреса addr
D: состояние стека данных
F: состояние стека FPU
Компиляция матрицы:
CREATE filter
S" +0.5" F, S" +0.5" F, S" +1.0" F, S" +1.0" F,
S" +1.0" F, S" +1.0" F, S" +1.0" F, S" +1.0" F,
S" +1.0" F, S" +1.0" F, S" +1.0" F, S" -0.5" F,
S" +1.0" F, S" +1.0" F, S" -0.5" F, S" -0.5" F,
Слово CREATE создаст слово filter, которое при исполнении будет класть в стек
данных адрес элемента a00, который одновременно является адресом всей матрицы.
В Форте желательно придерживаться правила: если объект не умещается в одной ячейке
стека данных, то он размещается в памяти, а для работы с объектом используется
указатель на него -- адрес первого байта объекта. Например это строки по стандарту
FORTH-83 (но не FORTH-94).
Аналогично вместо адреса памяти можно использовать другой идентификатор, например
номер сектора на диске, если объект полностью умещается (и хранится) в этом секторе.
Таким образом, если на веришине стека данных лежит некоторое (беззнаковое) целое
число matrix, оно является уникальным идентификатором матрицы (в нашем случае
адресом первого байта).
Для чтения/записи элементов матрицы по их координатам нам нужно определить слово
4x4[] ( i j matrix -- addr )
которое будет возвращать адрес элемента aij. Этот адрес мы будем использовать
для чтения элемента на стек FPU словом F@ и записи элемента со стека FPU F!.
4 CONSTANT WIDTH \ длина одной строки матрицы
: 4x4[] ( i j matrix -- addr )
\ пересчитать координаты i,j элемента матрицы хранящейся в памяти начиная
\ с адреса matrix, в адрес этого элемента
-ROT ( matrix i j )
WIDTH * ( maxtrix i j*4 )
+ ( matrix j*4+i )
\ j*4+i -- пересчет 2D координаты (i;j) в 1D индекс
FCELL * ( matrix (j*4+i)*4 )
\ (j*4+i)*4 -- преобразуем индекс в смещение элемента aij в байтах
\ относительно начала матрицы
+ ( addr )
\ прибавляя смещение к адресу матрицы, получаем абсолютный адрес aij
;
Для удобства определим слова
: 4x4@ ( D: i j matrix -- ) ( F: -- f )
\ чтение элемента aij матрицы matrix сразу на стек FPU
4x4[] F@
;
: 4x4! ( D: i j matrix -- ) ( F: f -- )
\ запись aij со стека FPU в матрицу matrix
4x4[] F!
;
Далее вы можете самостоятельно написать слова для работы с такими матрицами:
вывод матрицы (используя циклы со счетчиком), транспонирование, сложение, умножение
матриц и т.п.
На этом примере вы видете, как используя слова базового Форта (низкоуровневого
языка) и его свойство расширяемости можно получить высокоуровневый язык (расширение
Форта) для ваших задач.
Задание
Напишите расширение Форта для работы со строками, не пользуясь строковыми словами
вашей Форт-системы. Варианты хранения строк в памяти:
счетная строка: первый байт -- длина строки (0..255), остальные байты -- символы
ASCII
ASCIIZ строка: байты -- символы ASCII, последний байт 0 -- конец строки (формат
хранения строк в стандартном C)
строка FVM: первый cell -- длина строки (0..65535 для 16-битной FVM), остальные
cellы -- символы unicode
Если ваша Форт-система поддерживает динамическое распределение памяти (аналог
malloc/realloc/free в С), храните строки в блоках динамической памяти.