О Fortran...

  1. История и судьба этого языка программирования уникальны. Появившийся раньше множества уже безвозвратно ушедших в прошлое высокоуровневых языков, Фортран живёт и здравствует и теперь. В чём секрет подобного успеха? Сказать сходу сложно, но давайте попытаемся разобраться с тем, каков Фортран был, есть и будет, по порядку.

    Фортранова юность

    Фортран был первым из созданных человечеством языков программирования высокого уровня, поэтому путь его создателя был ох как тернист и непрост! Но Джон Бэкус, взявшийся за эту непростую задачу, с блеском довёл её до конца и мог по праву гордиться своим детищем до самой своей смерти, которая, кстати, произошла не так уж давно - в марте 2007 года. Конечно, грустно, что такой выдающийся человек уже покинул этот мир, но, с другой стороны, за 82 года жизни он успел не так уж и мало. Но биография Бэкуса - хорошая тема для отдельной статьи, а сейчас всё же о Фортране.
    Перед группой, создавшей Фортран (именно ею руководил Джон Бэкус), стояла очень непростая задача. Им нужно было не просто разработать высокоуровневый язык программирования, но и реализовать его в виде компилятора. Причём всё это должно было быть достаточно удобным в использовании, и, самое главное, быстрым, чтобы новый язык мог составить конкуренцию Ассемблеру на тогдашних маломощных машинах.
    Основная задача языка состояла в упрощении кодирования формул, откуда, собственно, и берётся его название: Fortran - это сокращение словосочетания "Formula translator". Что ж, следует признать, что это Бэкусу и его подчинённым удалось и было большим прорывом. Запись формул на Ассемблере, даже с использованием всяческих макросов, дело неблагодарное, нудное, да и код получается весьма громоздким. А если ещё и формулы восьмиэтажные... В общем, здесь полезность Фортрана было сложно переоценить.
    Эффект простоты записи формул в Фортране состоял в том, что этот язык моментально стал популярен в научной среде. Ведь кто больше всех нуждается в удобной записи математических формул? Именно учёные - математики, физики, ну и в чуть меньшей степени астрономы, химики и биологи, а также все остальные. Именно в построении компьютерных моделей Фортран тогда нашёл своё применение быстрее всего. Ведь профессиональным программистам свойственна о-о-очень большая инертность, которая является весьма весомым фактором торможения прогресса. Учёным же не нужно было цепляться за старые технологии, обеспечивающие им хлеб насущный, а потому они с радостью приняли новый инструмент, который пригодился им во многих исследованиях.
    Что характерно, в СССР Фортран появился гораздо позже, чем на Западе - даже позже, чем Алгол, который, кстати, и задержал появление Фортрана на просторах одной шестой части суши. Но помогли Фортрану всё те же учёные. Для науки "железный занавес" всё-таки был с дырками, и сотрудничать с европейскими коллегами советские учёные вполне могли. А как сотрудничать людям, говорящим на разных языках? Поэтому советская, а после и белорусская (как, впрочем, и российская) наука "заговорила" на Фортране. Первый советский компилятор языка, кстати, был создан для компьютера "Минск-2".
    Но, как бы интересна ни была история Фортрана, думаю, читателям будет интересно узнать и о том, что же такое представлял (и представляет) собою Фортран, если сумел завоевать такое всемирное признание.

    Язык собственной персоной

    Вряд ли среди читателей этой статьи много тех, кто программировал на Фортране с самого его появления в Советском Союзе. Тогда Фортран был совершенно иным, нежели теперь: ведь пользователи работали не с мониторами и клавиатурами, а с перфокартами и телетайпами. Именно с тех смутных времён первые шесть позиций в каждой программе на Фортране отдаются традиционно под обозначения комментария и метки. Комментарий - это, соответственно, когда строка начинается с латинского "с", а метки - это знаменитые goto, которые делают код программы своего рода головоломкой.
    Впрочем, для современных стандартов Фортрана, которым следуют современные же компиляторы, это всё уже не слишком актуально, поскольку язык давно уже позволяет использовать первые пять позиций строки так, как заблагорассудится разработчику, а необходимость в метках отпала в связи с введением в язык операторов циклов, условных переходов и выбора, которые делают код программы на порядок более читаемым. Впрочем, нельзя сказать, что от меток (т.е. операторов безусловного перехода), которыми славились издавна программы на Фортране, нет сейчас совсем никакой пользы. Именно Фортран, пожалуй, дал повод создателям языка Java вовсе исключить из него оператор безусловного перехода.
    Пример программы на современном Фортране я, конечно же, приведу, и, более того, сделаю это прямо сейчас. Сначала я подумал, что неплохо было бы даже объявить конкурс на то, кто раньше разберётся с тем, что делает данная программа, но потом подумал, что полезнее будет заранее рассказать это, чтобы те, кто не знаком с Фортраном, смогли лучше познакомиться с этим языком. Приведённый ниже код - совершенно самостоятельная программа, решающая линейные алгебраические уравнения.
    Код:
    Write (*,*)'AX=B, X=C'
    write (*,*)'Please, enter A'
    read (*,*)a
    write (*,*)'Please, enter B'
    read (*,*)b
    c = b/a
    if (a .ne. 0)then
    write (*,*)'Take the result, please... ', c
    elseif (a .eq. 0)then
    write (*,*)'No results found, sorry...'
    end if
    pause
    stop
    end
    Как видно из этого примера, Фортран - регистронезависимый язык, т.е. А и а в Фортране - одна и та же переменная. Кроме того, в нём присутствует соглашение по умолчанию об одно-символьном именовании переменных - как видите, они нигде не объявляются и, тем не менее, используются. Как и в Паскале, начинаться программа может со слова "program", но может, как в нашем случае, прекрасно обойтись и без него. А вот "end" в конце программы обязателен - как, опять-таки, и в Паскале. Такие общие черты Фортрана и Паскаля объясняются тем, что Паскаль был создан с учётом опыта Алгола, на который, в свою очередь, повлиял Фортран.

    Фортран сегодня

    Удивительно, но факт: Фортран сейчас - один из самых перспективных языков, какие только можно найти. Да, специалисты по Java, ASP.NET, PHP сегодня ценятся выше, но кто может поручиться, что завтра не нужен будет какой-нибудь C##, и Java-программисты не останутся за бортом? В случае с Фортраном можно с уверенностью, не побоявшись сглазить, сказать: этот язык переживёт и C++, и Java, и PHP, причём с такой же лёгкостью, с какой уже пережил Алгол, Кобол, PL/1 и многих других, имя которым - легион. Что даёт мне основание так говорить? Спокойствие. Сейчас я всё объясню; и, думаю, после этого вы со мной согласитесь.
    Помните, я говорил о том, что Фортран был принят на ура в научной среде и быстро в ней прижился? Так вот, именно благодаря ей он и живёт до сих пор. Учёные не нуждаются в объектно-ориентированном программировании, хорошей портируемости программ и в как можно скорейшей разработке бизнес-приложений. Их требования к языку программирования совершенно иные, нежели у рынка: им нужен хорошо зарекомендовавший себя, хорошо знакомый инструмент создания математических моделей, который будет обеспечивать наилучший баланс между скоростью программирования моделей (всё тех же формул, практически), простотой программирования и выполнением программ. Фортран соответствует всем этим требованиям как нельзя лучше.
    Помимо, собственно, языка программирования, учёным требуется большой набор хорошо отлаженных библиотек для реализации моделей. И здесь Фортрану тоже нет равных, да и вряд ли когда-нибудь кто-нибудь захочет потеснить его из этой ниши, учитывая, что академическая среда куда менее прибыльна, нежели рыночная. Хотя для инженерных расчётов, кстати, Фортран тоже используется довольно широко, но его уже изрядно потеснили такие системы моделирования, как AutoCAD, Maple, ANSYS и некоторые другие. Но, конечно, по мощности и гибкости им далеко до целого языка программирования с поистине бесчисленным множеством различных дополнительных библиотек. Недаром у математиков есть шутка о том, что если нужно решить какую-то задачу, нужно прежде всего поискать её уже существующее решение на Фортране.
    Хотя, конечно, веяние времени ощущается и в инертной академической среде, поэтому современные компиляторы Фортрана стараются генерировать код таким образом, чтобы скомпилированные библиотеки можно было скомпоновать и с кодом, реализуемым на других языках.
    На текущий момент существует довольно много компиляторов Фортрана, так что тем, кто программирует на нём, есть из чего выбирать. Среди самых хороших обычно сразу упоминают Sun Fortran от Sun Microsystems, Intel Fortran Compiler и OpenWatcom Fortran. Есть ещё GNU Fortran, он же gFortran. Долгое время самым лучшим из сравнительно современных компиляторов Фортрана считался компилятор от Microsoft, но корпорация уже давно забросила его разработку, посчитав её невыгодной. Теперь его разрабатывает фирма Compaq. Что касается интегрированных сред разработки для Фортрана, то их тоже не так уж мало. И Sun, и Compaq предлагают современные среды для своих компиляторов, да и для GNU Fortran есть вполне приличные среды разработки.
    Что касается самого современного языка Фортран, то, хоть я сгоряча и сказал, что учёным объектно-ориентированное программирование не слишком нужно, оно, тем не менее, предусматривается языковыми стандартами. Дело в том, что новые поколения учёных, приходящих с ВУЗовской скамьи, гораздо лучше владеют технологиями программирования, чем старое поколение, которое столкнулось с ними уже в зрелом возрасте и большей частью не пожелало менять привычки. Также в самом актуальном на сегодняшний день стандарте языка, FORTRAN 2003, добавлен ряд возможностей по взаимодействию программы с операционной системой.
    Среди возможностей современного Фортрана, в первую очередь, стоит отметить мощную поддержку массивов с гибким присвоением индексов, а также поддержку на языковом уровне асинхронного ввода-вывода данных. Одна из наиболее проблемных областей для разработчиков новой версии стандарта, если судить по публикациям, - параллельное программирование. В этом направлении развивается ряд проектов, таких, как High Performance Fortran (HPF) и Co-Array Fortran.
    Если вы решили изучить Фортран, то найти русскоязычную литературу не составит труда. Одна из лучших книг, которую можно рекомендовать, это "Современный Фортран" Сергея Немнюгина и Ольги Стесик. Она выпущена в 2002 году издательством "БХВ-Петербург". Вообще же книги по Фортрану можно найти в Сети и в электронном виде, так что Google вам в помощь.

    (c)Вадим СТАНКЕВИЧ
     
  2. проще такой код:

    Код:
    [B]Program[/B] Linear_solver 
      [B]Implicit None[/B]; [B]Real [/B]A,B 
      [B]Write[/B](*,*) 'a*x=b  solved with x=b/a';  [B]Write[/B](*,*)' type A/=0, B '
      [B]read [/B](*,*) A,B;  [B]if[/B](A==0) stop 'divide by a==0'  
      [B]write[/B](*,*)' result: x=', b/a,’ when a=’,a,’  b=’, b    
    [B]end Program[/B] Linear_solver   [I]! главное не на месте c = b/a[/I]
    подробнее по-русски о Фортране http://twcad.ifmo.ru

    ...позволю себе, как преподавателю, комментарии курсивом, как долгожителю Фортрана - подарок для желающих разобраться..
    .. напишу еще - по состоянию, Ваш Виктор Фомич Звягин..

    ИСТОРИЯ

    Этот неумирающий Фортран

    История и судьба этого языка программирования уникальны: появившиеся позже высокоуровневые языки уже безвозвратно ушли в прошлое, а Фортран живёт и здравствует и теперь. В чём секрет подобного успеха? Сказать сходу сложно, но давайте попытаемся разобраться с тем, каков Фортран был, есть и будет, по порядку.

    Юность Фортрана-66
    ранний Фортран-77
    - Современный Фортран-90,95
    - будущий Фортран-03,08

    Фортран был первым из созданных человечеством языков программирования высокого уровня (1954), поэтому путь его создателя был тернист и непрост! Но Джон Бэкус, взявшийся за эту непростую задачу, с блеском довёл её до конца и мог по праву гордиться своим детищем.

    Перед группой, создавшей Фортран, стояла очень непростая задача. Им нужно было не просто разработать высокоуровневый язык программирования, но и реализовать его в виде компилятора. Причём всё это должно было быть достаточно удобным в использовании, и, самое главное, быстрым, чтобы новый язык составил бы конкуренцию Ассемблеру.

    Основная задача языка состояла в упрощении кодирования формул, откуда берётся название: ForTran - это "Formula Translator". Следует признать, что это Бэкусу и его подчинённым удалось и было большим прорывом. Запись формул на Ассемблере, даже с использованием всяческих макросов, дело неблагодарное, нудное, да и код получается весьма громоздким. А если ещё и формулы восьмиэтажные... В общем, здесь полезность Фортрана было сложно переоценить. Эффект от простоты записи формул в Фортране состоял в том, что этот язык моментально стал популярен в научной среде - его делали инженеры. Ведь кто больше всех нуждается в удобной записи математических формул? Именно учёные - математики, физики, ну и в чуть меньшей степени астрономы, химики и биологи, а также все остальные. Именно в построении компьютерных моделей Фортран тогда нашёл своё применение быстрее всего. Ведь профессиональным программистам свойственна очень большая инертность, которая является весьма весомым фактором торможения прогресса. Учёным же не нужно было цепляться за старые технологии, обеспечивающие им хлеб насущный, а потому они с радостью приняли новый инструмент, который пригодился им во многих исследованиях.

    В СССР Фортран появился позже, чем на Западе - даже позже, чем Алгол60, который, кстати, и задержал появление Фортрана на просторах 1/6 части суши. Но помогли Фортрану те же учёные. Для науки "железный занавес" всё-таки был с дырками, и сотрудничать с европейскими коллегами из Церна советские учёные вполне могли. А как сотрудничать людям, говорящим на разных языках? Поэтому советская, а после и белорусская (как, впрочем, и российская) наука "заговорила" на Фортране. Первый советский компилятор языка, был создан для компьютера "Минск-2", последний - "Минск-32".

    Интересен нам Современный Фортран - стандарт 90,95, сумевший завоевать всемирное признание.

    Язык Фортран собственной персоной

    Вряд ли среди читателей этой статьи много тех, кто программировал на Фортране с самого его появления в Советском Союзе
    - позволю себе комментарии курсивом, как программист и преподаватель, который программировал на Фортране с самого его появления в Советском Союзе, это были:

    • Церн Фортран для БЭСМ-6

    • позже Фортран для Минск-22 и Минск-32 отечественной разработки

    • IBM Fortran не отечественной разработки для ЕС-ЭВМ отечественной разработки, для которых прототипом были IBM-360/370

    ...далее всё не отечественной разработки:

    • MS Fortran 5.1 для DOS IBM PC

    • MS Fortran Power Station для IBM PC Windows

    • DEC Visual Fortran для IBM PC Windows

    • Compaq Visual Fortran для IBM PC Windows

    • Intel Fortran Compiler для Windows и Linux на IBM PC и суперкомпьютерах

    Поначалу Фортран был иным - железо было проще и язык примитивнее. Пользователи тогда работали не с мониторами и клавиатурами, а с телетайпами, перфолентами и перфокартами, а также АЦПУ-алфавитно-цифровым печатающим устройством. В те времена первые шесть позиций в каждой строке программы на Фортране отдавались под обозначения комментария, метки, признака строки-продолжения. Комментарий - начинался с латинского "с" в 1-ой позиции, а метки и пресловутые goto делали код программы своего рода головоломкой.

    Пример 1. Как написать программу, притом плохую? Ответ очень прост - не думая, даже на хорошем языке можно написать плохо. Программа путанная, но её можно скомпилировать и сейчас – и она будет работать! Эта несложная программа написана очень по-старому - понять её непросто – попозже мы с ней разберёмся:

    Код:
    [B]goto[/B] 45
    46 [B]if[/B]( x.gt.40 ) [B]goto[/B] 10
    [B]goto[/B] 21
    10 R=y+40+y*1.5*(x-40)
    1 [B]write[/B](*,*) r; [B]goto[/B] 45
    21 r=y*x;  [B]goto[/B] 1
    45 [B]read[/B](*,*) x,y; [B]goto[/B] 46
    [B]end[/B] ! спрашивается: "Что же делает эта программа ?" – см.ниже
    В современных стандартах Фортрана так уже давно никто не пишет - всё это не актуально, поскольку необходимость в goto отпала, начиная с Фортрана-77, когда средства структурного программирования стали частью языка.

    Новые вызовы времени, связанные с появлением и развитием суперкомпьютеров, Фортран-90-95-03 принял, став единственным языком, на котором непосредственно возможно параллельное программирование для многоядерных систем и суперкомпьютеров.

    Простой пример программы на Фортране​


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

    Это оригинал, программа безголовая в прямом (нет заголовка) и переносном смысле

    • на хороших данных A≠0 – работает

    • на плохих A=0 – ломается, а хотелось бы, чтоб об этом сообщала...

    Код:
    [B]Write[/B] (*,*)'AX=B, X=C'
    [B]write[/B] (*,*)'Please, enter A'
    [B]read[/B] (*,*)a
    [B]write[/B] (*,*)'Please, enter B'
    [B]read[/B] (*,*)b
    c = b/a ! при а=0 - деление на 0 и аварийное завершение 
    [B]if[/B](a .ne. 0)[B]then[/B]
    [B]write[/B](*,*)'Take the result, please... ', c
    [B]elseif[/B](a .eq. 0)then
    [B]write[/B](*,*)'No results found, sorry..' ! при а=0 никаких сообщений не последует
    [B]end if[/B]
    [B]pause[/B]
    [B]stop[/B]
    [B]end[/B]
    Ниже программа укорочена автором сайта (очевидно twcad.ifmo.ru - прим. модератора). Программа изменена по 2 причинам:

    • следуя современной моде в программировании “ничего не оставлять не описанным”

    • важна правильная последовательность действий – сначала проверка знаменателя на 0, а затем деление

    Поясним, что в Фортране:
    • в отличие от Си и Pascal, каждый оператор пишется с новой строки
    • “;” - это тоже конец строки, как и Enter
    • короче, понятнее, главное, правильно –

    Код:
    [B]Program[/B] Linear_solver    ! Заголовок моей программы
    [B]Implicit None[/B]; [B]Real[/B] A,B   ! Ничего по умолчанию 
    [B]Write[/B](*,*) 'a*x=b  [B]solved[/B]  x=b/a'; [B]Write[/B](*,*) ' [B]type[/B] A/=0, B '
    [B]read[/B] (*,*) A,B      
    [B]if[/B](A==0) [B]stop[/B] 'divide by a==0'  
    [B]write[/B](*,*) '[B]result[/B]: x=', b/a,’ [B]whe[/B]n a=’,a,’ b=’, b; [B]pause 
     end Program[/B] Linear_solver
    Программа на Паскале - 15 строк:

    Код:
    [B]Program[/B] Linear_solver;
    [B]var[/B] A,B,c : [B]Real[/B];
    [B]begin[/B]
    [B]Writeln[/B]('a*x=b  solved  x=b/a'); 
    [B]Writeln[/B](' type A/=0, B ');
    [B]readln[/B] (A,B); 
    [B]if[/B](A=0) [B]then[/B]
    [B]writeln[/B]('деление на 0: нет ответа')  
    [B]else
    begin[/B]
    c:=b/a;
    [B]writeln[/B]('ответ: x=',c '  a=',a, ' b=', b);
    [B]end;
    readln;
    end.[/B]
    Заметны общие черты Фортрана и Паскаля и это объяснимо: Паскаль был создан по опыту европейского Алгола, на который, в свою очередь, повлиял американский Фортран. Есть важные различия.

    Сравним Фортран и Pascal на приведенном выше примере:

    • в Фортране имеется 2 знака конец строки “;” и Enter

    • Компилятор Фортрана понимает Write(*,1); read (*,*) A,B как 2 строки
    человек видит как одну Write(*,1); read (*,*) A,B

    • в конце программы на Фортране end обязателен, как и в Паскале

    • “.” в конце программы на Фортране не нужна

    • Фортран: Write(*,1); read (*,*) A,B - “;” после оператора не нужна при наличии Enter, а в середине между простыми операторами обязательна в качестве “невидимого” конца строки

    • Pascal: Writeln ; readln ; “;” нужна после каждого оператора даже когда есть Enter

    • стандарты Pascal и Си конец строки не используют, и он не влияет на восприятие текста программы компилятором, но из-за этого нужен знак окончания оператора “;” и c := b/a без “;” воспринимается как ошибка

    • программисты, конечно, используют конец строки, чтобы как-то организовать текст программы:

    - в Фортране их к этому обязывает стандарт и соблюдение традиции

    - в Си и Pascal - ничего не обязывает, и традиции покороче

    • программа на Фортране может начинаться со слова program, но может обойтись и без него. Тем не менее, я всегда пишу в Фортране Program как составной оператор:
    Код:
    [B][B]Program [/B][/B]Linear_solver
    .. ..
    [B][B]end Program[/B][/B] Linear_solver
    аналогично составному оператору (конструкции):

    Код:
    [B]if[/B] (a .ne. 0)[B]then[/B]  ! по традиции через Enter
      c = b/a;  [B]write[/B] (*,*)'Take the result, please... ', c  ! по традиции через “;”
    [B]else[/B]
      [B]write[/B] (*,*)'No results found, sorry...'
    [B]end if[/B]
    • языки Фортран и Pascal – не чувствительны к регистру, т.е. А и а - одно и то же;
    а вот Си и PHP - чувствительны

    • в оригинальной программе на Фортране переменные a,b,c не объявлялись – так сейчас не делают, во избежание путаницы при помощи Implicit None - отменяют устаревшее соглашение о типах по первой букве в имени, см. мою программу

    • попробуйте написать integer a,b – и Вы можете увидеть, что программа на Фортране “перестанет работать”, например, при b=3 a=2 получим b/a =3/2=1;
    Важно понимать, что тут просто разночтения в написании операции деления в Фортране и Pascal:
    - в Pascal два разных знака деления:
    * деление с целым ответом 3 div 2=1
    * деление с вещественным ответом 3./2.=1.5, 3/2=1.5, 3./2=1.5
    - в Си как и в Pascal – тоже два разных знака деления, но другие “%” и “/”
    - в Фортране знак деления “/” один и тот же - и для целых 3/2=1, и для вещественных 3./2.=1.5 – тип результата автоматически определяется по типам операндов, как самый широкий класс чисел, например, 3./2=1.5

    • так же по-разному пишется знак присваивания:
    - составной “:=” в Pascal c := b/a;
    - “=” в Фортране c = b/a и в Си c = b/a;

    • также по-разному пишется знак сравнения на равенство:
    - простой “a=0” в Pascal, потому как присваивание “:=”
    - составной “a == 0” в Фортране и в Си, потому как знак “=” уже занят под присваивание
    - раньше a==0 в Фортране писали по-другому a.eq.0 - ввиду преемственности языка Фортран сейчас можно писать и так a==0 и так a.eq.0

    • если выводить на консоль 'ответ: ' и ‘result: '
    - в Pascal - всё увидим – в нем и текст набирают, и программу исполняют – всё в среде DOS, в кодировке DOS
    - в Фортран - правильно увидим только по-английски result:
    - первое впечатление о том, что Фортран что-то не умеет – ошибочно; проблема в разной кодировке русских символов в DOS и Windows и в том, что Windows для консоли сохранил кодировку DOS

    Фортран сегодня - Современный Фортран ..
     
    3 пользователям это понравилось.
  3. Удивительно, но факт: Фортран сейчас - один из самых перспективных языков, какие только можно найти. Да, специалисты по Java, ASP.NET, PHP сегодня ценятся, но кто может поручиться, что завтра не нужен будет какой-нибудь C##, и Java-программисты не останутся за бортом? В случае с Фортраном с уверенностью, не побоявшись сглазить, можно сказать: этот язык переживёт и C++, и Java и PHP, причём с такой же лёгкостью, с какой уже пережил Алгол, Кобол, PL/1 и многих других, имя которым - легион. Что даёт мне основание так говорить? Спокойствие. Сейчас я всё объясню; и, думаю, после этого Вы со мной согласитесь.

    Помните, говорилось о том, что Фортран был принят на ура в научной среде и быстро в ней прижился? Так вот, именно благодаря ей он и живёт до сих пор. Учёные не нуждаются в объектно-ориентированном программировании, в скорейшей разработке бизнес-приложений, портируемость программ на Фортране и так хорошая. Их требования к языку программирования совершенно иные: им нужен хорошо зарекомендовавший себя, хорошо знакомый инструмент создания математических моделей, который будет обеспечивать наилучший баланс между скоростью программирования моделей (тех же формул), простотой программирования и скоростью выполнением программ. Фортран соответствует всем этим требованиям как нельзя лучше. Помимо, собственно, языка программирования, учёным требуется большой набор надёжных, хорошо отлаженных библиотек для реализации моделей. И здесь Фортрану тоже нет равных, да и вряд ли когда-нибудь кто-нибудь захочет потеснить его из этой ниши, учитывая, что академическая среда куда менее прибыльна. Для инженерных расчётов, Фортран используется довольно широко, но его теснят готовые системы моделирования, как MathCAD (в реферате – AutoCAD – а это для черчения), Maple, ANSYS и некоторые другие. Но, конечно, по солидности и гибкости им далеко до целоcтного языка программирования с поистине бесчисленным множеством различных дополнительных библиотек. Недаром у математиков есть шутка о том, что если нужно решить какую-то задачу, нужно, прежде всего, поискать уже существующее решение на Фортране.

    На сегодня существует множество компиляторов Фортрана, так что есть из чего выбирать. Среди самых хороших обычно упоминают:

    • Sun Fortran от Sun Microsystems

    • Intel Fortran Compiler + MS Visual Studio for Windows

    • OpenWatcom Fortran - можно прочитать в "КВ" №9/2006

    • GNU Fortran, он же gFortran

    • MS Fortran - долгое время был лучшим из современных компиляторов Фортрана [1996 год], но корпорация его продала, посчитав невыгодным. Поочередно его разработку продолжили фирмы DEC, Compaq, HP..

    • В учёбе MS Power Station до сих пор удобно использовать: это всего лишь
    110мб = IDE среда разработки + Fortran-90 + 3 книги + библитека IMSL с 1500 готовыми программами с описаниями и примерами применения. Имеется серьёзное ограничение этого компилятора – нет автоматической параллелизации.

    Интегрированных сред разработки (IDE) для Фортрана тоже немало. И Sun, и Microsoft предлагают современные среды для своих и не своих компиляторов, да и для GNU Fortran есть вполне приличные среды разработки.

    Что касается самого современного языка Фортран, то, хоть я сгоряча и сказал, что учёным объектно-ориентированное программирование не слишком-то нужно, оно, тем не менее, предусматривается в Fortran-03,-08. Помимо того в Fortran-03 добавлен ряд возможностей по взаимодействию программы с операционной системой и смешанное программирование на Fortran и Си. Среди возможностей современного Фортрана, в первую очередь, стоит отметить мощную поддержку массивов с гибким присвоением индексов, поддержку на языковом уровне асинхронного ввода-вывода данных. Принцип “разделяй и властвуй” реализуется в виде разделения задачи на подзадачи в виде подпрограмм и функций. Важнейшая новинка Фортрана-90 – это развитие этого принципа и распространения его на данные. Модули, модульные подпрограммы, подразделение подпрограмм на внешние и внутренние позволяют легко структурировать данные в сложных и сверхсложных проектах. Одной из новых проблемных областей для разработчиков и пользователей Фортрана является возможность параллельного программирования, предусмотренная и развиваемая в стандартах языка Фортран-90,95,03,08.. В этом направлении развиваются не только новые стандарты Фортрана, но и ряд продвинутых проектов, таких, как High Performance Fortran (HPF) и Co-Array Fortran. Ставится теоретически и решается практически задача автоматизированного параллельного программирования прямо на Фортране. Это становится возможным без ручных манипуляций для превращения последовательной программы в параллельную программу. Именно так вручную приходится делать на Си или старом Фортране (см. технологии OMP и MPI).

    Если вы решили изучить Фортран, то найти русскоязычную литературу не составит труда. Лучшие книги, которые можно рекомендовать, это:

    1. “FORTRAN: основы программирования” – Артёмов И.Л. , М., Диалог-МИФИ, 2007
    Посмотреть вложение 480

    2. "Современный Фортран" - Сергей Немнюгин и Ольга Стесик. СПб., "БХВ-Петербург". 2004

    3. "Современный Фортран" - Бартеньев, М., Диалог-МИФИ, 2002

    4. "Фортран для студентов" - Бартеньев, М., Диалог-МИФИ, 2003

    5. "Современный Фортран" – Ю.И. Рыжиков, Рыжиков,СПб., “Корона”, 2005

    6. “Программирование на Фортране Power Station для инженеров” – Ю.И. Рыжиков,СПб., “ Корона”, 1999

    7. "Практикум по современному Фортрану в курсе информатики" – В.Ф. Звягин, Н.А. Яньшина, В.Н. Голыничев СПб., Учебное пособие, санкт-Петербург, СПбГУ ИТМО, 2010, 135с.
    http://twcad.ifmo.ru/?rub=metoda

    Вообще же книги по Фортрану можно найти в Сети и в электронном виде, так что Google вам в руки.

    Обзор Фортрана

    Символы.

    Программа на Фортране - это последовательность символов. Когда эти символы обрабатываются компилятором, они интерпретируются в различных контекстах как символы, имена, метки, константы, строки и операции. Символы можно поделить на основные группы:

    • $ _ и 52 малых и больших латинских буквы (от A до Z и от a до z)

    • 10 цифр (от 0 до 9)

    • разделители = , . / : ; Enter

    • скобки ( ) [ ]

    • знаки операций "+ - * / > <" , причем из-за нехватки символов некоторые операции уже 2-х символьные "** // == >= <= /=", а некоторые даже подлиннее, как действующие операции ".and. .or. .not. .eqv. .neqv." , так и устаревшие ".ne. .gt. .lt. .le. .ne. .eq.", а также определяемые по воле программиста имена в точках для его собственных операций

    • специальные символы "!" – комментарий "&" - перенос оператора

    • другие печатные символы из набора символов ASCII или ANSI

    • русские буквы можно писать в литералах и комментариях


    Имена.

    Имена присваивают константам, переменным, массивам, функциям, процедурам или модулям Вашей программы. Имя в Фортране содержит последовательность буквенно-цифровых символов. Ограничения на имена таковы:

    1. максимальное число символов в имени не превышает 132

    2. начальный символ должен быть буквой, а последовательность символов в имени может быть только буквенно-цифровой или $ или _

    3. пробелы в именах не допустимы

    4. есть особые имена операций, окруженные точками - как действующие
    .and. .or. .not. .eq. .eqv. .neqv. так и устаревшие .ne. .gt. .lt. .le. .ne. .eq.

    5. имена .and. .or. .not. несколько непривычны для тех, кто пришел из Pascal, где они писали то же самое без точек как and or not, не забывая, что это ключевые слова, недопустимые в качестве имен в Pascal, тогда как and or not вполне допустимые имена в Фортране

    С учетом упомянутых ограничений на имя, любая последовательность символов может быть использована как имя в Фортране. Здесь нет зарезервированных имен (ключевых слов), как в других языках. Последовательности символов, используемые компилятором Фортрана как ключевые слова, не смешиваются с именами пользователя. Компилятор различает ключевые слова по их контексту, и поэтому на использование имен пользователями нет никаких ограничений, кроме двусмысленности, так, например:
    • можно использовать стандартную функцию max(x,y)
    • можно использовать переменную max=a
    • в одной программе нельзя использовать и стандартную функцию max(x,y) и переменную max=a

    В программе могут быть массивы с именами IF, READ или GOTO и ошибок при этом не возникает. Однако использование ключевых слов как имен пользователя мешает "читабельности" программ, и его следует избегать.

    Типы.


    Данные в Фортране относят к одному из пяти базовых типов либо к производному типу:

    1. целые integer (integer*4) и integer*2

    2. вещественные обычной точности real (real*4) и вещественные двойной точности real*8 или double precision, а также real*16

    3. комплексные complex (complex*8) и double complex или complex*8, а также complex*16

    4. логические logical (logical*4) и logical*2 и logical*1

    5. строковые character (character*1) и character*12, здесь 12, как пример длины строки, а можно от 1 до 32767

    6. структура или производный тип данных, определяемый программистом
    type имяТипа ... end type имяТипа

    Типы данных обязательно объявлять для каждой величины, если предварительно указано implicit None. Если нет implicit None и тип не объявлен, то он определяется по первой букве имени (из соглашения по 1-ой букве, по умолчанию или расписанного в операторе implicit). Оператор описания типа может также включать информацию о размерности. Тип определяет не только способ хранения в памяти, но и допустимый набор операций. Операции и функции Фортрана определены как элементные, т.е. могут быть не только скалярами, но и массивами, одинаковыми по форме – векторами, матрицами.. Если программист сам определяет производный тип, то он сам определяет операции над объектами этого типа. Такого рода определения задаются в виде модульных программах, размещаемых в модуле.

    Выражения


    Выражение - это формула, написанная по правилам языка; выполняет операции, предусмотренные для операндов каждого типа. Операнды могут быть переменными, константами, функциями, подвыражениями и ..
    И вот на этом другие языки остановились, а Фортран-90 разрешил действия с массивами целиком. В выражении : A + B плюс (+) - это операция, а A и B – операнды; операнды A и B могут быть не только скалярами, но и массивами, одинаковыми по форме – векторами, матрицами..

    Как и в других языках, в Фортране есть минимум 4 вида выражений :

    1. арифметические выражения – могут смешивать разные числовые типы

    2. логические выражения, включая отношения

    3. строковые выражения

    4. выражение производного типа

    Тип выражения определяется по типам операндов:

    • для смешанного арифметического выражения тип определяется автоматически по самому широкому классу чисел из операндов выражения

    • логический для логических операндов

    • строковый для строковых операндов

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

    В примере на оператор присваивания, правая часть после знака присваивания является выражением, причём X,A,B-матрицы по 10 строк, 11 столбцов:

    Код:
    [B]real,dimension[/B](1:10,-5:5) :: X,A,B
    X=2./3+sin(A)+B   ! в Pascal, Си пришлось бы писать 2 вложенных цикла по 10 строкам, 11 столбцам
    Форма текста программы


    Начиная с Фортран-77, поддерживается:

    • *.f90 новая свободная форма текста программы - новые тексты следует писать в ней

    • *.for старая фиксированная форма текста программы – для преемственности

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

    • оператор или его продолжение

    • строка комментария начинается с ! или вся пустая - только выводится в листинге и игнорируется при обработке

    • строка метакоманды со знаком доллара в первой позиции в MS Фортран
    Операторы.

    По-русски операторы в языке программирования - не самый удачный термин, но он, к сожалению, прижился, а в английском используется более удачный термин statement – это предложение языка. Операторы Фортрана подразделяются:

    • по месту в программе - на исполняемые (что делать?) и неисполняемые (какой, как?)

    • по написанию – на простые однострочные операторы и составные операторы

    заголовок

    ... начинка ...

    end заголовок

    • по назначению - операторы описания, форматы, присваивания, управляющие конструкции, программные единицы, чтение и запись файлов, вспомогательные и прочие.

    Решение, проекты, приложения, программы и компилируемые компоненты программ


    Среда разработки Visual Studio предусматривает создание проектов и организацию решений из тематически связанных проектов. Проект строится из компилируемых компонент программы. В результате компиляции и сборки проекта строится приложение в машинных командах. Минимальными компилируемыми компонентами программы являются программные единицы. Ими могут быть:

    • программы: главная программа, подпрограммы, функции – все они содержат описания и действия, которые можно выполнять, вызывая программу

    • модули – содержат только описания, их можно использовать и не содержит действий, - вот почему модуль используют use M

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

    Выполнение программы всегда начинается с первого выполняемого оператора в главной программе. Следовательно, в каждой выполняемой программе должна быть единственная главная программа. Программная единица (ПЕ) начинается с PROGRAM FUNCTION SUBROUTINE или MODULE.


    Актуальны два вида ПЕ:
    1. внешняя ПЕ (program function SUBROUTINE module)
    2. внутренние процедуры (function SUBROUTINE)
    операторная функция – устарела.

    внешняя ПЕ
    описания
    действия в программах (нет действий в модулях)
    contains
    внутренние ПП
    end внешняя ПЕ


    Процедура

    Имеется 2 вида процедур:

    функция, как удобный частный вид процедуры function f(x,y..), возвращающей, как и стандартные функции, единственный результат, связанный с именем f вызываемой функции, параметры которой могут быть только входными intent(in) :: x,y
    Существуют стандартные функции, встроенные в Фортран, но в этом разделе речь не о них, а о функциях, разрабатываемых программистом. Функция-подпрограмма имеет вид

    FUNCTION f(x,y,...)
    описания
    действия ...
    f=...
    End FUNCTION

    f – это программная единица, которую вызывают, упоминая в выражении. Функция возвращает вычисленное значение f, которое непосредственно используется при вычислении выражения, где она упоминается.


    подпрограмма SUBROUTINE S(x,y..u,v,..z,t..),) - общий вид процедуры, возвращающей любое количество результатов, её параметры могут быть:
    - входными intent(in) :: x,y
    - выходными intent(out) :: u,v
    - входными-выходными intent(inout) :: z,t
    SUBROUTINE S(...)
    описания
    действия .. ..
    End SUBROUTINE S

    Подпрограмма – это процедура, которую вызывают из других ПЕ оператором call S(..). Будучи вызванной, подпрограмма выполняет предусмотренную последовательность действий, а затем возвращает управление на оператор, следующий за оператором вызова call S(..). С именем процедуры непосредственно не связан результат, хотя она может возвращать результаты в вызывающую программу через выходные параметры или через переменные, заимствованные из внешней программы или модуля.


    Модули


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

    Module M
    описания глобальных общедоступных данных
    действий нет
    end Module M

    Модуль может быть не только контейнером для данных, но также может содержать подпрограммы для их обработки.

    Module M
    описания глобальных общедоступных данных
    действий нет
    contains
    модульные ПРОЦедуры
    end Module M

    Инкапсуляция позволяет припрятать данные от непосредственного упоминания в программах, использующих модуль, тем самым упрощая модификацию сокрытых данных. Интерфейсы, по-другому правила пользования внешними ПРОЦедурами, также уместно помещать в модули. Переопределение операций и присваиваний, особенно для пользовательских типов данных обычно относят к компетенции модулей.

    Библиотеки


    Если Вам надо использовать некоторые процедуры в других программах, Вы можете создать объектную библиотеку из этих процедур и затем собирать их с каждой из программ, в которой нужны эти процедуры. Если процедуру надо усовершенствовать, Вы можете изменить содержание и даже переписать ее на Ассемблер или Си, а остальная часть Вашей программы не изменится.

    Ввод / Вывод

    Ввод (чтение) - это передача данных из внешней среды или из строки в оперативную память. Вывод (запись) - это передача данных из оперативной памяти во внешнюю среду или в строку. В Фортране, в отличие от Си и Pascal, имеются операторы, обеспечивающие передачу данных. В дополнение к операторам передачи данных, существует несколько вспомогательных операторов ввода/вывода, управляющих внешней средой, или определяющих, или описывающих свойства соединения с внешней средой. Для понимания системы ввода/вывода важны следующие понятия:
    Записи. Понятие, на котором основана файловая система Фортрана. Запись - это последовательность символов или значений. Существует три вида записей: форматные, бесформатные и конец файла.
    Файлы. Последовательности записей. Файлы могут быть внешними или внутренними. Внешние файлы - это файлы, относящиеся к устройству, или само устройство. Внутренний файл - это строковая переменная, которая используется либо как текст, либо как назначение некоторых действий по форматному вводу/выводу.


    Файлы имеют следующие атрибуты:

    • условный номер устройства ввода-вывода
    • имя файла в файловой системе ПК (произвольное)
    • метод доступа (последовательный или прямой)
    • структура (форматная или текстовая, неформатная или двоичная)
    • указатель позиции в файле

    Комбинируя атрибуты, получаем большое разнообразие типов файлов, хотя самые распространенные на практике - последовательные текстовые файлы.

    Управление ходом вычислений


    Пример 1. Как написать программу, притом плохую? Ответ очень прост - не думая. Вот пример несложной программы, написанной по-старому - понять её непросто:

    Код:
    goto 45
    46 if( x.gt.40 ) goto 10
    goto 21
    10 R=y+40+y*1.5*(x-40)
    1 write(*,*) r; goto 45
    21 r=y*x;  goto 1
    45 read(*,*) x,y; goto 46
    end ! спрашивается: "Что же делает эта программа ?"
    Для современных стандартов Фортрана всё это не актуально, поскольку необходимость в goto отпала, начиная с Фортрана-77, когда средства структурного программирования стали частью языка.

    Пример 2. Что делается в примере 1 ? - или как написать хорошую программу. Наличие шести операторов goto в примере 1 завязывает простую программу морским "узлом" и она становится аморфной и нечитаемой. Попробуем разобраться. Сформулируем постановку задачи из примера 1: вычислять R(x,у) , вводя значения аргументов x,у, пока они есть:

    * если " x>40" вычислять R(x,у) по одной формуле
    * иначе вычислять R(x,у) по другой формуле

    Приняв во внимание словесное описание алгоритма, получим внятное решение,
    вложив if в бесконечный цикл, схематично это выглядит так:

    от ПОВТОРЯТЬ ! - это цикл – в нем следующие действия

    * читать x,y - пока они есть, выйти - по концу данных
    * если (x>40 ) - формула R = R1(x,у)
    * если (x<=40) - формула R = R2(x,у)
    * печать x,у, R

    до ПОВТОРЯТЬ

    do ПОВТОРЯТЬ или цикл -
    это типичная конструкция Фортрана
    enddo

    Окончательно структурированная программа будет выглядеть так:

    Код:
    Program XY_estR   
      real  x,y,R
     do
       read(*,*, end=77) x,y   !   параметр end указывает, куда перейти по концу данных
       if(  x>40  )   R = y + 40 + y * 1.5 * (x-40)
       if( x<=40 )   R = y*x;     
       print * , ' R = ' , R,'  при  x=',x,'  y=', y
     end do
    77  end Program XY_estR ! задача завершается по концу данных end=77

    Циклы в управлении ходом вычислений


    * Цикл - группа неоднократно выполняемых операторов или вычисляемых выражений. Применяются управляющие конструкции и операторы конструкция do .. enddo - для последовательных вычислений
    * конструкция forall .. end forall - для параллельных вычислений
    * список выражений для ввода-вывода или инициализации, заключенный в круглые скобки

    Имеется несколько разновидностей циклов. Проиллюстрируем их применение на вычислении суммы квадратов чисел от 1 до 10

    Бесконечный цикл - конструкция неограниченого повторения

    s=0; N=1
    do ! реальный выход - это Exit
    S = S + N**2; N=N+1; if(N>10) exit
    enddo


    Цикл по условию

    s=0; N=1
    do while(N<=10) ! условие продолжения цикла
    S = S + N**2; N=N+1
    enddo


    Цикл по переменной, изменяющейся по закону арифметической прогрессии n = ОТ, ДО, ШАГ

    Для суммирования квадратов проще цикл по переменной
    s=0
    do N=1,10, 1 ! переменная N изменяется по закону 1,2,3 .. 10
    S = S + N ** 2
    enddo
    print *, S


    Конструктор массива

    Начиная с Фортран-90, можно с конструктором массива [ ], который заполняется встроеннным циклом простым перечислением, вообще без цикла
    integer,dimension(1:10)::V=[1,2,3,4,5,6,7,8,9,10]
    print *, sum (V**2)


    Встроенный цикл

    Это особая однострочная запись цикла по переменной, когда вместо
    do
    ..
    enddo
    пишут скобки ( ), а вместо операторов - список выражений. Его встраивают в операторы ввода-вывода или, как в данном случае, - в конструктор массива
    print *, sum ( [(n**2,n=1,10)] ) ! конструктор массива [..] наполняется квадратами чисел от 1 до 10 встроенным циклом () , которые затем суммируются с помощью стандартной функции sum ( [()] )


    конформные управляющие конструкции where и forall

    Фортран-95, оператор forall из параллельного программирования

    integer,dimension(1:10) :: V
    forall (n=1:10) V(n)=n**2
    print *, sum(V)


    Пример на where:
    integer,dimension(-10:10) :: V
    forall (n=-10:10) V(n)=n ! V=[-10, -9, ..,0,.. 9,10]
    where(V<0) V=-V ! V=[+10,+9, ..,0,.. 9,10]
     
  4. Современный Фортран - модули

    Современный Фортран

    Современный Фортран - это Фортран 90-95, для которого уже есть действующие мощные компиляторы, как, например, Intel 9, 10, 11.
    Говоря о современном Фортране, надо иметь в виду 2 темы
    1. модульное программирование
    2. использование понятия конформности применительно к массивам, выражениям, присваиваниям
    Модули – новинка Fortran-90
    Ключевые слова: компонента проекта, модуль, модульная процедура, интерфейс, заимствование формы и/или типа, родовые функции, элементные функции, определяемые операции и присваивания.
    1. Отличительные особенности модулей
    2. Что определяют в модуле и как его используют
    3. Интерфейсы
    a) интерфейс вызываемой программы
    b) функции, перенимающие тип и/или форму у аргумента
    c) переопределение операций и присваивания
    4. Списки переменных для ввода-вывода по Namelist
    5. Определения производных типов данных

    Отличительные особенности модулей

    Модуль(module), как и деление программ на внешние и внутренние, появились в Фортране-90. Внешняя программа и используемые модули расширяют область видимости программы и вводят 2 новых способа обмена данными между компонентами проекта в дополнение к передаче данных через параметры. Использование модулей по своим возможностям перекрывает
    · устаревшие commom-блоки, не рекомендуемые в новых разработках
    · include-файлы, недавно появившиеся и тут же устаревшие в Фортране
    Имеется 3 вида программ
    program subroutine function
    - все они имеют описания и действия. Допускается 2 уровня вложений:
    Внешние ПЕ – Внутренние ПП

    Модуль отличается от программ тем,
    что в нём есть описания, но нет действий. Допускается 3 уровня вложений:
    Модуль – Модульные ПП – Внутренние ПП

    Код:
    Program имярекГлавнойПрограммы
        Описания
       действия
         Contains   ! если надо
           вложены Внутренние подпрограммы
    End Program имярекГлавнойПрограммы
    ________________________________
    subroutine имярекПроцедуры
        Описания
        действия
          Contains   ! если надо
           вложены Внутренние подпрограммы
    End subroutine имярекПроцедуры
    _________________________________
    
    function имярекФункции
        Описания
       действия
         Contains   ! если надо
           вложены Внутренние подпрограммы
    End function имярекФункции
    
    Module имярекМодуля
        Описания
        Действия 
      Contains  ! Модульные процедуры
        subroutine имярекМодульнойПодпрораммы
          Описания
            действия
              Contains ! если надо
                Внутренние подпрограммы
        End subroutine имярекМодульнойПодпрораммы
         function имярекМодульнойФункции
           Описания
           действия
             Contains   ! если надо
                Внутренние подпрограммы
         End function имярекМодульнойФункции
      .. ..
    End Module имярекМодуля
    Программы вызывают для выполнения предусмотренных действий. Допустим вызов подпрограмм по цепочке, включая вызов самое себя, т.е. рекурсия
    Модули используют, чтобы увидеть определяемые в нём глобальные объекты.
    Использование по цепочке допустимо, но не допустима тавтология (определение через самое себя)

    Важно подчеркнуть, что модуль – это оригинальная самостоятельная, отдельно компилируемая программная компонента Фортрана, прямого аналога которой нет в “C”. Сам модуль – это альтернатива include-файлу в “C”. Оператор использования модуля use имярекМодуля - альтернатива препроцессорной директиве include файл, равно как и “C”-подобным commom-блокам Фортрана.

    модуль в Фортране - оригинальная самостоятельная, отдельно компилируемая программная компонента
    include-файл в “C”-самостоятельно не компилируется

    описание глобального объекта делается однократно, централизованно в модуле

    глобальный объект надо описывать в каждой ПЕ, где он используется; include – это всего лишь подключение описаний из файла

    Оператор использования модуля
    use имярекМодуля не дублирует описания, а делает видимым всё, что описано в модуле или заимствовано им из других, ранее отлаженных модулей

    include-файл – это всего лишь дурное повторение описаний препроцессором, чаще без вывода, что является неиссякаемым источником ошибок

    иерархия определений отображается в иерархии модулей, что незаменимо в реализации больших проектов

    include-файлы крайне плохо структурированы и неудобны в больших проектах

    компилятор контролирует коллизию имен поэтапно по иерархии модулей; есть простые средства от коллизии имён

    коллизия имён проясняется слишком поздно - уже при компиляции, из-за чего диагностика ошибок затруднена

    описания производных типов данных и интерфейсов помогают скрывать детали реализации проекта

    описания производных типов данных и шаблонов вызова подпрограмм - просто разновидности описаний

    Уточним, что
    · для каждого модуля компилятор формирует текстовый файл с расширением mod, который и используется при компиляции других компонент
    · в едином текстовом файле модуль должен быть раньше компонент, которые его используют
    · компиляция изменившегося модуля должна проходить раньше компиляции программных компонент, использующих его; FPS4.0 не всегда это отслеживает, что приводит к появлению необоснованных ошибок, - приходится выбирать, кого компилировать первым
    Что определяют в модуле и как его используют

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

    1. тип из 5 стандартных типов integer real complex logical character или из производного типа type(имярекtype), заранее объявленного
    2. форма – в атрибуте dimension (по умолчанию - скаляр)
    3. начальное значение в виде
    a. имярек=значение для скаляров стандартных типов
    b. имярек= имярекtype(список значений_полей) для скаляров производного типа данных
    c. имярек=[ список значений_элементов массива ], где каждый элемент задаётся как скаляр
    скобки [ ] конструктора появились в Фортран-95, а поначалу их задавали как (/ /)
    4. общедоступность (public- по умолчанию) или локализация в модуле (private)
    5. возможность изменения (по умолчанию) или константное значение (атрибут parameter)

    Рекомендуемый способ описания иллюстрируется примером, в котором определены локализованные в модуле нечетные dig_even и четные dig_odd целые константы в пределах десятка с нумерацией от 0 до 4 в массиве
    Integer, parameter, private, dimension(0:4) :: dig_even =[1,3,5,7,9], dig_odd =[0,2,4,6,8]
    Двойное двоеточие :: можно читать как “по определению”.
    Список атрибутов ←слева :: справа→ список объектов, возможно, со значениями.

    Модули – это централизованное, иерархическое хранилище, в котором систематизирована информация, общедоступная в проекте
    · описания именованных констант, переменных и массивов, отнесенных к стандартным типам
    · модульные подпрограммы доступа к данным
    · для родовых функций прописано заимствование типа у своих аргументов
    · для элементных функций прописано заимствование формы у своих аргументов
    · для внешних программ и программ на других языках даны шаблоны правильного вызова
    Код:
    interface
       шаблон
       шаблон
        ..   ..
    end interface
    · составлены группы переменных Namelist для упрощения ввода-вывода

    · сконструированы производные типы данных и операция над ними, удобные в конкретной задаче


    Оператор использования модуля use имярекМодуля

    · пишется сразу после заголовка программной единицы, которая использует этот модуль
    · позволяет использовать все имена, видимые в модуле, в том числе заимствованные им из других модулей
    · коллизия присоединяемых имён модуля и имён, видимых в ПЕ фиксируется как ошибка
    · исключает возникающую коллизию присоединяемых имён модуля и имён, видимых в ПЕ, если в use
    · перечислить только нужные имена с помощью параметра only
    use имярекМодуля, only: ограниченный список заимствуемых объектов через запятую
    · заменить имена заимствуемых объектов частными именами
    use имярекМодуля, частное_имя1 => имя1_ модуля, частное_имя2 => имя2_ модуля ..
    · позволяет контролировать правильность вызова подпрограмм по их интерфейсам
    · позволяет использовать родовые функции для заимствования типа или формы при конкретном вызове
    · позволяет использовать переопределённые присваивания и операции для производных типов данных
    · делает видимыми группы переменных Namelist
    · даёт доступ к модульным ПП, а через них косвенно к объектам модуля

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

    Конструирование данных производных типов может быть иерархическим и складывается из
    · определений производных типов; тут определения по цепочке допустимы, а тавтология определений (через самое себя) - нет
    type имярекType
    .. .. описания переменных и массивов - полей стандартных и производных типов
    end type имярекType
    · описаний констант, переменных и массивов, отнесенных к производным типам данных
    · создания модульных подпрограмм, реализующих операции над данными производных типов
    · определение новых и переопределение стандартных операций и присваивания для производных типов
    · определения класса в результате связывания операций с производным типом данных

    Интерфейсы

    Модули многолики, это не только контейнер данных и программ доступа к ним, но и систематическое применение интерфейсов, среди которых различают

    1. интерфейсы с шаблонами вызова функций и подпрограмм для контроля правильности их применения
    a. к программам на других языках, например на “С”, см. подробнее
    b. к внешним программам, см. подробнее о шаблонах
    2. именованные родовые интерфейсы для заимствования функцией атрибута своего аргумента из её вызова, см. подробнее
    3. интерфейсы для переопределения стандартных и определения собственных операций (operator), см. подробнее
    4. возможно и совмещение пп. 2,3, см. пример
    5. интерфейсы для производных типов данных дополнительно к пп. 2, 3, 4 требуют определения присваивания (ASSIGNMENT)
    Говорят, что вызываемая подпрограмма (ПП) имеет явный интерфейс в вызывающей программе, если вызываемая подпрограмма является
    1. стандартной встроенной функцией Фортрана, например, sum( array, dim, mask )
    2. внутренней ПП внешней вызывающей программы
    3. внутренней ПП модульной вызывающей ПП
    4. внешней ПП, описанной в операторе interface вызывающей программы
    5. модульной ПП модуля, используемого в вызывающей программе
    6. внешней ПП, описанной в операторе interface модуля, используемого в вызывающей программе

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

    Обычно сопоставление аргументов вызывающей программы и параметров вызываемой ПП производится позиционно. При наличии явного интерфейса появляются новые возможности
    · для ключевого параметра в виде имя_параметра=аргумент позиция среди аргументов не существенна
    например, s=sum( mask=A>0, array=A, dim=1) вместо позиционного s=sum(A, 1, A>0)
    · наряду с этим при вызове ПП можно пользоваться и позиционным написанием аргументов
    s=sum(A, mask=A>0, dim=1)
    · аргумент может быть опущен, если параметр заявлен как необязательный (атрибут optional)
    real:: s; real, dimension(1:3):: P; s=sum(P, mask=P>0) для одномерного массива P параметр dim=1 опущен
    · имея атрибут dimension, и стандартные и пользовательские функции могут быть не только скалярными, но и векторными, матричными, любого предусмотренного ранга
    real, dimension(1:3) :: s; real, dimension(1:2,1:3):: A; s=sum(A, 1, A>0)

    Интерфейс модульной процедуры в пределах модуля - явный без написания шаблона, потому при задании родового интерфейса требуется указать лишь её имя в виде module procedure имярек. Подобный пример см. далее.
    Для производных типов данных интерфейс позволяет переопределять стандартные или определять новые операции, назначая модульные функции для операций и для присваивания.
    Интерфейс вызываемой программы
    Чтобы выполнить исчерпывающий контроль всех вызовов внешней подпрограммы или функции удобно однократно описать её интерфейс, поместив в модуль. Тем более это важно для программ на другом языке, например, “С”. Интерфейсы модульных и внутренних подпрограмм писать не требуется, так как по правилам они сами явно присутствуют в охватывающих программах.

    Interface – составной неисполняемый оператор, который чаще всего присоединяют, используя модуль, где он написан. Иногда интерфейс пишут непосредственно среди описаний вызывающей программы. Интерфейс внешней подпрограммы – это инструкция по её применению, в “С” подобное применение интерфейса также называют шаблоном.
    В простейшем случае Interface описывает шаблоны для правильного вызова ПП.
    Код:
    Interface
      шаблон для правильного вызова ПП
      шаблон для правильного вызова ПП
       .. ..
    end Interface
    Чтобы текст вызываемой ПП превратился в её шаблон, надо всего лишь удалить отмеченное пунктиром

    Код:
    subroutine имярек(формальные параметры) 
        .. описания формальных параметров..
       [I] .. описания .. [/I]  ! не попадает в интерфейс
        [I]  .. действия .. [/I]   ! не попадает в интерфейс
    end subroutine имярек
    Код:
    function имярекFun(только входные формальные параметры)  ! по предназначению – входные - intent(in) 
        .. описание функции
        .. описания формальных параметров..
           [I].. описания .. [/I]  ! не попадает в интерфейс
           [I]  .. действия ..[/I]    ! не попадает в интерфейс
           [I]в том числе имярекFun=выражение[/I]    ! не попадает в интерфейс
    end function имярекFun
    Хорошая практика использования интерфейсов –
    1. начать написание внешней подпрограммы (ПП) с написания её интерфейса в модуле с интерфейсами проекта
    2. сначала это позволяет проконтролировать правильность вызова ПП путем компиляции
    3. далее можно скопировать шаблон ПП в качестве заготовки самой ПП, дополнив которую вычислениями, имитирующими правильные ответы, можно провести первичную отладку вызывающих программ
    4. затем можно реализовать саму ПП и провести её окончательную отладку
    5. проводя предыдущие пункты поочередно для всех ПП можно эффективно отладить всю программу по частям

    Пример интерфейса к процедуре на другом языке.

    Процедура на “С” под именем OemToCharA изменяет кодировку с-подобной строки (с 0-символом в конце) из ASCII в ANSI. В Фортране её будут вызывать под именем OemToChar. В примере модуль содержит интерфейс к процедуре. В данном примере интерфейс, может быть, проще было бы написать прямо в программе без модуля, но в реальных программах все интерфейсы действительно собирают в одном контейнере, каким и является модуль. Строка !MS$ATTRIBUTES – это атрибут, записанный в форме особого комментария по соглашению компиляторов “MS-Cи/Fortran”.

    Код:
    module proInterface
       interface  
        subroutine OemToChar(int1_name,AnsiName)  
          !MS$ATTRIBUTES ALIAS:'[email protected]' :: OemToChar
          integer*1,intent(in),dimension(*) :: int1_name
          integer*1,intent(out),dimension(*) ::  AnsiName
        end subroutine OemToChar
      end interface
    end module proInterface
    
    Program ASCIItoANSI
     use proInterface
        integer*1,dimension(100)  ::   ASCII
          character*100 ::  charASCII; equivalence (charASCII, ASCII)
          integer*1,dimension(100) ::  ANSI
    open(1,file=’ansi.txt’)
    read(5,*) charASCII   ! charASCII=’Текст на русском языке’c – с консоли читается в кодировке ASCII
    charASCII=trim(charASCII)//char(0) ! цепляем 0-символ, чтоб сделать строку с-подобной
    Call OemToCharA(ASCII,ANSI)
    Write(1,*) ANSI
    End Program ASCIItoANSI
    Функции, перенимающие тип и/или форму у аргумента
    Написание программы упрощается, если программист указывает тип аргумента вызываемой функции, а компилятор автоматически назначает тип функции. Так делают при вызове стандартных функций, такого же эффекта можно достичь и при вызове функций, определяемых программистом. Рассмотрим, как функция может перенимать, или заимствовать тип/форму у своего аргумента. Смысл предлагаемого термина “перенимает или заимствует” поясняется на примере стандартной функции abs(x)
    · если x – вещественная величина, то и абсолютная величина abs(x) имеет вещественный тип, то есть функция abs(x) перенимает тип у своего аргумента x
    · если x – целая величина, то и abs(x) – целая, перенимает тип
    · если x – скаляр, то и abs(x) – скаляр, перенимает форму
    · если X – массив, то и abs(X) – массив, перенимает форму
    · если X – вещественный массив, то и abs(X) – вещественный массив, перенимает тип и форму

    В русских книгах по языкам программирования в подобных случаях используют термин перегрузка, см. словарь Lingvo. В этом словаре для оригинального термина overloading приводится правильное толкование – “возможность выбора компилятором различных реализаций одной и той же функции в зависимости от типов ее параметров в вызове функции”. Как кажется нам, да и не только нам [см. книгу Немнюгина, стр.167], перегрузка – крайне неудачный перевод для overloading. Суть дела, на наш взгляд, лучше отражают слова “функция заимствует тип” или “ функция перенимает форму” у своего аргумента. Попробуем объяснить подробнее.

    Пояснение. В первой словарной статье для корня load находим грузить. С приставкой over, корнем load и отглагольным окончанием ing слово overloading формально перевели как перегрузка. Перегрузка – это действие, а при компиляции имеют дело не с действиями, а, скорее, с определениями. На самом деле определения используют, но никак не грузят. Слово перегрузка в русском языке также имеет смысл нагрузки, превышающей какой-то предел, что совсем неуместно. В последующей словарной статье, именно для loading можно найти перевод – “использование каких-либо материалов в целях фальсификации или подделки”, тогда overloading – это использование в целях переделки, применительно к нашему случаю использование в целях переопределения типа функции. Использование в целях переопределения лучше отражает суть дела и вполне согласуется с использованием модулей и определений. Термин “заимствовать или перенимать” мы предлагаем, чтобы обозначить конечный результат переопределения типа или формы функции. Компилятор делает выбор одной из реализаций функции в зависимости от атрибутов её аргумента и, в конечном счете, получается, что функция как бы заимствует или перенимает тип или форму у своего аргумента.

    Для стандартных функций механизм заимствования очевиден. Реализация заимствования для функций, определяемых программистом, требует пояснений
    · создаётся семейство одинаковых по назначению функций с одним и тем же набором параметров
    · атрибуты функций семейства должны попарно различаться из-за различия хотя бы в одном атрибуте сходных параметров
    · функции семейства перечисляются в именованном родовом (generic) интерфейсе - в принципе, не возбраняется, чтобы родовое имя родовоеИмярек совпадало с одним из имён семейства
    · каждый раз функцию вызывают по родовому имени, задавая аргументы нужного типа (разновидности типа) и формы
    · компилятор по атрибутам аргументов подыскивает функцию семейства с подходящим набором параметров, что и определяет атрибуты функции при конкретном вызове
    · если для какого-либо вызова нет подходящей функции в семействе, то компилятор сообщает об ошибке
    no specific match for reference to generic родовоеИмярек
    · по результатам компиляции создается впечатление, что функция заимствует или перенимает атрибут у своего аргумента

    По приведенной схеме функция может перенимать у своего аргумента тип, разновидность типа, форму, эта же методика используется и при определении/переопределении операций, см. следующий раздел. Функции, перенимающие форму, называют элементными. Функции, перенимающие тип, называют родовыми.

    Поясним реализацию механизма заимствования формы на примере вычисления полинома по схеме Горнера
    · в семейство входят 2 функции gscalar(A, x) и gvector(A, x) – обе вычисляют значение полинома и имеют по 2 параметра A, x
    · функции gscalar(A, x) и gvector(A, x) различаются формой параметра x
    · у скалярной функции gscalar, параметр x – скаляр
    · у векторной функции gvector, параметр x – вектор
    · в интерфейсе с родовым именем polinom, 2 функции gscalar и gvector перечислены как члены семейства
    · теперь при каждом вызове функции по родовому имени polinom(A, x) компилятор в зависимости от формы аргумента x построит вызов
    · либо gscalar(A, x), если x – скаляр
    · либо gvector(A, x) , если x – вектор
    · создается впечатление, что функция polinom(A, x) заимствует форму у своего аргумента x
    · поскольку родовой интерфейс polinom и обе функции gscalar(A, x), gvector(A, x) размещены в одном и том же модуле gorner, то можно не писать сами шаблоны функций, а ограничиться указанием их имён в строке
    MODULE PROCEDURE gscalar, gvector
    · сравним функции gscalar(A, x), gvector(A, X)
    · различаются именами функций gscalar и gvector
    · различаются формой ответа gscalar-скалярная и gvector-векторная
    · совпадают по списку параметров (A, x),
    · различаются описаниями x и описаниями функций (скаляр/вектор)
    · функция GVECTOR конформна X, как и функция gscalar конформна x
    · функции совпадают в части действий (с поправкой на имена функций)
    · оператор присваивания gscalar=gscalar*x + A(i) – скалярный,
    а оператор присваивания GVECTOR=GVECTOR*X + A(i) - векторный

    Код:
    module gorner
     implicit none
     integer, private :: i
     private gscalar, gvector
     interface  polinom ! сводит воедино скалярную и векторную функцию, даёт имя родовой функции (или процедуре)
    
       MODULE PROCEDURE   gscalar, gvector
    ! атрибуты формальных параметров функций gscalar, gvector должны отличаться – в этом примере формой параметра X
     end interface  polinom
    
                    contains
    
    function  gscalar(A,x)
     implicit none
      integer :: na ! степень полинома
       real, dimension(0:) :: A
        real, intent(in)::  x ! x -скаляр
         real gscalar
    
       na=size(A)-1;  gscalar = A(0)  ! коэффициент при старшей степени полинома
    
          do i=1,na
             gscalar=gscalar*x + A(i)
         enddo
    
    end function  gscalar
     
    function  GVECTOR(A,X)
     implicit none
      integer :: na
       real, dimension(0:) :: A
        real, intent(in), dimension(:) ::  X     ! X -вектор 
         real, dimension(1:size(X)) :: GVECTOR   ! GVECTOR-вектор,  перенимает форму у X
    na=size(A)-1; GVECTOR = A(0) ! коэффициент при старшей степени полинома
    do i=1,na
       GVECTOR=GVECTOR*X + A(i)
    enddo
    end function  GVECTOR
    
    end module gorner
    Вычислим в программе полином P(x)= 0.3x3+ 2x2+ 2x -7.1
    для числа a=23.8 , для вектора аргументов X=[ 2.5, 2.0, 1.5, 1., 0.5, 0. ] и в этом же интервале с шагом -0.01

    Код:
    program tester
     use   gorner
    Real,dimension(0:3) :: P = [ 0.3, 2., 2., -7.1 ] ! коэффициенты полинома
    Real :: a=23.8,b 
    Real,dimension(1:6) :: X=[ 2.5, 2.0, 1.5, 1., 0.5, 0. ], Y
    
        write(6,*)  ‘a=’,a ;           
       write(6,*) ' polinom(P,a)=’, polinom(P,a)  ! в ответе 1 число
           write(6,*) 'X= ', X;  Y = polinom(P, X);  
           write(6,*) ' polinom(P,X)=’, Y    ! в ответе 6 чисел
          write(6,*) ' polinom(P,X)=’, polinom(P, [(b, b=2.5, 0., -0.01)] )  ! в ответе 26 чисел
    
    End program tester
    Определение операций
    Расширяя запас операций, используемых в выражениях, мы повышаем выразительность языковых средств. Определение операции конструктивное – в интерфейсе указывают функцию(и), которые её реализуют. Интерфейс назначает одну функцию или семейство функций, отвечающих за выполнение операции. Уместно подобные определения сводить в модуль, чтобы сделать их общедоступными. В Фортране имеются следующие возможности
    · переопределение любой из стандартных операций + - / * ** > < >= <= == /= .and. .or. .not. .eqv. .neqv. .gt. .lt. .ge. .le. .eq. .ne. для других типов - что пишется как, например,
    Код:
    INTERFACE  OPERATOR (+)
       module procedure  имярекFunction_for_AddOperator
    End INTERFACE
    · определение собственной операции, имя которой записывается в окружении точек
    Код:
    interface operator (.имярекОперация. )
       module procedure  имярекFunction_for_имярекОперация
    End INTERFACE
    · переопределение присваивания, что пишется как
    Код:
    INTERFACE ASSIGNMENT(=)
      module procedure  имярекASSIGNMENT
    End INTERFACE
    · заимствование типа, формы и определение операции можно совмещать
    Примечания
    · обратим внимание на разночтение в терминологии: в русском “операция”, а в английском “operator ”.
    · в интерфейсе определяемой операции пишут
    · либо ключевую фразу Module procedure имярек для модульной функции
    · либо шаблон для внешней функции

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

    Пример. Научиться писать поразрядные логические операции для целых так, как пишут логические операции.
    Имеющиеся стандартные функции выполняют поразрядные операции над целыми и по названиям похожи на логические операции, сравните ior(a,b) и a.or.b, iand(a,b) и a.and.b , not(a) и .not.a
    Из-за нагромождения скобок функций поразрядные формулы без операций трудно читаются с = ior( iand(a, not(b) ),b), а с операциями выглядят проще c = b .or. .not. b .and. a
    Что, если и поразрядные выражения писать так же, как логические выражения. Возможно ли это?
    - Оказывается можно, если для целых определить новые поразрядные операции.
    Произведем назначения поразрядных операций ИЛИ И НЕ и исключающее ИЛИ
    · вместо стандартной поразрядной функции “ior” с двумя параметрами через интерфейс назначена новая целая двуместная операция a.or.b - реализованная модульной функцией bor(a,b), которая как раз вызывает стандартную функцию ior(a,b)
    · аналогично для iand(a,b) - операция a.and.b
    · аналогично для not(a) - операция .not.a
    · аналогично для ior(a,b) - a.neqv.b
    · присваивание для целых чисел происходит обычным образом, и в переопределении нет необходимости, поскольку в примере нет производных типов данных

    Следует обратить внимание на то, что наш пример носит учебный характер, а на самом деле надо обдумывать последствия назначений
    · например, нельзя заменить ior(a,b) на операцию + или iand(a,b) на операцию *, так как эти операции для целых уже заняты
    · заменив ior(a,b) на операцию .or. и т.д., надо опасаться путаницы из-за расширительного толкования этих операций, как для целых, так и для логических величин; вполне возможно, лучше бы даже оставить что-либо вроде . bwor. .bwand. .bwnot.

    После сделанных назначений .or. .not. .and. .neqv. можно писать поразрядные операции над целыми практически также, как обычные логические выражения, порядок действий сохраняется. Поясним приведенные равноценные выражения
    · c = b .or. .not. b .and. a ! - с нашими определениями поразрядных операций .or. .not. .and.
    · с = ior( iand(a, not(b) ),b ) ! – допустимо стандартом Фортрана

    Проиллюстрируем на нашем примере, как определяется новая операция .and. для целых

    · в интерфейсе прописывается определение операции .and. для целых через модульную функцию band
    Код:
    Interface operator  ( .and. )
        module procedure  band
    End Interface
    · в модуль добавляется модульная функция band; в реализации она совсем простая – всё делает стандартная функция iand(a,b) - поразрядное И для своих аргументов

    Код:
    Function band(a,b)
      Integer,intent(in)::  a,b  ! по предназначению a,b  - входные параметры
       Integer :: band   
         band=iand(a,b)  ! стандартная функция iand(a,b)
    End Function band
    Модуль наиболее полезен, если решать задачу в общем виде, в нашем примере ставилась задача писать поразрядные операции наподобие логических операций, поэтому надо охватить все поразрядные операции.

    Код:
    Module bitwise ! при компиляции одним текстом модуль bitwise должен быть раньше главной программы tester
        Interface operator ( .not. ); module procedure  bnot;  End   interface                 
        Interface operator ( .and. ); module procedure  band; End Interface            
        Interface operator  ( .or. );  module procedure  bor;   End Interface
        Interface operator  (.neqv.);module procedure  beor;  End Interface
    Contains
    
      Function bnot(a)! предназначение параметров указывать обязательно
        Integer,intent(in) ::a ;  Integer::bnot; bnot=not(a); 
      End Function bnot
      Function band(a,b)
       Integer,intent(in) ::  a,b;  Integer::band; band=iand(a,b)
      End Function band
    
      Function bor(a,b)
       Integer,intent(in) ::  a,b ;   Integer::bor;  bor=ior(a,b)
     End Function bor
    
       Function beor(a,b)
        Integer,intent(in) ::  a,b;  Integer::beor;   beor=ieor(a,b)
       End Function beor
    
    End Module bitwise
    
    program tester
     use   bitwise
     Integer :: a=#2345678, b=#23344747 , c, d
             write(6,*) a;    write(6,*) b
              c=a.band.(.bnot.b) .bor. b;  write(6,*) 'c=a.band.(.bnot.b) .bor. b=',c
                   c=ior(iand(a,not(b)),b);       write(6,*) 'c=ior(iand(a,not(b)),b)=',c
    
             d=c .and. (a .neqv.b);          write(6,*) d
    
    End program tester
    Ключевая фраза Module procedure - указывает на явное присутствие модульной функции в том же самом модуле (напомним, что модульные и внутренние ПП не требуют написания интерфейса ), почему и требуется указать только её имя. Если бы это была внешняя функция, то пришлось бы явно прописать её интерфейс.

    Заимствование формы и определение собственных операций можно совмещать. Проиллюстрируем на примере операции .and. Стандартная функция iand(a,b) является элементной, то есть умеет перенимать форму у своих аргументов. По этой причине изменения будут минимальными
    · В интерфейсе – добавлена векторная функция wand
    Код:
        Interface operator  ( .and. )
                        module procedure  band ,[I]wand[/I]
        End Interface
    · добавлена векторная модульная функция
    Код:
    Function wand(a,b)
      Integer,intent(in),dimension(:) ::  a,b 
       Integer,dimension(1:size(a)) :: wand   ! функция перенимает форму у своих аргументов
         wand=iand(a,b)  ! стандартная функция iand(a,b) является элементной
    End Function wand
    Модуль наиболее полезен, если решать задачу в общем виде, в нашем примере ставилась задача записывать поразрядные операции наподобие логических операций, теперь мы её обобщили со скаляров на скаляры и векторы. Ниже в примере, совершенно одинаково выглядят действия со скалярами a, b, c, d и векторами wa и wb.

    Код:
    program tester
     use   bitwise
      Integer::       a=2345678,b=23344747 ,c,d
       Integer,dimension(1:2)::wa=(/1,2/),  wb=(/2,0/)
        write(6,*) a;            write(6,*) b
        c= b .or. a.and..not.b;        write(6,*) 'c=a.and..not.b .or. b=',c
        c=ior(iand(a,not(b)),b);     write(6,*) 'c=ior(iand(a,not(b)),b)=',c
        d=c .and. (a .neqv. b);       write(6,*) ‘d=c .and. (a .neqv. b)=’,d
         write(6,*)   ‘wd=wb .or. wa.and..not.wb=’, wb  .or.   wa .and. .not.wb
    End program tester
     
  5. Современный Фортран - вокруг конформности

    Описание массивов, конформные выражения и присваивания, параллельные управляющие конструкции, параллельные вычисления в Фортране

    Параллельные вычисления - это эффективный по быстродействию код, который строится из кирпичиков, как-то:
    · конформные массивы
    · конформные выражения
    · конформные присваивания
    · конформные управляющие конструкции where и forall
    · параллельные вычисления

    Одно из другого определяется иерархически
    · массивы, одинаковые по форме, короче - конформные массивы - база для построения конформных выражений
    · массив в левой части, конформный выражению в правой части присваивания - это конформное присваивание
    · where и forall с маской в виде логических выражений или секций, конформных вложенным присваиваниям - это конформные управляющие конструкции
    · ну а всё вместе - параллельные вычисления

    Характеристики массивов

    Массив – это множество однотипных элементов, объединенных общим именем. Доступ к элементам производится по индексам, их может быть от 0 до 7. При хранении в памяти в Фортране
    * принята простая индексная модель массива
    o массив – многомерная прямоугольная таблица с неизменными протяженностями сторон – размерностями (экстент-это не по-русски)
    o модель нечувствительна к числу измерений массива
    o модель нечувствительна к способу наделения массива памятью
    + статический массив
    + динамический массив
    + динамический автоматический массив
    + массив, перенимающий форму
    + массив, передаваемый по адресу ..
    * размещение в памяти - самый быстрый индекс - первый, медленнее – второй, и т.д.
    * матрица хранится по столбцам - 1-ый индекс-номер строки, 2-й индекс-номер столбца

    В языке “C” не так четко - модель путаная, матрица хранится по строкам, чаще всего пользуются не индексами, а указателями или указателями на указатели, а для динамических массивов – и вовсе многие индексы вручную пересчитывают в единый адрес от начала по формулам


    К характеристикам массива относят
    * Имя массива
    * Тип массива – все его элементы однотипны
    o базовые типы integer real complex logical character
    o типы, определяемые программистом type (имярек)
    o в отличие от “С” Фортран придерживается строгой типизации: каждому типу - только свои операции
    * ранг, или число измерений массива, или число индексов от 0 до 7
    o 0 – нет индексов - простая переменная
    o 1- вектор, одномерный массив
    o 2 – матрица, двумерный массив
    o 3 – система матриц, трехмерный массив
    o 4 - 7.. многомерные массивы
    * по каждому измерению массива в описании задают на выбор
    o либо, что предпочтительнее - диапазон возможных значений индекса начало:конец,
    тогда размерность= конец - начало+1;
    здесь начало и конец – это целые числа, в том числе отрицательные и 0; причем конец ≥ начало

    почему предпочтительнее: не возникает двусмысленности для T(1:8) - везде массив из 8 элементов
    Код:
    real*8,dimension(1:8):: T   ! описание массива из 8 элементов
    read(*,*)  T(1:8)  как и read(*,*)  T   ! читать все 8 элементов
    o либо размерность – неотрицательное число, указывающее на количество возможных значений индекса, тогда диапазон 1: размерность

    почему по-старому хуже: возникает двусмысленность для T(8)
    real T(8) ! описание массива из 8 элементов
    read(*,*) T(8) ! читать только 8-й элемент, а не массив из 8 элементов

    * форма массива array – это целочисленный вектор, составленный из размерностей; форму измеряют shape(array);
    * массивы, одинаковые по форме, называют конформными – это ключевое понятие в параллельном программировании
    Примеры:
    Код:
    real*8,dimension(1:2,1:3):: Mr,M2
    понятно, что в одном списке все свойства одинаковы, в том числе shape(Mr)=shape(M2)
    Код:
    real*8,dimension(0:1,-1:1):: Mp
    однако shape(Mr)=shape(M2)= shape(Mp)= [2,3]; Mr,M2,Mp – все 3 массива конформны
    Код:
          real*8,dimension(0,3):: T
          real*8,dimension(3,0):: P
    shape(T)≠shape(P) так как [0,3] ≠ [0,3] – поэтому массивы T, P не конформны, хотя и size(T)=size(P)=0

    * размер массива (количество элементов) можно измерить size(array)
    или подсчитать через форму по формуле product(shape(array)); product – это произведение
    * память под массив = <память на элемент> * size(array)
    * способ наделения массива памятью
    o статический
    o динамический
    o динамический автоматический - только в подпрограмме
    o с переменной размерностью - только в подпрограмме
    o перенимающий размерность - только в подпрограмме
    * прочие атрибуты intent, save, parameter, private, public ..

    Конформные массивы - одинаковы по форме, – это ключевое понятие в параллельном программировании. Параллелизация начинается с описания конформных массивов, но это лишь база.

    Примеры:
    Код:
          real*8,dimension(1:100,1:100):: Matr,  Matr2
           real*8,dimension(1:100):: Vec,Prod 
            real*8 :: x=0.73
    Параллелизация продолжается конформными формулами и конформными присваиваниями над векторами и матрицами, но этим не исчерпывается.

    Правильно: Matr = -(2*x); Matr2 = abs(Matr)-4
    Правильно: Prod = 2*x**(1./3)+4
    Сомнительно по ОДЗ: Prod = 2*Vec**(1./3)+4 так как одна из компонент вектора Vec может оказаться отрицательной
    Сомнительно по ОДЗ: Vec=cos(-1/(2*Prod)) так как одна из компонент вектора Prod может оказаться нулевой
    Неправильно: Matr = Vec + Prod - это сложение конформных векторов, но неконформное присваивание.

    Обратим внимание
    * в примерах имеются только формулы и присваивания, нет циклов, хотя вычисляется много элементов
    * в правильные выражения входят конформные объекты:
    o конформные массивы
    o элементные функции от них (элементными называют функции, перенимающие форму у своего аргумента, как, например, выше abs(Matr))
    o исключение – скалярные подвыражения (в частности константы и переменные), они как бы перенимают форму у окружающих конформных массивов
    * в конформном присваивании переменная=выражение непременно переменная и выражение - конформны
    * по написанию формулы для скаляров, векторов и матриц не различается, а способ обработки – передается в ведение компилятора
    * вообще, со стороны программы нет никаких указаний о порядке выбора компонент вектора или матрицы - именно такие “векторные”, а лучше говорить конформные выражения и присваивания - основа параллелизации
    * нет циклов, порождающих последовательность действий
    * проверка ОДЗ требует покомпонентного контроля, и тогда параллелизация продолжается в написании специальных управляющих конструкциях, например
    Код:
          where ( Vec >=0 ) 
             Prod = 2*Vec**(1./3)+4 
           elsewhere ! только для отрицательных компонент - по-другому
              Prod = -2*(-Vec)**(1./3)+4
           endwhere
    Секция и конструктор массива

    Секцией (section) называют рабочий (виртуальный) массив, который отдельно не объявляют. Если индекс обычного массива задать не в виде скаляра, а в виде вектора, то сформируется секция. В книгах наряду с термином секция иногда используют термин сечение. Предусмотрено несколько способов задания множественного индекса
    · либо в виде триплета (тройка чисел начало:конец:шаг), задающего арифметическую прогрессию наподобие цикла do i=начало,конец,шаг
    · либо в виде целочисленного вектора
    · либо в виде конструктора массива из целых чисел

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

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

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

    Примеры манипуляций с секциями

    Пусть заданы описания конформных массивов X,Y

    · real,dimension(1:4) :: X,Y ! массивы X,Y по 4 элемента
    read(1,*) X ! Чтение массива целиком
    Y=X ! задаёт пересылку массива целиком

    При пересылке только элементов с нечетными номерами индексов
    X 1 2 3 4
    Y 1 3
    Do i=1,3,2 ! по-старому пришлось бы задавать цикл
    Y(i)=X(i)
    Enddo

    что не хорошо по 2 причинам
    1. выглядит архаично и громоздко
    2. главное - фиксирует последовательность действий

    К счастью, Fortran-90 ввел в обращение секции массива, которые формируются из базового массива. Ими можно пользоваться практически везде, где можно было пользоваться массивами. Используя секции, пересылку, написанную выше по-старому, можно записать по-новому короче Y(1:3:2)=X(1:3:2). Используя для задания секции конструктор [1,3], то же можно записать и так Y( [1,3] )=X( [1,3] )

    Если надо поменять местами элементы массива с нечетными и четными номерами
    X1234
    Y2413
    пишут Y(1:3:2)= X(2:4:2) ; Y(2:4:2)= X(1:3:2) .

    Если надо составить массив Y из четных, потом нечетных элементов
    X1234
    Y2413
    то, используя конструктор массива, вместо Y(1:2)=X(2:4:2); Y(3:4)=X(1:3:2) пишут еще короче Y = (/ X(2:4:2), X(1:3:2) /) или Y = [ X(2:4:2), X(1:3:2) ] - Фортран-95 разрешил вместо (/ /) более простые скобки [ ] конструктора Y = [ X(2:4:2), X(1:3:2) ]

    Заметим, что, если надо поменять местами “на месте”,
    Y1234
    то решение Y(1:3:2)= Y(2:4:2) ; Y(2:4:2)= Y(1:3:2) – будет неправильным, так как при выполнении 2-го присваивания уже Y(1:3:2)= Y(2:4:2)=[2,4]

    Y1234
    Y2424

    а решение с конструктором Y = [ Y(2:4:2), Y(1:3:2) ] – будет правильным, так как справа используются старые значения массива
    Y1234
    Y2413

    Имея тот же смысл, что и в цикле Do индекс=начало,конец,шаг тройка целых чисел, составляющих триплет начало:конец:шаг пишется с другим знаком препинания.

    Объясняется это местоположением триплета – его пишут на месте индекса, а индексы сами разделяются запятыми.

    В написании цикла Do индекс=начало,конец [,шаг] необязательным является только шаг, подразумевается 1.

    В написании триплета [начало] : [конец] [: шаг] необязательным является всё, кроме первого двоеточия. Все недостающие сведения берутся из определения массива.

    Оформляя секцию массива, мы фактически создаем новый виртуальный массив со своими размерностными характеристиками, которые могут отличаться от базового массива, что интересно, не только в меньшую, но и в большую сторону, поясним на примерах
    Код:
    real,dimension(1:4,1:6) :: M  
    M(1,:) – 1-я строка матрицы, одномерный массив из 6 элементов
    M(i,:) – i-я строка матрицы, одномерный массив из 6 элементов
    M:),2) – 2-й столбец матрицы, одномерный массив из 4 элементов
    M:),j) – j-й столбец матрицы, одномерный массив из 4 элементов
    M(1:3:2,2:6:2) – матрица, двумерный массив 3 строки, 3 столбца
    M(1:3:2,6:2:-2) – матрица, двумерный массив 3 строки, 3 столбца, столбцы в обратном порядке
    M( [1,3,4,2], 2:6:2) – секция с векторным индексом - матрица, двумерный массив 4 строки, 3 столбца, порядок следования строк изменен на [1,3,4,2]
    M( [1,3,4,2,3,1], 2:6:2) – секция с векторным индексом - матрица, двумерный массив 6 строк, 3 столбца, порядок следования строк изменен на [1,3,4,2,3,1], мало того строки 1 и 3 дублированы, столбцы только с четными номерами
     
  6. Современный Фортран

    Современный Фортран
    в продолжение темы параллельное программирование
    - управляющие конструкции Фортрана для последовательных и параллельных вычислений


    Вспомним традиционные конструкции и обобщим их применительно к конформным вычислениям, а также рассмотрим конструкции where и forall, предусмотренные в Фортране специально для конформных вычислений. Дополнительные управляющие конструкции окончательно оформились только в Фортране-95. Напомним, что конформными называют объекты, одинаковые по форме. Форма – это вектор, составленный из размерностей объекта в порядке их следования. Наравне с массивами ющие конструкции применительно к конформным присваиваниям. Из истории развития Фортрана можно вспомнить, что в Фортране-77 был узаконен стиль структурного программирования, опирающийся на 2 основные управляющие конструкции DoWhile и If , которые включают скалярные условия. Конструкции Do и If и подразумевали последовательные скалярные действия. В Фортране-90 стали возможны конформные присваивания под управлением конструкций Do, If и Select case.

    Цикл Do .. .. enddo в 3 разновидностях предполагает последовательные действия
    ·
    Код:
      Do    ! бесконечный цикл
      ..  .. конформные присваивания и другие действия
         enddo
    ·
    Код:
    Do   параметр= начало , конец , шаг   !  цикл по параметру, в секциях триплет начало , конец , шаг   записывается как начало : конец : шаг   
       ..  .. конформные присваивания и другие действия
    Enddo
    ·
    Код:
    Do while( условие )   !  итеративный цикл или цикл по скалярному условию
      ..  .. конформные присваивания и другие действия
                      Enddo
    Ветвление If в 3 разновидностях с количеством блоков 2, 1, 0
    ·
    Код:
    If( условие ) then ! с 2 блоками
               ..  .. блок ДА - конформные присваивания и другие действия
              else
                ..  .. блок НЕТ - конформные присваивания и другие действия
              endif
    ·
    Код:
    If( условие ) then ! с 1 блоком
                ..  .. блок ДА - конформные присваивания и другие действия
              endif
    ·
    Код:
    If( условие ) единственный_простой ! без блоков- конформный простой оператор присваивания
    

    Переключатель на много+1 направлений


    Код:
    Select case (целое или символ)
      Case ( константа, диапазон_констант, перечисление_констант ) ! и их сочетания
       .. .. - конформные присваивания и другие действия
    Case ()
       .. .. - конформные присваивания и другие действия
    Case default   ! в остальных случаях
       .. .. - конформные присваивания и другие действия
    End Select
    
    Блочные скалярные конструкции Do If и Select case могут вкладываться друг в друга неограниченно.

    При вложении конформных присваиваний в скалярное управление получится управление на уровне манипуляций с векторами и матрицами (вообще, массивами) целиком.

    Конформные управляющие конструкции

    Условия во вновь появившихся конструкциях Фортрана-95 стали векторными, матричными (в общем случае многомерными). Появилась возможность их варьировать покомпонентно. Прежние управляющие конструкции DoWhile If и Select case, ориентированные на скаляры, для этих целей не подошли. Условия в управляющих конструкциях стали конформными проводимым вычислениям. Новые управляющие конструкции как раз реализуют параллельные действия над компонентами векторов и матриц (вообще, массивов) под управлением компонент конформной им маски, заданной логическим выражением. Имеется лишь 2 конструкции: where и forall, причем для обеих допускается и безблочная операторная форма.

    Оператор и конструкция where

    Ветвление where по форме записывается аналогично If в 3 разновидностях с количеством блоков 2, 1, 0

    where (конформное_условие ) ! с 2 блоками
    .. .. блок ДА - _действия, конформные условию
    else where
    .. .. блок НЕТ - действия, конформные условию
    end where[/CODE]

    Код:
    if (условие )   then ! с 1 блоком
       ..  .. блок ДА
    end if
    
    where (конформное_условие )   ! с 1 блоком
       ..  .. блок ДА - действия,  конформные условию
     end where
    Отметим внешнее сходство if и where - оба они ветвления. Однако есть кардинальное отличие
    · if задает скалярное ветвление, группируя действия над скалярами или векторами целиком
    · where - это векторное покомпонентное ветвление, которое группирует параллельные действия над компонентами векторов, матриц, многомерных массивов, секций с непременным требованием конформности условия и действий

    Имеется также определенное сходство where с Do - оба могут обрабатывать массивы. Правда, Do может задавать закон изменения лишь одного индекса массива по какому-либо измерению, а where обеспечивает работу по всем компонентам массива независимо, не важно, сколько в нем измерений. Следует подчеркнуть, что имеется принципиальное различие Do и where:
    · Do обрабатывает компоненты вектора последовательно
    · where обрабатывает компоненты вектора независимо, параллельно

    Так что where - это вовсе не цикл, а перечисление потенциально параллельных процессов. Реально параллельность достигается только в случае распараллеливающего компилятора и наличия многих процессоров. В случае, когда компилятор не умеет распараллеливать, конструкция where для одномерного массива как бы заменяет Do и If.

    Порядок действий, соответствующих where
    * вычисляется конформная маска
    * затем над всеми элементами конформных массивов выполняются конформные действия - одно или несколько в последовательности заданной в блоке where
    * действия выполняются только для истинных значений в маске
    * действия выполняются параллельно

    Как всегда, параллельность - это прежде всего независимость процессов, потенциальная способность - в том смысле, что это станет реальностью,
    * если будет использован распараллеливающий компилятор IFC
    * если процессоров будет предостаточно
    * если процессоров будет недостаточно, то поочередно пачками, как решит компилятор и операционная система
    * ввиду многих задач и многих пользователей ситуация по числу свободных процессоров может меняться, что затрудняет наши измерения времени решения задачи
    * если компилятор не распараллеливает или процессор единственный, то действия выполняются поочередно по всем элементам, правда, программа выглядит покороче

    Пример: найти сумму обратных величин. Приведем 3 варианта решения.

    1. Рассмотрим сначала на первый взгляд простое решение, которое можно охарактеризовать как “ловушку для начинающих”

    Код:
    Real  SumObr
    real,dimension(1:10,1:10) :: A
    SumObr=Sum( 1/A, A/=0 )
    Ошибка состоит в непонимании порядка действий –
    a) сначала вычисляем аргумент 1/A и рискуем ”поделить на 0” для нулевой компоненты вектора A
    b) потом суммируем по маске A/=0.

    2. Вычисление обратной величины Obr логично выполнить в виде 2-блочной конструкции where
    Код:
    Real  SumObr
    real,dimension(1:10,1:10) :: A,Obr
    where (A/=0 )   ! с 2 блоками
        Obr=1/A ! где можно – вычислить обратную величину
    else where
       Obr=0 ! где нельзя – обнулить обратную величину
    end where
    SumObr=Sum(Obr)
    3. Вычисление обратной величины можно выполнить и в виде простого оператора where

    Код:
    Real  SumObr
    real,dimension(1:10,1:10) :: A,Obr
       Obr=0; where (A/=0) Obr=1/A   !  безблочный оператор
      SumObr=Sum(Obr)

    Оператор и конструкция forall

    В конструкции where управляющая маска задается в векторной форме, а в конструкции forall маска задается более гибко по отношению к индексированным переменным. Аналогично и выражения в блоке задаются по отношению к индексированным переменным. Другими словами выполняются маскированные конформные действия в пространстве некой секции. Пространства изменения индексов такой виртуальной секции задаются в заголовке forall триплетами, аналогично заданию триплетами секций массивов. Заголовок конструкции forall задает триплеты по ряду измерений и маску отбора к полученной секции по смыслу аналогичную where.

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

    Триплеты, указывающие законы изменения индексов в секции, и маску для секции задают
    · в простом безблочном операторе
    forall (индексимярек=триплет, .. [, маска_секции]) единственный_простой_конформный
    · или в составном одноблочном операторе
    forall (индексимярек=триплет, .. индексимярек=триплет [, скалярная_маска_секции] )
    .. .. маскированные действия, конформные секции, построенной по всем возможным комбинациям индексов из триплетов
    end forall
    Сперва рассмотрим 4 варианта решения простой задачи - найти diaPro - произведение ненулевых элементов главной диагонали.
    На этих решениях покажем разницу в понимании Do и forall

    1. Правильное решение с традиционными конструкциями Do и If

    Код:
    real,dimension(1:10,1:10) :: A
    real diaPro; integer i
    diaPro =1  
    do i=1,10
      if( A(i,i)/=0)    diaPro = diaPro* A(i,i)
    enddo
    2. Заменив do на Forall, получим неправильное решение с помощью Forall:

    Код:
    real,dimension(1:10,1:10) :: A
    real diaPro; integer i
    diaPro =1;  
    Forall (i=1:10, A(i,i)/=0)    diaPro = diaPro* A(i,i)  ! ошибка в левой части присваивания diaPro
    diaPro = diaPro* A(i,i) – это присваивание не конформное заголовку Forall, иначе говоря, diaPro-скаляр, в то время как A(i,i) определено в рамках секции 1:10

    3. Правильное решение для Forall

    Код:
    real,dimension(1:10,1:10) :: A
    real,dimension(1:10) :: Diagonal
     real diaPro; integer i
    Diagonal =1; Forall (i=1:10, A(i,i)/=0)    Diagonal(i)= A(i,i)
    diaPro=product(Diagonal)
    4. Нет решения с помощью where

    Ниже показан более сложный пример вычисления парафлоида – квадратной матрицы минимальных расстояний между вершинами ориентированного графа со взвешенными дугами. Граф задан квадратной матрицей инцидентности. Строки и столбцы матрицы соответствуют вершинам графа. Инцидентные дуги соответствуют положительным компонентам матрицы. Схематично показано вычисление меры близости вершин графа по методу Флойда. С целью упрощения на схеме не показаны условия, также прописанные в этом простом операторе подпрограммы.

    A A(i,j)= minval(A(i,:)+A:),j)) где A(i,:)-строка i A:),j)-столбец j

    Парафлоид удивительно компактно выражается единственным выполняемым оператором
    forall( i=1:n, j=1:n, i/=j) parafloid (i,j)=minval(A(i,:)+A:),j),mask=A(i,:)>0.and.A:),j)>0)
    Попробуйте написать программу с помощью традиционных конструкций – и Вы увидите, что она будет на порядок сложнее.

    Пример парафлоида – многогранный:
    - A(i,:)- i-ая строка и A:),j) - j-ый столбец заданы секциями двумерного массива, образующими векторы
    - Конформные векторы A(i,:) и A:),j) - суммируются A(i,:)+A:),j), образуя новый вектор маскированных крестообразных сумм
    - из сумм выбирается минимум при помощи итоговой функции, образуя элемент парафлоида
    - forall задаёт перебор в пространстве секции, совпадающей с положительно определёнными недиагональными элементами исходной матрицы
    - вычисление парафлоида оформлено в виде подпрограммы-функции
    - функция с атрибутом dimension непременно должна иметь явный интерфейс или быть внутренней

    Внешняя подпрограмма-функция

    Код:
    function  parafloid( n,A)
     integer,intent(in)::n
      integer,intent(in),dimension(1:n,1:n) :: A ! взвешенная входная матрица инцидентности
       integer,dimension(1:n,1:n) :: parafloid ! парафлоид - выходная матрица мин. расстояний
        integer i,j
     Forall (i=1:10) parafloid (i,i)=0 ! главная диагональ
     where(A<=0)  parafloid=0 ! не положительно определенные
     !  положительно определенные
     forall(i=1:n,j=1:n, i/=j.and.A(i,j)>0) parafloid(i,j)=minval(A(i,:)+A(:,j),mask=A(i,:)>0.and.A(:,j)>0)
    end function  parafloid
    Главная программа с интерфейсом

    Код:
    program d
    integer, dimension(1:100,1:100) :: A, parafloidA
    integer, dimension(1:300,1:300) :: B, parafloidB
     interface
      function  parafloid( n,A)
       integer,intent(in)::n
        integer,intent(in),dimension(1:n,1:n) :: A
       integer,dimension(1:n,1:n) :: parafloid
      end function  parafloid
     end interface
    
    ! задать матрицу A
     parafloidA = parafloid( 100,A)
    ! вывести матрицу parafloidA
    
    ! задать матрицу B
     parafloidB = parafloid( 300,B)
    ! вывести матрицу parafloidB
    
    end program d
    У Forall есть аналогии
    · с Where - оба задают работу с многомерным объектом
    · с вложенными Do - обе конструкции задают закон изменения индексов в виде арифметической прогрессии по триплету в заголовках
    · с Do - и в теле оператора forall элементы массивов адресуются по индексам, заданным в заголовке forall

    Однако смысл адресации элементов массива в Do и в forall абсолютно разный
    - в Do элементы массива выбираются последовательно по мере изменения индексов
    - в forall элементы массива выбираются независимо-параллельно, по предварительно построенным всем возможным комбинациям индексов, порядок обработки в языке не фиксирован

    Таким образом
    · DO - основа последовательного выполнения повторяющихся действий
    · Forall - основа параллельного выполнения повторяющихся действий

    Любое присваивание массивов и WHERE можно переписать как FORALL, но некоторые FORALL не могут быть записаны только на уровне манипуляций с массивами в WHERE.
    · Например, легко записать через FORALL: WHERE(A/=0.0) B=1./A - сами напишите
    · Однако, следующий пример FORALL нельзя записать на уровне манипуляций с массивами
    FORALL(I = 1:N, J = 1:N) H(I, J) = 1.0/(I + J - 1)
    · Этот оператор устанавливает элемент массива H(I, J) равным значению 1.0/ (I + J - 1) для любых пар значений I и J между 1 и N.

    Forall задает
    * список имен индексов, варьируемых в секции
    * каждому поименованному индексу сопоставлен триплет, определяющий закон его изменения
    * после списка задается необязательная маска, конформная секции, которая записывается в терминах варьируемых индексов
    * над какими компонентами массивов, какие именно действия и в какой последовательности - задается в блоке оператора forall
    * порядок обработки компонент в языке не фиксирован

    Порядок действий, соответствующих forall
    * по заданным триплетам создаются списки всех возможных значений индексов
    * далее формируются все комбинации индексов
    * по комбинациям индексов формируется секция и конформная ей маска
    * затем независимо-параллельно выполняются конформные действия в последовательности заданной в блоке
    * действия выполняются только для истинных значений конформной им маски

    Получается, что это не цикл, а перечисление потенциально параллельных процессов.

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

    Различие скалярных и векторных конструкций

    Поскольку forall не цикл, а перечисление параллельных процессов, то напрашивающаяся аналогия с циклом ограниченна.

    В ряде случаев do и forall одинаковы по действию, тривиальный пример: если A-массив
    Код:
    Integer, dimension(1:4) :: A
    *
    Код:
    A=0 ! параллельные действия 
    *
    Код:
    forall  (i=1:4) 
            A(i)=0! параллельные действия
           end forall
    *
    Код:
    do  i=1,4  ! последовательные действия
             A(i)=0
           end do
    Простой пример, из книги Немнюгина, иллюстрирующий разницу между Do и Forall
    * цикл Do для массива Integer, dimension(1:4) :: A=(/ 1,2,3,4 /)
    Код:
    Do i=2,3! последовательные действия
             A(i) = A(i-1) + A(i) +  A(i+1)
    Enddo  !  получим
    A(2) = A( 1) + A2) + A(3)=1+2+3=6
    A(3) = A( 2) + A(3) + A(4)=6+3+4=13 ! A( 2)=6 - изменившееся значение
    * forall, перечисляющий параллельные процессы над исходными значениями массива, - те же по виду действия дают другой ответ

    Код:
    Forall(  i=2:3 ) ! параллельные действия
       A(i) = A(i-1) + A(i) +  A(i+1)  ! подставляется исходное значение A( 2)=2
    ! то же по-другому A(i)=sum(A(i-1:i+1))  - проясняет суть дела
    End forall  ! даст ответ
    A(2) = A( 1) + A2) + A(3)=1+2+3=6
    A(3) = A( 2) + A(3) + A(4)=2+3+4=9 ! A( 2)=2 исходное значение

    * A(2:3) = (/ sum(A(1:3)) , sum(A(2:4)) - всё разъясняет: берем старые значения, а заносим новые значения A
    * и ни к чему здесь forall - это чисто академический стиль

    Нетрудно заметить внешнее сходство Do - цикл и Forall - как бы цикл по параллельным процессам. Не проходит аналогия Forall с циклом foreach из языка PHP.

    Есть кардинальное отличие цикла и перечисления параллельных процессов Forall
    · Do задает последовательность действий, которые выполняются над скалярами либо целиком векторами
    · forall задает параллельные действия, которые выполняются над компонентами векторов, матриц, многомерных массивов, секциями с непременным требованием конформности условия и действий
    · Do работает с текущими значениями массивов, а Forall как бы работает с двойной памятью - использует стартовые значения массивов, а формирует новые значения

    Вложение конструкций

    Относительно вложения конструкций друг в друга
    · блочные конструкции Do и If могут друг в друга вкладываться неограниченно
    · блочные конструкции forall и where могут друг в друга вкладываться неограниченно, если условия и действия конформны
    · блочные параллельные конструкции forall и where могут вкладываться в скалярные блочные конструкции Do и If.
    · скалярные конструкции Do и If нельзя вкладывать в параллельные конструкции forall и where, поскольку туда можно вкладывать только конформные параллельные конструкции.

    В теле оператора forall может быть следующее
    · присваивание
    · WHERE - оператор или конструкция
    WHERE используют маску при присваивании для массивов
    · FORALL - оператор или конструкция
    · разрешено вложение только параллельных действий над компонентами векторов, матриц, многомерных массивов с непременным требованием конформности условия и действий

    Относительно вложения других операторов в forall и where
    · не разрешено вложение обычных скалярных операторов, в частности write и read
    · не разрешено вложение обычных скалярных операторов, в частности Do и If

    Преобразование последовательных конструкций в параллельные

    Заметим, что при определенных условиях, конструкции Do на самом деле можно преобразовать в параллельные. Например, для integer,dimension(1:1000):: A циклу
    Код:
    Do i=1,1000
     A(i)=0
    Enddo
    соответствует простейшее A=0

    Однако, понятно, что если Вы собираетесь заниматься параллельным программированием, то написание такого цикла просто неразумно - и тут не стоит полагаться на то, что компилятор оптимизирует такие ляпы. Хотя вполне возможно, что IFC10 действительно это сделает - проверьте. Да и с точки зрения читаемости программы подобный цикл режет натренированный глаз.

    Кстати сказать, если писать на "Си", то там кроме циклов других вариантов нет. Препроцессорные примочки сути дела не меняют - последовательный цикл остается. Если же Вы станете пользоваться MPI или OMP для параллелизации, то написание только усложнится, потому что придется добавлять дополнительно директивы на этих языках - вчитайтесь в текст на Си.
     
    2 пользователям это понравилось.
  7. Практика сравнительного программирования параллельных и последовательных вычислений

    Векторное Произведение Квадратной Матрицы На Вектор
    Одна и та же программа на Фортране может стать и параллельной и последовательной - это 34 строки, см. ниже
    • Программа на Фортране для параллельных вычислений
    параллельной - если скомпилировать в IFC - Intel Fortran Compiler и выполнить на многопроцессорном компьютере
    • Программа на Фортране для последовательного вычисления
    последовательной - если скомпилировать в MS FPS 4.0
    последовательной - если даже скомпилировать в IFC, но выполнить на однопроцессорном компьютере
    • Программа на C для последовательного вычисления - это 234 строки
    Программа на С исключительно последовательная и не содержит никаких сведений для распараллеливания..
    • Директивы MPI делают программу на любом языке работающей параллельно-последовательно
    Программа на С последовательная, не содержит сведений для распараллеливания.. - это 793 строк
    Надо думать и о решении задачи на С и о распараллеливании на языке MPI -
    не С-программа становится параллельной, такой её делают директивы MPI

    Пример программы на Фортране-90 , оценивающей время умножения квадратной матрицы на вектор-строку
    Одна и та же программа может стать и параллельной и последовательной
    • параллельной - если скомпилировать в IFC - Intel Fortran Compiler и выполнить на многопроцессорном компьютере
    • последовательной - если скомпилировать в MS FPS 4.0
    последовательной - если даже скомпилировать в IFC - Intel Fortran Compiler, но выполнить на одноопроцессорном компьютере
    Попробуем пояснить принципы параллельного программирования на простейшей характерной задаче векторного умножения квадратной матрицы на вектор-строку
    c=A*b, где i=1,n
    A - квадратная матрица n на n b - вектор-строка c - вектор-столбец из n элементов
    Составим программу на Фортране-90, которая решает эту задачу.
    Сама по себе задачка несложная, однако, чтобы почувствовать суть распараллеливания, будем задавать размерности вплоть до экстремальных значений от n=10 до n=14000.
    Экстремальное – это на грани работоспособности ПК:
    когда будет исчерпана виртуальная память,
    • Ваш ПК превратится в паровоз с КПД 3-5%
    • винч будет гудеть
    • а расчет будет стоять
    Отметим, что нас будет интересовать не результат умножения, а время его выполнения в зависимости от размерности задачи.
    Задача подразделена на 3 программные единицы (ПЕ)
    • главная программа program matamula
    задает 17 размерностей n
    для каждой из размерностей n в цикле вызывает процедуру seqvcalc(n), получая от неё время умножения
    выводит таблицу с двумя колонками “время в зависимости от размерности n “
    • процедура seqvcalc
    по n динамически наделяет памятью массивы Vec, Matr, Prod
    заполняет числами Vec, Matr
    пользуясь стандартной программой Prod = matamul(Matr,Vec), умножает Matr на Vec, делая отсечку интервала времени функцией tima() и сохраняя его
    освобождает память для повтороного использования
    • функция tima( ) опрашивает текущее время суток до соток секунды
    интефейс к измерителю времени gettim( ) прописан в модуле, специфично названным для каждого компилятора
    либо IFPORT у фирмы INTEL для IFC10
    либо MSFLIB у фирмы MS для FPS4.0;
    поскольку у фирмы MS на модуль MSFLIB программы настроены по умолчанию, то его можно и не использовать
    Вы можете опрашивать чисто процессорное время, вызвав другой таймер, см. помощь по F1
    Если параллельно Вашей программе работает еще кто-то, то он съедает ресурсы, и Ваши измерения будут искажены – даже мышку не трогать!
    Код:
    program matamula  ! главная программа
     implicit none
      integer,parameter::NofDima=17 ! от n=10 до n=14000
       integer :: k
        integer,dimension(1:NofDima):: &
          Dima=(/10,100,500,(k,k=1000,(NofDima-3)*1000,1000)/)
         real ,dimension(1:NofDima):: Timer
     open(6,file="timelooses.txt")
     do k=1,NofDima ! цикл по размерностям
       call seqvcalc(Dima(k))
     enddo
     write(6,11)( Dima(k), Timer(k),k=1,NofDima)
    11 format ("время  в зависимости от размерности  n "/(i7,f8.2))
                      contains
    subroutine seqvcalc(n) ! вычислительно-измерительная процедура
     implicit none
      integer,intent(in)::n
       real*8,dimension(1:n,1:n)::Matr ! автоматические динамические массивы
        real*8,dimension(1:n):: Vec,Prod массив   
         real startTimer
          real*8 :: x=0.73
     Matr=-sin(2*x); Vec=cos(-x/2) ! заполнение Vec,Matr
     startTimer=tima()             ! стартовое время
      Prod=matmul(Matr,Vec)        ! умножение Matr на Vec
     Timer(k)=tima()-startTimer   ! интервал времени
    end subroutine seqvcalc 
     
     real function tima(  )  ! таймер
      USE IFPORT ! для FPS4.0 – эту строчку надо закомментарить,  для IFC10- открыть
       implicit none
        INTEGER*2 hour,hminuit,hsec,h100
      call GETTIM(hour,hminuit,hsec,h100)
      tima=hour*3600+hminuit*60+hsec+h100*0.01
     end function tima
     
    end program matamula
    
    Эксперименты с программой
    Программа на Фортране скомпилирована дважды практически безо всяких изменений
    1. на новом IFC 10.1 для параллельно-последовательных вычислений с автоматической параллелизацией циклов
    2. на старом MS FPS4.0 для чисто последовательных вычислений
    Указание: на самом деле 2-ю строку функции tima() надо корректировать в зависимости от используемого компилятора
    1. USE IFPORT ! использовать модуль IFPORT для опроса времени GETTIM
    2. ! USE IFPORT для FPS4.0 – эту строчку надо закомментарить
    Вызвав диспетчер задач по Alt^Ctrl^Del, мы выполнили программы в указанном порядке, на 2-х ядерном ПК с Core Duo. Снимок экрана по PrintScreen сохранен как рисунок
    Сравним поведение двух программ
    • хронология загрузки 2 процессоров разная в 2 программах
    параллельно: равномерно до 91% загружены оба CPU
    последовательно: оба CPU загружены, но с перекосом и всего лишь на 50%,
    • хронология использования файла подкачки, видно, что память нарастает с разной скоростью и до разного уровня
    параллельно: память нарастает быстрее и до уровня, чуть более высокого
    последовательно: память нарастает медленнее
    времена исполнения программ
    2 единицы для параллельно-последовательных вычислений
    19 единиц для последовательных вычислений
    отметим не 2, а 10-кратное увеличение быстродействия ?


    Векторное произведение квадратных матриц - Программы на Фортране
    • последовательные вычисления - это 51 строка, см. ниже
    Программа намеренно написана в виде 3 вложенных последовательных конструкций - циклов, и её не сделает параллельной даже компилятор IFC
    • параллельно-последовательные вычисления - это 43 строки, см. ниже
    Программа написана с применением перечислителя параллельных процессов forall, а каждый из процессов умножает матрицу на вектор по параллельной стандартной программе matmul, как в предыдущей задаче

    Умножение матриц - Параллельная процедура ​

    Умножение матриц A=B*C​

    все матрицы квадратные n на n при этом n изменяется от n=300 до n=3000
    Код:
    !Параллельная процедура
    program matamula
     implicit none
      integer,parameter::NofDima=10,sota=300
       integer:: k    
        integer,dimension(1:NofDima):: Dima=(/(k,k=sota,NofDima*sota,sota)/)
         real ,dimension(1:NofDima):: Timer
     
     open(6,file="timelooses.txt")
     do k=1,NofDima
      call      seqvcalc(Dima(k))
     enddo
    write(6,"(i7,f8.2)") ( Dima(k), Timer(k),k=1,NofDima)
     
    contains
     
    subroutine seqvcalc(n)
     implicit none
      integer,intent(in)::n
      integer i
        real*8     startTimer
         real :: x=0.73
          real,allocatable,dimension(:,:) :: A,B,C 
          allocate(A(1:n,1:n),B(1:n,1:n),C(1:n,1:n))
    A=-sin(2*x);  B=0.1*x**3 
     startTimer=tima()            !  стартовое время 
     forall( i=1:n)
       C(:,i)=matmul(A,B(i,:))         !  умножение      Matr на Vec
     end forall
     Timer(k)=tima()-startTimer  !  финишное время
          deallocate(A,B,C)
    end subroutine seqvcalc 
     
     real*8 function tima(  )
     USE IFPORT
     implicit none
       INTEGER DATE_TIME (8)
      CHARACTER (LEN = 12) REAL_CLOCK (3)
      CALL DATE_AND_TIME (REAL_CLOCK (1), REAL_CLOCK (2), &
                          REAL_CLOCK (3), DATE_TIME)
    read(REAL_CLOCK (2),"(f10.3)")  tima                    
    END function tima
     
    end program matamula

    Время (сек.) ~ n^2


    Последовательная процедура​

    Код:
    program matamula
     implicit none
      integer,parameter::NofDima=10,sota=300
       integer:: k    
        integer,dimension(1:NofDima):: Dima=(/(k,k=sota,NofDima*sota,sota)/)
         real ,dimension(1:NofDima):: Timer
     
     open(6,file="timelooses.txt")
     do k=1,NofDima
      call      seqvcalc(Dima(k))
     enddo
    write(6,"(i7,f8.2)") ( Dima(k), Timer(k),k=1,NofDima)
     
    contains
     
    subroutine seqvcalc(n)
     implicit none
      integer,intent(in)::n
        real*8     startTimer
         real*8 :: x=0.73
          real,allocatable,dimension(:,:) :: A,B,C 
            real cij
            integer  i,j,p
         allocate(A(1:n,1:n),B(1:n,1:n),C(1:n,1:n))
    A=-sin(2*x);  B=0.1*x**3 
       startTimer=tima()            !  стартовое время
           !  C=matmul(A,B)
    do i=1,n
     do j=1,n
      cij = 0
      do p=1,n
          cij   = cij + A(i,p)*B(p,j)
      enddo
           c(i,j)=cij
     enddo
    enddo
     
       Timer(k)=tima()-startTimer !  финишное время
          deallocate(A,B,C)
    end subroutine seqvcalc 
     
     real*8 function tima(  )
     implicit none
       INTEGER DATE_TIME (8)
      CHARACTER (LEN = 12) REAL_CLOCK (3)
      CALL DATE_AND_TIME (REAL_CLOCK (1), REAL_CLOCK (2), &
                          REAL_CLOCK (3), DATE_TIME)
    read(REAL_CLOCK (2),"(f10.3)")  tima                    
    END function tima
     
    end program matamula


    Вычисление ряда для синуса - Программы на Фортране
    • последовательные вычисления, см. ниже
    1. x f(x) An(x) Sn(x) - скаляры
    2. по Eps, по x, по n - вложенные циклы: расчет - печать для каждой пары x, eps
    • параллельно-последовательные расчеты, см. ниже
    имеется цикл только по n
    1. вектор X-заполнен заранее при фиксированном Eps
    • X f(X) An(X) Sn(X) - векторы
    • цикл по X исчез - распараллеливание по X стало возможно
    2. вектор X-заполнен заранее, вектор Eps
    • f(X) - вектор
    • An(X, Eps), Sn(X, Eps) – матрицы
    •распараллеливание с помощью forall по X и Eps, см. ниже
    сначала расчеты - потом печать

    Вычисление синуса с использованием ряда Тейлора
    последовательные вычисленияпоследовательные вычисления
    CENTER]
    • скаляр x / скаляр eps - базовый расчет
    • скаляр x / Вектор Eps - последовательное вычисление для многих X
    • Вектор X / cкаляр eps - параллельное вычисление по X
    • Вектор X / Вектор Eps - параллельное вычисление по X по Eps
    Скаляр x / скаляр eps - базовый расчет
    • последовательные вычисления
    • x f An Sn Eps - скаляры
    • цикл по n: расчет - печать для каждого n
    Код:
    Program var1 
     Implicit None
      Real :: eps=0.00001   ! методическая погрешность для графика
       Real :: x = 2.5,x2   !   - конкретное число из области сходимости ряда
        Integer :: n ! номер члена ряда 
         Integer :: Ng =50   ! MAX допустимое значение номера члена ряда, например, 50 или 150
          Real An  ! член ряда, An - это в программе, а в математике это A0, A1, .. An, An +1
           Real Sn,f   ! сумма Sn зависит от n, а функция f(x0) не зависит от n
            Namelist /avaria/ x, An, Sn, f, eps, N, Ng
    Open(1,file="Teilor.txt")   ! для просмотра глазами 
    Write(1,*) 'eps=',eps ; 
    Write(1,*) '           n,      An           f,             Sn,         abs(f-Sn)'!надписи
    x2=x*x   
    An=x  ! Выбрать стартовое значение A0  или A1 - конкретная формула для начала Вашего ряда
    Sn=An ! стартовое значение суммы ряда  
    f=sin(X);! эталон  Вашей функции для фиксированного аргумента 
        Write(1,*) 0,An,f,Sn,abs(Sn-f)
     do n=0,NG ! суммирование ряда, начиная с n=0 или n=1
        An = -An*x2/((2*n+2)*(2*n+3));  Sn=Sn+An ! рекуррентная формула А(n+1) = An*Tn
          if(abs(Sn-f)<eps) exit ! так нужно при переменном знаке An
        Write(1,*) n+1,An,f,Sn,abs(Sn-f)
    endDo
                If( n>Ng ) then ! при n>Ng - проблемы 
                    Write(1, avaria); Stop " KARAUL!!!"
                EndIf
    Write(1,*) 'fin:',x, f,Sn, abs(f-Sn), n ! предельное Sn, abs(f-Sn) и n для заданного х
    End Program var1  ! бесформатный вывод


    Вычисление синуса с использованием ряда Тейлора
    Вектор X / cкаляр eps - параллельное вычисление по X
    • параллельно-последовательные расчеты
    • имеется цикл по n –членам ряда
    • X f(X) An(X) Sn(X) - векторы, X-заполнен заранее
    • цикл по X исчез - распараллеливание по X стало возможно
    • сначала расчеты - потом печать

    Код:
    Program paraSin_X 
     Implicit None
      Real :: eps=1e-6   ! методическая погрешность для графика
       Integer,parameter :: NofX=6, Ng=20 ! Ng-допустимое значение n члена ряда
        Integer :: n, p, b=NofX+1, cnt,cnt1 ! n-номер члена ряда 
         Real,save,dimension(0:NofX) ::An, Sn,X2,f,X=(/(p*(1./NofX),p=0,NofX)/)
         Integer*1,save,dimension(0:NofX) :: NofAn 
    Open(1,file="Teilor.txt")   
    f=sin(X); Nofan=1; cnt=0; An=x; Sn=An; X2=X*X
    do n=0,NG 
      where(abs(Sn-f)>eps) ! участвуют конформные векторы и скаляры
        An = -An*X2 / ((2*n+2)*(2*n+3));  Sn = Sn+An; NofAn=n+1      
      endwhere
      cnt1=sum(int(Nofan)); if( cnt==cnt1 ) exit
      cnt=cnt1
    enddo
    call printresX
     
    contains
     
    subroutine printresX
      Write(1,4) eps 
      Write(1,6)
      Write(1,1) '     x   ', x
      Write(1,6)
     
    if(n<=NG)  then ! успешно
     Write(1,2) '     n   ', NofAn
     Write(1,1) '     f   ', f
     Write(1,1) '     Sn  ', Sn 
     Write(1,3) '  |f-Sn| ', abs(f-Sn)
     Write(1,6)
     Write(1,5) maxval(Nofan)
    Else ! не успешно
     
      Write(1,1) ' не успешно: n>Ng :  ', An
    endif
    1 format('|',a,'|',<b>(g11.5,'|'))
    2 format('|',a,'|',<b>(i7,4x,'|'))
    3 format('|',a,'|',<b>(e7.1,4x,'|'))
    4 format('eps=',e7.1)
    5 format(95('-')/'Nmax=',i2)
    6 format(95('-'))
    end subroutine printresX
     
    End Program paraSin_X 

    Вычисление синуса с использованием ряда Тейлора
    Самое свободное и гибкое решение - раcпараллеливание при помощи forall по секции X*Eps

    Вектор X вектор Eps - параллельное вычисление по X по Eps
    • параллельно-последовательные расчеты
    • имеется цикл только по n
    • X f(X) - векторы, X-заполнен заранее
    • An(X, Eps), Sn(X, Eps) – матрицы
    • распараллеливание с помощью forall по X и Eps
    • сначала расчеты - потом печать

    Код:
    Program paraSin_X_EPS   
     Implicit None
      Integer,parameter :: Nofeps=6,ng=20,NofPrint=11
      Integer,parameter :: NofX=10000,stepOfPrint=(2*NofX+1)/(NofPrint-1.)
    ! по {Eps}:
       Real,save,dimension(1:Nofeps):: eps=(/0.1,0.01,1e-3, 1e-4, 1e-5, 1e-6/)     
    ! по {X}:f(X),X2(X)
        Real,save,dimension(-NofX:NofX) :: f,x2,x=(/(p*(1./NofX),p=-NofX,NofX)/)
    ! по {X,Eps}: an(X,Eps),sn(X,Eps),Nofan(X,Eps) Eps-методическая погрешность
         Real,save,dimension(-NofX:NofX,1:Nofeps) :: an,sn
         Integer*1,save,dimension(-NofX:NofX,1:Nofeps) :: Nofan
          Integer :: p,n, ke,kx, cnt,cnt1
     Open(1,file='Teilor.txt') ! таблица для просмотра Nofan, An,Sn
     x2=x*x; f=sin(x); Nofan=0; cnt=0
     An=SPREAD(X,DIM=2,NCOPIES=Nofeps); Sn=An
     DO n=0,ng  ! последовательные итерации по n-членам ряда
     ! впараллель в пространстве двумерной секции {X,Eps} по маске, где |An|>eps
      forall (kx=-NofX:NofX,ke=1:Nofeps, abs(An(kx,ke))>eps(ke) ) 
        An(kx,ke) = -An(kx,ke)*x2(kx) / ((2*n+2)*(2*n+3))
        sn(kx,ke)=sn(kx,ke)+an(kx,ke) !  результаты запоминаются
        Nofan(kx,ke)=n+1 ! по каждой паре {X,Eps}: своё n
      endforall
      cnt1=sum(int(Nofan)); if( cnt==cnt1 ) exit 
      cnt=cnt1
     ENDDO
     call  printResults  ! печать после параллелей
     
     contains
     
     subroutine printResults ! печать после параллелей
      Integer i,j
      where(Nofan==0) Nofan=1
      do i=1,Nofeps  
       Write(1,10) eps(i) !  !  вывод "шапки" таблицы  красивая таблица с циклом по eps
       do  j=-NofX,NofX,stepOfPrint
        Write(1,12) x(j),f(j),Sn(j,i),Nofan(j,i),abs(f(j)-Sn(j,i)) ! вывод строки таблицы
       enddo   Write(1,11)  ! вывод ”донышка” таблицы
      enddo
      Write(1,13) n
     10 Format('Точность вычисления ряда eps=',e11.1/&
     ' ------------------------------------------------------- '/ &
     '|  x  |   станд    |  Тейлор    | Членов ряда |  Разница  |'/ &
     ' ------------------------------------------------------- ')
     12 Format('|',F5.2,2('|',F12.6),'|',I6,5x,'|',e11.1,'|')
     11 Format(1x,55('-'),1x)
     13 Format(/'Nmax=',i3)
     endsubroutine printResults
    End Program paraSin_X_EPS 


    Метод Зейделя для решения уравнений в частных производных
    Программы на Фортране для расчетов по матрицам 287 на 287
    Разница в просмотре точек сечения
    • последовательный – просмотр всех с последующей оценкой невязки в итерации
    • параллельный – просмотр только точек с большой невязкой
    • сложность не в уравнениях системы – они простые
    U(i-1,j)
    U(i,j-1) U(i,j)=∑соседей / 4 U(i,j+1)
    U(i+1,j)

    • сложность – в количестве этих уравнений при n=287 их до 287 *287 =82369 и в большом числе итераций 23935 для достижения заданной точности 0.3E-02. Степень дискретизации n влияет на достижимую точность.

    Программа на Фортране для расчетов по методу Зейделя.​

    Код:
    program ParallelvZedel
    ! "Расчеты по Зейделю для дифуравнений в частных производных"
    ! внутри поначалу 0, а границы по 100 / расчет: параллельный n=287 
     !  100 100 100
     !  100  0  100
     !  100 100 100
       implicit none  
        integer,parameter::N=287,NofIterations=100*n 
        real*8,save,dimension(0:n+1,0:n+1):: U;  real*8 Uij
         integer i,j,c
          real :: startTimer, Timer, eps=0.003
    	  Uij(i,J)=0.25*( U(i-1,j) + U(i+1,j) + U(i,j-1) + U(i,j+1))
     open(6,file="parazedel.txt")
     startTimer=tima()        !  стартовое время
     U=0   ! везде 0      
     U(0,:)=100; U(n+1,:)=100; U(:,0)=100; U(:,n+1)=100 ! каёмочка 100 
      DO c=1,NofIterations ! итерации до eps
       forall( i=1:n,j=1:n, abs(Uij(i,J)-U(i,j))>eps ) u(i,j)=Uij(i,J)   
       if(all(abs(Uij(i,J)-U(i,j))<=eps)) exit         
      ENDDO
    Timer=tima()-startTimer	! текущее время
       if(c<100*n ) then
        write(6,1) c,eps,n,Timer
        1 format(' Всего итераций: ',i5, & 
              ' для точности ',e7.1/'n=',i4,' время ',f8.2,'сек.')
        call printTemp(U,n)
       else
        write(6,*) 'итераций: ',c,'>',100*n  
       endif 
     close(6) 
    
    contains
    
    subroutine printTemp(U,n)  ! матрица температур
     USE IFQWIN
      implicit none
       integer,intent(in)::n
       real*8,dimension(0:n+1,0:n+1):: U 
        integer,parameter ::steper=#4000 !  шкала цветная
        integer,dimension(0:1023) :: Tcol 
         integer :: i,j,d4
         integer*2 :: x0=10,y0=60,x1,y1,x2,y2,r=2,dummy2
      Tcol=(/ (ior(steper*i,#770077),i=0,1023) /)      
      do i=0,n+1  ! матрица температур
       y1=y0+i*r; y2=y1+2*r
       do j=0,n+1
        x1=x0+j*r; x2=x1+2*r
        d4=setcolorrgb(Tcol(nint(U(i,j)*10))) ! цветом - до десятых 
        dummy2= rectangle($GFILLINTERIOR,x1,y1,x2,y2)
       enddo
      enddo
    end subroutine printTemp
    
    real function tima(  )
     USE IFPORT
     implicit none
      INTEGER*2 hour,hminuit,hsec,h100
       call GETTIM( hour,hminuit,hsec,h100)
        tima = hour*3600+hminuit*60 + hsec	+  h100*0.01
    END function tima
    
    end program ParallelvZedel
    
     
    2 пользователям это понравилось.