Сейчас работаю с программным пакетом, который использует набор библиотек. Одна из них обсчитывает геофизику (волны Лява и Рэлея). Обнаружил, что она сохранилась ещё со времён перфокарт и магнитных лент, будучи написанной в соответствии со стандартными бланками разметки (на исходную публикацию этого пакета ПО в 1978 году можете посмотреть здесь - в конце более чем двухсотстраничного документа приложена переводная (с русского!) статья по математике, которая призвана помочь разобраться с идеями, заложенными в программе).
И да, с тех пор его так и используют. Я попытался найти более свежую версию, но альтернатив не нашлось. Другие найденные мной программы используют вычислительное ядро в виде этого же оригинального пакета. Кстати, за что я очень ценю фортран: этот древний код даже вперемежку с кодом новых стандартов (а свежайший - это 2008 год) совершенно спокойно компилируют оба основных современных компилятора - gfortran (GCC) и ifort. Покажите мне любой другой язык программирования, для которого поддерживают прямую совместимость фрагментов с сорокалетней разницей в возрасте и стандартах в пределах одного файла. И, кстати, на уровне описания языка поддерживающий тип данных комплексной переменной и встроенные функции, работающие в мнимых числах (что очень актуально для любой вычислительной математики, работающей с волнами).
Тем не менее, поддерживать этот код, а тем более - модицировать (например, поправки на сейсмические затухания), достаточно сложно. Поэтому я решил его переписать на современный стандарт, благо 80 килобайт - это не так много.
Код писан в те времена, когда переходы по строкам были очень дешёвы (для компьютера), а вычисления - очень дороги. В функциях вычисления собственных значений тут до двухста переменных, хранящих косинусы и синусы. А вот циклов и условных переходов в современном понимании нет - есть прыжки по блокам кода. Например:
if (number) 401,402,403
if (number1.lt.number2) goto 505
Если number меньше нуля, код идёт на позицию 401, если равен нулю - то на 402, если же больше - то на 403. Второе выражение выполняет переход на метку 505, если number1 меньше (less than) number2. И ещё:
10 CONTINUE
Означает просто продолжить выполнение (перейдя на следующую строчку). Однако если это метка завершения цикла, то выполнение возвращается на первую строчку:
DO 10 I=1,20
Так задаются циклы для всех i от 1 до 20, причём последняя инструкция цикла находится на строке с меткой 10.
Теперь же я хочу всем желающим проникнуться духом тёплой ламповой эпохи, а так же некрофилам от лингвистики и литературы - перевести на любой известный язык программирования отрывок кода ниже:
C******************************************************************** C---------------start of loop for Period k--------------------------S do 9998 k=1,kmax call flat1(d,rho,a,b,mmax,kind) C--------------------loop for modes---------------------------------S do 9997 iq=1,kmode if(k-1) 605,605,599 599 if(iq-2) 600,601,601 600 c1=0.90*c(k-1,1) goto 605 601 if(dc*(c(k-1,iq)-c(k,iq-1))) 602,603,604 602 c1=c(k,iq-1)+0.01*dc goto 605 603 c1=c(k,iq-1)+0.01*dc goto 605 604 c1=c(k-1,iq) 605 continue idrop=0 999 del1=dltar(c1,t1,ifunc) 80 c2=c1+dc idrop=0 del2=dltar(c2,t1,ifunc) if(sign(1.,del1).ne.sign(1.,del2)) goto 54 81 c1=c2 del1=del2 qbq=0.8*b(1) if(c1-0.8*b(1)) 250,251,251 251 if(c1-(b(mmax)+0.3)) 252,250,250 252 goto 80 54 continue call nevill (t1,c1,c2,del1,del2,ifunc,cn) if(lstop.eq.0) go to 3006 lstop=0 if(k.eq.1.and.iq.eq.1) go to 3005 if(k.eq.1) go to 3004 do 3003 i=1,k-1 do 3000 j=1,mode if(i.gt.imax(j)) go to 3001 3000 continue jmax=mode go to 3002 3001 jmax=j-1 3002 continue 3003 continue 3004 if(iq.eq.1) go to 3005 jmax=iq-1 3005 continue go to 9999 ! to the end of the subroutine 3006 c1=cn if(c1-b(mmax)) 121,121,250 121 c(k,iq)=c1 C----------------ifunc=kind : Rayleigh if =2 or Love if =1----------- goto (6001,6002),ifunc 6002 continue ratio(k,iq)=dltar(c1,t1,3) goto 6003 6001 continue 6003 continue c1=c1+0.01*dc imax(iq)=k goto 9997 250 if(k*iq-1) 256,256,255 256 continue write(*,*) ncount if(ncount.eq.1)then PRINT 258 do i=1,mmax print *, b(i), a(i) ,rho(i), d(i), qs_ref(i) enddo 258 format(1x,/22x,'Improper initial value. No zero found.') endif goto 9999 ! this goes to the end of the subroutine 9997 continue C-----------------end of loop for mode------------------------------E C--------------------search of root is finished---------------------- goto 9998 255 kmode=iq-1 if(kmode) 9996,9996,9998 9998 continue C-----------------end of loop for period k--------------------------E 9996 continue C********************************************************************
Вообще, очень интересное (для программирующих) упражнение, которое даёт очень наглядное ощущение того, как далеко ушли компьютеры от машины Тьюринга. Знакомые же с языком ассемблера заметят, насколько к нему близок этот перфокарточный фортран. Отдельного внимания заслуживает навигация по блокам кода для максимального переиспользования фрагментов кода - скорее всего, для минимизации ошибок при копировании. В то время как прыжок в неправильный блок моментально приведёт к заведомо бредовому ответу, пропуск одной инструкции может вести к сложно выявляющимся ошибкам.
Из предыдущего фрагмента у меня получился такой код (он тут уже несколько оптимизирован под конкретную процедуру):
Из предыдущего фрагмента у меня получился такой код (он тут уже несколько оптимизирован под конкретную процедуру):
! start of loop for Period k do k=1,kmax ! kmode is = mode = 1 in our case (fundamental modes) do iq=1,kmode if (k-1 > 0) then if (iq-2 < 0) then c1=0.90*cc(k-1,1) else if(dc*(cc(k-1,iq)-cc(k,iq-1)) <= 0.) then c1=cc(k,iq-1)+0.01*dc else c1=cc(k-1,iq) endif endif endif idrop=0 ; del1=dltar(c1,t1,ifunc) goin = .false. do c2=c1+dc idrop=0 ; del2=dltar(c2,t1,ifunc) if (sign(1., real(del1))*sign(1., real(del2)) < 0.) then call nevill (t1,c1,c2,del1,del2,ifunc,cn) goin = .true. exit endif c1=c2 del1=del2 qbq=0.8*b(1) if (c1-0.8*b(1) < 0. .and. c1-(b(mmax)+0.3) >= 0.) exit enddo if (goin) then if (lstop /= 0) then lstop=0 if(k /= 1) then do i=1,k-1 jmax=mode do j=1,mode if(i > imax(j)) then jmax=j-1 exit endif enddo enddo endif if(iq.eq.1) return jmax=iq-1 endif c1 = cn if(c1-b(mmax) <= 0) then cc(k,iq)=c1 if (ifunc == 2) ratio(k,iq)=dltar(c1,t1,3) ! 2 = Ray, 1 = Love c1=c1+0.01*dc imax(iq)=k cycle endif endif if(k*iq-1>0) exit write(*,*) ncount if(ncount == 1)then PRINT *, 'Improper initial value. No zero found in calcul.f' do i=1,mmax print *, b(i), a(i) ,rho(i), d(i), qs_ref(i) enddo endif return enddo enddo ! end of loop for k
Кажется, пора заводить жанр героического эпоса о борьбе отважных программистов и чудовищ из глубины веков. Программист, приносящий Машине хороший выверенный код, отбитый в схватке с ордами перфокарт, должен занять своё место наравне с рыцарями, отбивающих у драконов прекрасных дам. Герой даже будет чернокнижником и некромантом, что только добавит пикантности сюжету.
Желаю приятного субботнего вечера!
Комментариев нет:
Отправить комментарий