Главная страница
Навигация по странице:

  • 4.12.4.6. Транспонирование матрицы

  • 4.13. Ввод/вывод массива под управлением списка Управляемый списком В/В используется при работе с последовательными текстовыми файлами и стандартными устройствами 153

  • 5.1. Арифметические выражения

  • 158 5. Выражения, операции и присваивание Замечание.

  • современный фортран , Бортеньев. О. В. Бартеньев Современный Фортран


    Скачать 2.24 Mb.
    НазваниеО. В. Бартеньев Современный Фортран
    Анкорсовременный фортран , Бортеньев.pdf
    Дата28.05.2018
    Размер2.24 Mb.
    Формат файлаpdf
    Имя файласовременный фортран , Бортеньев.pdf
    ТипДокументы
    #19729
    страница17 из 49
    1   ...   13   14   15   16   17   18   19   20   ...   49
    151

    О. В. Бартеньев. Современный ФОРТРАН
    !
    2 2
    !
    3 3
    4.12.4.5. Функции сдвига массива
    CSHIFT(array, shift [, dim]) - выполняет циклический сдвиг массива ar-
    ray по заданному необязательному индексу dim.
    array - массив любого типа.
    shift - число позиций (INTEGER), на которое сдвигаются элементы array.
    Может быть целочисленным массивом, ранг которого на единицу меньше ранга array. Если shift - скаляр, то результат получается циклическим сдвигом каждого одномерного сечения по индексу dim на shift позиций.
    Если shift - массив, то каждый его элемент задает сдвиг для соответствующего сечения array. При этом форма shift должна совпадать с формой array за вычетом размерности dim. Положительный сдвиг выполняется в направлении уменьшения индексов (влево в случае вектора), и, наоборот, отрицательный сдвиг выполняется в направлении увеличения значений индексов массива (вправо в случае вектора).
    dim - необязательный параметр (INTEGER), задающий индекс, по которому выполняется сдвиг; 1 < dim < n, где n - ранг array. Если dim опущен, то сдвиг выполняется по первому индексу.
    Функция возвращает массив, в котором выполнен циклический сдвиг элементов, того же типа и формы, как и у array. Если ранг array больше единицы, то циклически сдвигается каждое одномерное сечение по заданному индексу dim или по первому индексу, если dim опущен.
    Пример: integer array (3, 3), ar1(3, 3), ar2 (3, 3) data array / 1, 4, 7, 2, 5, 8, 3, 6, 9 /
    ! Массив array: 1 2 3
    !
    4 5 6
    !
    7 8 9
    ! Сдвиг в каждом столбце на одну позицию ar1 = cshift(array, 1, dim = 1)
    ! Результат:
    4 5 6
    !
    7 8 9
    !
    1 2 3
    ! Сдвиг в первом ряду на -1, во втором - на 1 ar2=cshift(array, shift=(/-1, 1, 0/), dim = 2)
    ! Результат:
    3 1 2
    !
    5 6 4
    !
    7 8 9
    EOSHIFT(array, shift [, boundary] [, dim]) - выполняет вытесняющий левый или правый сдвиг по заданному необязательному индексу dim и
    152

    4. Массивы заполняет необязательными краевыми значениями образуемые в результате сдвига пропуски.
    array - массив любого типа.
    shift, dim - имеют тот же смысл, что и для функции CSHIFT.
    boundary - необязательный параметр того же типа, что и array. Задает значения, которыми заполняются возникающие в результате сдвига пропуски. Может быть массивом граничных значений, ранг которого на единицу меньше ранга array. Если boundary опущен, то задаваемые по умолчанию замены зависят от типа array: целый - 0; вещественный - 0.0; комплексный - (0.0, 0.0); логический - .FALSE.; символьный - пробел.
    Функция возвращает массив, в котором выполнены сдвиг и замены.
    Пример: integer shift(3) character(1) array(3, 3), ar1(3, 3) array = reshape((/'a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i'/), (/3, 3/))
    ! Массив array: a b c
    ! d e f
    ! g h i
    shift = (/-1, 1, 0/) ar1 = eoshift (array, shift, boundary = (/'*', '?', '#'/), dim = 2)
    ! Результат: * a b
    ! e f
    ?
    ! g h i
    4.12.4.6. Транспонирование матрицы
    TRANSPOSE(matrix) - меняет местами (транспонирует) столбцы и строки матрицы (двумерного массива) matrix. Тип и разновидность типа результирующего массива и matrix одинаковы. Если matrix имеет форму (k,
    n), то результат имеет форму (n, k).
    Пример: integer array(2, 3), result(3, 2) array = reshape((/1, 2, 3, 4, 5, 6/), (/2, 3/))
    ! Массив array: 1 3 5
    !
    2 4 6
    result = transpose(array)
    ! Результат: 1 2
    ! 3 4
    ! 5 6
    4.13. Ввод/вывод массива под управлением списка
    Управляемый списком
    В/В используется при работе с последовательными текстовыми файлами и стандартными устройствами
    153

    О. В. Бартеньев. Современный ФОРТРАН
    (клавиатура, экран, принтер). Преобразования В/В выполняются в соответствии с типами и значениями вводимых и выводимых величин.
    При вводе массива из файла возможны случаи:

    известно число вводимых данных;

    необходимо ввести весь файл, но его размер до ввода неизвестен.
    В последнем случае правильнее выполнять ввод в динамический массив.
    4.13.1. Ввод/вывод одномерного массива
    Рассмотрим пример В/В одномерного статического массива. integer, parameter :: nmax = 20 integer :: ios, n = 15, i
    ! Планируем ввести n значений character(60) :: fn = 'a.txt' real :: a(nmax) = 0 open(2, file = fn, status = 'old', iostat = ios) if(ios /= 0) then
    ! Останов в случае ошибки print *, 'Не могу открыть файл ' // trim(fn) stop end if if(n > nmax) stop 'Размер списка ввода больше размера массива' read(2, *, iostat = ios) (a(i), i = 1, n) if(ios /= 0) then write(*, *) 'Число введенных данных меньше заявленного.' n = i - 1
    ! n - число введенных значений if(eof(2)) backspace 2
    ! Позиционируем файл перед end if
    ! записью "конец файла" write(2, *, iostat = ios) (a(i), i = 1, n) close(2) !
    Закрываем файл write(*,'(1x, 5f5.2)') a(1:n)
    ! Контрольный вывод на экран end
    Состав файла a.txt (до выполнения оператора WRITE(2, *, ...):
    1.0 2.0 3.0 4.0 5.0
    Отображаемый на экране результат:
    Число введенных данных меньше заявленного.
    1.00 2.00 3.00 4.00 5.00
    Пояснения:
    1. Оператор OPEN открывает устройство В/В с номером 2 и подсоединяет к нему файл a.txt. При удачном подсоединении файл a.txt открыт. Далее в программе для доступа к файлу используется номер устройства.
    Параметр status = 'old' означает, что открываемый файл должен существовать. Параметр ios позволяет передать в программу код завершения выполнения оператора OPEN. Целочисленная переменная ios
    154

    4. Массивы равна нулю при успешном открытии файла и отлична от нуля, если возникла ошибка.
    После подсоединения файл позиционируется в свое начало. Файл a.txt открывается и для чтения и для записи. Доступ к файлу последовательный.
    2. Контроль ввода выполняется параметром ios: если нет ошибок ввода, то значение ios равно нулю; если достигнут конец файла, значение ios равно
    -1; ios больше нуля, если имели место иные ошибки. В нашем примере будет достигнут конец файла, однако параметр i циклического списка оператора READ сохраняет свое значение после завершения работы READ, что позволит подсчитать число введенных элементов. Правда, такой способ определения числа введенных элементов не сработает, если ввод данных будет прекращен при обнаружении в файле a.txt слеша (/), поскольку в этом случае ios = 0.
    3. Оператор ввода содержит циклический список (a(i), i = 1, n). Это позволяет прочитать первые n произвольно размещенных полей (если, конечно, не возникло ошибки ввода). Ввод выполняется с начала записи, и если оператор ввода должен прочитать больше полей, чем находится в текущей записи, то недостающие поля будут взяты из последующих записей. Каждый оператор READ (если не задан ввод без продвижения) начинает ввод с начала новой записи файла. Поэтому в случае применения цикла do i = 1, n read(2, *, iostat = ios) a(i) end do нам потребовалось бы расположить в текстовом файле каждое число на отдельной строке, т. е. в столбик, что выглядит весьма неуклюже.
    С появлением сечений циклический список (a(i), i = 1, n) можно заменить сечением a(1:n). В нашем примере сечение применено в операторе
    WRITE.
    Для ввода также можно применить оператор: read(2, *, iostat = ios) a
    ! или a(1:nmax)
    Он попытается ввести весь массив - передать из файла первые nmax полей.
    Однако если полей ввода меньше nmax, то при таком вводе уже нельзя вычислить число введенных данных.
    Вывод всего массива выполнит оператор write(*, *) a
    ! Вывод на экран
    4. Вывод массива в файл a.txt приведет к тому, что в файл начиная с новой строки (записи) будут переданы n элементов массива a. Строка, начиная с которой будут передаваться данные, определяется по правилу: если файл установлен на строке line, то новые данные будут добавляться начиная со строки line + 1. Причем, поскольку доступ к файлу
    155

    О. В. Бартеньев. Современный ФОРТРАН
    последовательный, все существующие после строки line данные будут "затерты" (заменены на вновь выводимые). В общем случае при управляемом списком выводе каждый оператор вывода создает одну запись, если длина создаваемой записи не превышает 79 символов. Число полей в созданной записи равно числу элементов в списке вывода. Если же для размещения элементов вывода требуется большее число символов, то создаются новые записи. В качестве разделителей между полями оператор
    WRITE использует пробелы.
    5. Оператор CLOSE(2) закрывает файл a.txt - отсоединяет файл от устройства 2.
    Рассмотрим теперь, как ввести весь файл или все его первые числовые поля в размещаемый массив. Прежде следует подсчитать, сколько данных можно ввести из файла, затем выделить под массив память, "перемотать" файл в его начало и ввести в массив данные. Например: integer, parameter :: nmax = 10000 integer, parameter :: unt = 3 integer :: ios, i, n character(60) :: fn='a.txt'
    ! Используем файл предыдущего примера real tmp
    ! Используем tmp для подсчета количества real, allocatable :: a(:)
    ! подряд идущих чисел в файле;
    !
    tmp
    имеет тот же тип, что и массив a open(unt, file = fn, status = 'old') read(unt, *, iostat = ios) (tmp, i = 1, nmax) if(ios == 0) stop 'Нельзя ввести весь файл' n = i - 1
    ! n - число вводимых данных allocate( a(n) )
    ! Выделяем память под массив a rewind unt
    ! Переход в начало файла read(unt, *) a
    ! Ввод массива a close(unt) !
    Закрываем файл write(*, '(1x, 5f5.2)') a(:n) deallocate(a) end
    4.13.2. Ввод/вывод двумерного массива
    Пусть надо ввести из файла b.txt данные в двумерный массив a(1:3, 1:4).
    Занесем в файл b.txt данные так, чтобы строка файла соответствовала одной строке массива a(1:3, 1:4):
    11 12 13 14 21 22 23 24 31 32 33 34
    Тогда ввод массива по строчкам можно выполнить в цикле do i = 1, 3 read(2, *, iostat = ios) (a(i, j), j = 1, 4)
    ! или a(i, 1:4) end do
    156

    4. Массивы
    Для ввода i-й строки массива вновь использован циклический список.
    В принципе при управляемом списком вводе двумерного массива можно использовать и вложенный циклический список: read(2, *, iostat = ios) ((a(i, j), j = 1, 4), i = 1, 3)
    Такой оператор также обеспечит ввод данных в массив a построчно
    (быстрее изменяется параметр j). Однако при этом вводимые данные не обязательно располагать в трех строчках, по 4 числа в каждой. Они могут быть размещены, например, так:
    11 12 13 14 21 22 23 24 31 32 33 34
    Если же расположение данных в файле соответствует их размещению в столбцах массива (т. е. их порядок в файле совпадает с порядком их размещения в памяти ЭВМ), например так:
    11 21 31 12 22 32 13 23 33 14 24 34 или так:
    11 21 31 12 22 32 13 23 33 14 24 34 то ввод всего массива можно выполнить оператором read(2, *, iostat = ios) a
    Контрольный вывод массива на экран по строкам организуется в цикле do i = 1, 3 write(*, *) (a(i, j), j = 1, 4)
    ! или a(i, 1:4) end do
    Замечание. В рассмотренных нами примерах к файлам был организован последовательный метод доступа, при котором возможно лишь чтение и запись данных. Редактирование отдельных записей файла становится возможным при прямом методе доступа (гл. 10).
    157

    5. Выражения, операции и присваивание
    В настоящей главе обобщаются сведения о выражениях и операциях.
    Операции применяются для создания выражений, которые затем используются в операторах Фортрана.
    Операции Фортрана разделяются на встроенные и задаваемые программистом (перегружаемые).
    Встроенные операции:

    арифметические;

    символьная операция конкатенации (объединение символьных строк);

    операции отношения;

    логические.
    Символьные выражения и операция конкатенации в этой главе не рассматриваются, поскольку подробно изложены в разд. 3.8.5.
    Часть арифметических действий реализована в Фортране в виде встроенных функций, например вычисление остатка от деления, усечение и округление, побитовые операции и др. Рассмотрение встроенных функций выполнено в следующей главе.
    Выражения подразделяются на скалярные и выражения-массивы.
    Результатом выражения-массива является массив или его сечение. По крайней мере одним из операндов выражения-массива должны быть массив или сечение массива, например: real :: a(5) = (/ (i, i = 1, 5) /) a(3:5) = a(1:3) * 2
    ! Возвращает (1.0 2.0 2.0 4.0 6.0)
    5.1. Арифметические выражения
    Результатом арифметического выражения может быть величина целого, или вещественного, или комплексного типа или массив (сечение) одного из этих типов. Операндами арифметического выражения могут быть:

    арифметические константы;

    скалярные числовые переменные;

    числовые массивы и их сечения;

    вызовы функций целого, вещественного и комплексного типа.
    5.1.1. Выполнение арифметических операций
    Арифметические операции различаются приоритетом:
    ** возведение в степень (операция с наивысшим приоритетом);
    *, / умножение, деление; одноместные (унарные) + и -;
    +, - сложение, вычитание.
    158

    5. Выражения, операции и присваивание
    Замечание. В Фортране в отличие от СИ унарным операциям не может предшествовать знак другой операции, например: k = 12 / -a
    ! Ошибка k = 12 / ( -a )
    ! Правильно
    Операции Фортрана, кроме возведения в степень, выполняются слева
    направов соответствии с приоритетом. Операции возведения в степень выполняются справаналево.Так, выражение -a + b + c будет выполнено в следующем порядке: (((-a) + b) + c). А выражение a**b**c вычисляется так:
    (a**(b**c)). Заключенные в круглые скобки подвыражения вычисляются в первую очередь.
    Пример: k = 2 * 2 ** 2 / 2 / 2
    ! 2
    ! Проиллюстрируем последовательность вычислений, расставив скобки: k = ((2 * (2 ** 2)) / 2) / 2
    ! 2
    ! Проиллюстрируем влияние скобок на результат выражения k = 2 ** 8 / 2 + 2
    ! 130 k = 2 ** (8 / 2 + 2)
    ! 64 k = 2 ** (8 / (2 + 2))
    ! 4
    В арифметических выражениях запрещается:

    делить на нуль;

    возводить равный нулю операнд в отрицательную или нулевую степень;

    возводить отрицательный операнд в нецелочисленную степень.
    Пример: a = (-2)**2.2
    ! Ошибка - нарушение последнего ограничения
    5.1.2. Целочисленное деление
    Рассмотрим простую программу: real(4) dp, dn dp = 3 / 2 dn = -3 / 2 print *, dp, dn
    ! 1.0 -1.0 end
    Программисты, впервые наблюдающие целочисленное деление, будут удивлены, увидев в качестве результата 1.0 и -1.0 вместо ожидаемых ими
    1.5 и -1.5. Однако результат имеет простое объяснение: 3, -3 и 2 - целые числа и результатом деления будут также целые числа - целая часть числа
    1.5 и целая часть числа -1.5, т. е. 1 и -1. Затем, поскольку переменные
    dp и dn имеют тип REAL(4), целые числа 1 и -1 будут преобразованы в стандартный вещественный тип.
    159

    О. В. Бартеньев. Современный ФОРТРАН
    Чтобы получить ожидаемый с позиции обычной арифметики результат, в программе можно записать: dp = 2.0/3.0 или dp = 2/3.0, или dp = 2.0/3
    Можно также воспользоваться функциями явного преобразования целого типа данных в вещественный (разд. 6.5) и записать, например, d = float(2)/float(3) или d = real(2)/real(3)
    Еще несколько примеров целочисленного деления:
    2 ** (-2) возвращает 0 (целочисленное деление);
    2.0 ** (-2) возвращает 0.25 (нет целочисленного деления);
    -7/3 возвращает -2;
    19/10 возвращает 1;
    1/4 + 1/4 возвращает 0.
    5.1.3. Ранг и типы арифметических операндов
    В Фортране допускается использовать в арифметическом выражении операнды разных типов и разновидностей типов. В таком случае результат каждой операции выражения определяется по следующим правилам:

    если операнды арифметической операции имеют один и тот же тип, то результат операции имеет тот же тип. Это правило хорошо иллюстрируется целочисленным делением;

    если операнды операции имеют различный тип, то результат операции имеет тип операнда наивысшего ранга.
    Ранг типов арифметических операндов (дан в порядке убывания):
    COMPLEX (8) или DOUBLE COMPLEX - наивысший ранг;
    COMPLEX(4);
    REAL(8) илиDOUBLE PRECISION;
    REAL(4) илиREAL;
    INTEGER(4) илиINTEGER;
    INTEGER(2);
    INTEGER(1) или BYTE - низший ранг.
    Пример: integer(2) :: a = 1, b = 2 real(4) :: c = 2.5 real(8) d1, d2 d1 = a / b * c
    ! 0.0_8 d2 = a / (b * c)
    ! 0.2_8
    При вычислении d2 первоначально выполняется операция умножения, но прежде число 2 типа INTEGER(2) переводится в тип REAL(4). Далее
    160

    5. Выражения, операции и присваивание выполняется операция деления, и вновь ее предваряет преобразование типов: число 1 типа INTEGER(2) переводится в тип REAL(4). Выражение возвращает 0.2 типа REAL(4), которое, однако, в результате присваивания преобразовывается в тип REAL(8). При этом типы операндов выражения - переменных a и b, разумеется, сохраняются.
    В ряде случаев в выражениях, например вещественного типа с целочисленными операндами, чтобы избежать целочисленного деления, следует выполнять явное преобразование типов данных, например: integer :: a = 8, b = 3 real c c = 2.0**(a / b)
    ! 4.0 c = 2.0**(float(a) / float(b))
    ! 6.349604
    Встроенные математические функции, обладающие специфическими именами, требуют точного задания типа аргумента, например: real(4) :: a = 4 real(8) :: b = 4, x x = dsqrt(a)
    ! Ошибка: тип параметра должен быть REAL(8) x = dsqrt(b)
    ! Верно
    Перевод числа к большей разновидности типа, например от REAL(4) к REAL(8), может привести к искажению точности, например: real(4) :: a = 1.11 real(8) :: c c = a print *, a
    ! 1.110000 print *, c
    ! 1.110000014305115
    В то же время если сразу начать работу с типом REAL(8), то точность сохраняется, например: real(8) :: c c = 1.11_8
    ! или c = 1.11d0 print *, c
    ! 1.110000000000000
    Искажение значения может произойти и при переходе к низшей разновидности типа, например: integer(2) :: k2 = 325 integer(1) :: k1
    ! -128 <= k1 <= 127 k1 = k2 print *, k2
    ! 325 print *, k1
    ! 69
    5.1.4. Ошибки округления
    Необходимо учитывать, что арифметические выражения с вещественными и комплексными операндами вычисляются неточно, т. е. при их вычислении возникает ошибка округления. В ряде случаев
    1   ...   13   14   15   16   17   18   19   20   ...   49


    написать администратору сайта