Массивы

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

В таких случаях необходимо использовать массив. Массив можно представить как "большую" переменную, состоящую из нескольких ячеек. Ячейки называются элементами массива. Каждый элемент имеет одинаковый тип, но может хранить разные значения. Все элементы имеют одно имя - имя массива, но отличаются номерами.

Например, можно создать массив с именем Ar, состоящий из 10 элементов целого типа. Для этого надо воспользоваться оператором описания типа:

integer Ar(10)

Компилятор, "наткнувшись" на этот оператор, выделит память под 10 ячеек для целых значений.

Далее предположим, что мы хотим присвоить каждому элементу массива его номер (то есть положить в каждую ячейку значение, равное номеру ячейки: в 1-ую ячейку - число 1, во 2-ую - число 2 и т.д.). Вместо того чтобы писать 10 операторов присваивания


  Ar(1)=1
  Ar(2)=2
  ....
можно воспользоваться циклом:
 
  do i=1,10
  	    Ar(i)=i
  end do
  

Другие примеры описания массивов:


    integer m1,a(5),ff(25,7) ! одномерный массив a из 5 элементов 
        ! целого типа, двухмерный массив ff протяженностью 25 по первому измерению 
        ! и 7 - по второму, всего - 25*7 элементов целого типа. 
	real a3(5),a5
	real ,dimension(5) ::a1,a2		! dimension - спецификатор размерности
	dimension a5(5)  !	Объявлены одинаковые массивы a1,a2,a3,a5.

Ни один атрибут не может быть специфицирован для объекта более 1 раза:


	Real a(5)  ! нельзя !
	Dimension a(5)

Если размерность массива описана и в dimension и в объявлении объекта, то последнее перекрывает предыдущее:

Задание конфигурации – [нижняя :] верхняя граница – определяют протяженность по какому-то измерению. Граница может быть отрицательной, равной 0 или положительной. Если не указана нижняя граница, то она равна 1. Если lowbound>upperbbound – размер равен 0 и массив не создается.


     real ,dimension(-5:5):: s1,s(10) 
                    ! 11 элементов в s1, начинается с -5 элемента: s1(-5),…s1(5), 
                    ! но 10 elements в s(1)…s(10)  

Определение массива и его свойств в стандарте языка Fortran(3):

Массив – набор скалярных данных одного типа и длины, индивидуальные элементы которого размещены по прямоугольной схеме. Элемент массива – один из индивидуальных элементов массива и является скаляром. Секция массива – поднабор (подмножество) массива, которое само является массивом.

Массив может иметь до 7 измерений и любую протяженность (кол-во элементов) по любому измерению.Размерность массива – кол-во его измерений, размер – общее кол-во элементов массива, равное произведению протяженностейпо всем измерениям.

М может иметь нулевой размер.

Все массивы должны быть объявлены, и размерность определяется в объявлении (описании).

Два М согласованы, если у них одинаковая конфигурация. Скаляр согласован с любым массивом. И все операции, определенные для скаляров, определены для согласованных массивов. Такие операции выполняются поэлементно. При этом интересно отметить, что эти поэлементные операции могут выполняться в любом порядке или одновременно!

Объекты-массивы могут быть любого встроенного типа.

Использование массивов

Полный массив – имя переменной (или именованная константы), которая является массивом. Появление полного массива в выполняемом операторе специфицирует выполнение операции со всеми элементами массива.

Например:

  Integer a(5)
  A=7   ! всем элементам массива присваивается 7
  A=a-2 ! всем элементам массива присваивается 5
  

Индекс – скалярное выражение.
Индекс секции – скалярное выражение или индексный триплет или векторный индекс.
Индексный триплет - [индекс] : [индекс] [ : шаг ].
шаг – скалярное выражение,
векторный индекс – целое выражение,
Элемент массива – скаляр. Секция массива – массив.

Например, дано описание:

 
  	real A (10,10) 
	integer B (5, 5) 
тогда A (1, 2) – элемент, A(1:N:2,M) – вектор (одномерная секция) – элементы с индексами (1,m),(3,m)(5,m)…(n,m); B (2:5) – двумерный подмассив.
Предположим, массив описан следующим оператором:
 integer A (5, 4, 3) 
Секция A (3 :5, 2, 1 : 2) есть массив (3, 2):

  A(3, 2, 1)   A (3, 2, 2)
  A(4, 2, 1)   A (4, 2, 2)
  A(5, 2, 1)   A (5, 2, 2)
  

Например, если описание массива B (10), секция B (9 :1 : –2) есть массив размера (5), элементы которго B (9), B (7), B (5), B (3), и B (1), в этом порядке


Одиночная переменная


Массив

Чем еще важен массив? Работа с массивами дает возможность выполнять вычисления параллельно на ЭВМ, имеющих несколько процессоров.

Конструктор массива служит для его инициализации:

 REAL :: d(100)=(/REAL(i)=1,100/) 
 INTEGER:: a(6)=(/1,2,3,6,7,8/)
 

Ввод/вывод массива

Ввод/вывод элементов массива можно осуществлять 3-мя способами:

  1. read *,a - ввод всех элементов массива (согласно его описанию)
  2. read*,(a(i),i=1,n) - ввод элементов массива с номерами 1…n
  3. do i=1,n
       read*,a(i)
    end do
    
Последний способ интересен тем, что позволяет осуществлять контроль за вводом выводом каждого элемента массива.

Пример: Поиск элемента в массиве


program tst1
 !неверный вариант!
 implicit none
 !поиск числа в массиве
 real a(10)
 integer n,1,b
 do i=1,10
   read *,a  !- is too simple but not good
 end do
 n=0 
 do i=1,10
  if(a(i)==b) then
   n=i
  end if
 end do
 if (n>0) then 
  print *,b,' is in element number ',n
 else 
  print *,'there are no element == ',b
 end if
end program tst1

Какие ошибки в этом варианте реализации?
1) При чтении элементов массива отсутствуют скобки в операторе чтения – в результате массив будет вводиться 10*10 раз!
Правильно: READ *,a(i)
2) Неизвестно что ищем – в переменной b не содержится никакого определенного значения. Его надо ввести.

print*,'b=?'
read *,b
3) Логическая операция ==, примененная для вещественных чисел, может дать неожиданный результат из-за неточности (вернее, ограниченной точности) хранения значения (вместо 5. может быть 4.9999). Поэтому можно ввести некий допуск на совпадение a(i)-b/<eps. А так как мы не знаем, в какую сторону «качнется» погрешность вещественного числа, необходимо взять модуль от разности с помощью встроенной функции abs():
if(abs(a(i)-b<eps) then

4) Программа тихо умирает, не сообщив о диагнозе! – Это глупость проектирования программы.


program tst1
  ! более правильный вариант J
implicit none
  !  поиск числа в массиве
 real a(10),b,eps
 integer n,i
 eps=0.0001 ! например
  ! read *,a - is too simple but not good
 do i=1,10 
   print *,'enter a(',i,')' 
   read *,a(i)
 end do
 print *,'b=?'
 read *,b
 
 n=0 
 do i=1,10
  if(abs(a(i)-b)<eps)  n=i
 end do

 if (n>0) then 
   print *,b,' is in element number ',n
 else 
   print *,'there are no element == ',b
 end if
end program tst1

Теперь более тонкий момент: смешение вещественного и целого в одной операции приводит к появлению нескольких дополнительных действий (команд) процессора. Так, оператор if () дает 16 или 18 команд ассемблера для случаев сравнения однотипных и разнотипных данных.

В чем нелогичность данной программы? Если в массиве присутствует более 1 искомого числа, то программа выведет последнее из них. Можно переделать на поиск первого. Для этого достаточно добавить внутри проверки на равенство выход из цикла с помощью оператора EXIT:


if(abs(a(i)-b)<eps) then
  n=I
  exit
end if

Проблема в том, что изначально поставленная формулировка задачи была неполной! Это вызывает проблемы и неоднозначности при проектировании программы.

Достаточно интересная возможность инициализировать значения переменных-массивов (инициализировать – задавать начальные значения) :

 
 integer ar(10)
 ar=(/10,-5,(i,i=1,5),4,-3,0/)
В массиве ar будут содержаться следующие числа:
10 -5  1 2 3 4 5  4 -3 0

Пример:

Поиск элемента в упорядоченном массиве – бинарный поиск.

Нахождение макс/мин в матрице.

В реальных программах целесообразно использовать не статические, а динамические массивы.

Повторение:

Переменная – простой ящик, массив – ящик со множеством отделений.

НА ГЛАВНУЮ ДАЛЕЕ
Hosted by uCoz