%表題   下位基本数学処理ライブラリリファレンスマニュアル
%
%履歴
%\Drireki{
%         91/12/25  酒井 敏
%         92/01/13  酒井 敏
%         92/03/16  酒井 敏・塩谷雅人
%         92/04/22  林祥介   (4.1版)
%         94/04/23  塩谷雅人 (5.z版)
%         95/04/20  塩谷雅人
%        }
%
\section{FORTRAN77規格について}
  % \label{概要.FORT}

FORTRANは1950年代から使われている, 
もっとも古い高級言語である. 
1966年に FORTRAN66 として規格が定められ, 
その後, 1978年に全面的に改訂されて FORTRAN77 となった. 
\vspace{1em}  

このように, 長い歴史を持つ言語だけに, 
FORTRANで書かれたライブラリーは膨大な量にのぼる. 
反面, C言語など新しい言語が数多く存在する現在では, 
古い歴史を引きずった堅い(融通の効かない)言語という印象も免れない. 
しかしながら, 
FORTRANには科学計算用の言語として, 
他の言語にはない使いやすさがある. 
単に「蓄積が大きい」というだけでなく, 
この「使いやすさ」こそ, 
FORTRANが30年以上も使い続けられてきた真の理由である. 
\vspace{1em}  

FORTRANに限らず, どんな言語でもそれを使いこなすのは, 
なかなか大変である. 
幸にも, FORTRAN の仕様は比較的小さい(文法書が薄い)ので
学習は容易なほうであるが, 
それでもFORTRANを``極める''のは容易なことではない. 
MATH1を含む地球流体電脳ライブラリでも, 
入門書に書いてない「奥義」をかなり使っている. 
もちろん, 
これらの知識がなくても電脳ライブラリを使うことは可能である. 
しかし, 電脳ライブラリをよりよく理解し上手に使いこなすためには
是非とも知っておいていただきたい. 
また, 
これらの知識は応用範囲が広いので, 
一般のFORTRANプログラムを書く際にも, 
きっと役にたつはずである. 

\subsection{FORTRANの方言}
  % \label{概要.FORT.方言}

計算機言語には各国の標準機関 (日本ではJIS) 及び国際標準機関 (ISO) により
定められた規格がある. 
FORTRAN も例外ではなく, 
\begin{itemize}
\item {\bf アメリカ規格: } ANSI X3.9-1978 Programming Language FORTRAN
\item {\bf 国際規格: } IS 1539 Programming Languages --- FORTRAN
\item {\bf 日本工業規格: } JIS C 6201 電子計算機プログラム言語 FORTRAN
\end{itemize}
という言語規格があるが, 実質的には全て同じもので, 
いわゆる FORTRAN77 の規格である. 
\vspace{1em}  

言語規格というのは「日本語の文法」の様なもので, 
計算機に自分が意図した動作をさせるためには
これをよく知っていなければならない. 
これは, 当り前のようで, 意外に\.く\.せ\.も\.のである. 
なぜならば, 普通, 多くの人々は FORTRAN を勉強する時, 
英語の勉強のように, 文法を厳しく教わりながら勉強
するわけではないからである. 
どちらかというと, 日本語を覚えたときと同じ様に, 
「見よう見まね」または「試行錯誤」により
FORTRANを勉強するのが普通である. 
そうなると, 多くの人が生まれた土地の方言を, 
方言と気づかずに覚えてしまうのと同じ様に, 
特定の計算機の特定のコンパイラだけが解釈できるプログラムを
平気で書いてしまうことになる. 
\vspace{1em}  

FORTRAN コンパイラは通常, 
標準語としてのFORTRAN77規格 $ + \alpha $ の機能を持っている. 
この$ + \alpha $ の部分は, 拡張機能と呼ばれ, 
このような拡張機能を持つコンパイラは
FORTRAN77 上位互換(アッパーコンパチ) コンパイラと呼ばれる. 
これは, 標準語だけで書かれたプログラムでも, 
$+ \alpha$ の機能を使ったプログラムも, 
どちらもコンパイルできるという意味では
コンパイラの「機能が高い」のであるが, 
逆に$+\alpha$の機能を使ったプログラムは, 
そのコンパイラでないとコンパイルできないことになる. 
したがって, 我々は$+\alpha$の部分を「方言」と呼ぶ. 
\vspace{1em}  

日常会話の中では, 方言は特に問題にならず, 
むしろ標準語では表現できないニュアンスまで
伝えることができて便利である場合が多い. 
これは, 方言が標準語の拡張機能として働いているためであるが, 
ビジネス上の会話で方言を使うと, とんでもない結果をもたらす. 
多くの人は「明日, 東京にいかずー」という約束をしたら, 
明日, 東京にいくべきか否か迷うに違いない. 
(これは中部地方の方言で, 東京にいこう, という意味である)
\vspace{1em}  

これと同じ様に, FORTRANの「方言」は特定の計算機を使いこなすには
便利な場合も多いが, 
むやみに使うと非常に危険である. 
しかし, 生まれた土地を離れたことがないと, 
自分の言葉が方言かどうかわからないのと同じ様に, 
特定の計算機しか使ったことがないと, 
正確なFORTRANの文法を身につけるのは難しい. 
なぜなら, 
いつも使っているFORTRAN77のコンパイラがちゃんとコンパイルしてくれれば, 
そのプログラムはFORTRAN77規格に準拠したプログラムであると
思ってしまうからである. 
しかし, これは明かな間違いである. 
どんなコンパイラでも大なり小なり「方言」を持っており, 
その「方言」を使ったプログラムは, 
たとえ, FORTRAN77規格のコンパイラでコンパイルできたとしても, 
FORTRAN77規格準拠のプログラムではない. 
\vspace{1em}  

そこで, FORTRANで何とかプログラムが書けるようになった初心者も, 
この道10年のベテランも, 
市販の文法書以外に「岩波FORTRAN辞典」を
座右におかれることを強くお勧めする. 
これは単なる文法書ではなく, 
規格が意味するところや, 歴史的背景などが解説してあって, 
辞典としてだけでなく, 読物としても面白い書物である. 
\vspace{1em}  

電脳ライブラリはいくつかの例外を除いて, 
極力この「方言」を避けて書いてある. 
どうしても「方言」使わなければならないようなところは, 
直接「方言」を使わず「電脳標準語」を定義して, 
各プログラムの中では「電脳標準語」を使うようにしている. 
\vspace{1em}  

例えば, SYSLIB の中の {\tt MSGDMP} はエラーを検出したとき, 
システムに依存したエラー処理をおこなって
(たとえば, エラーのトレースバック情報を出力して)
プログラムを強制終了する {\tt OSABRT} というサブルーチンを呼ぶ. 
各コンパイラは必ずといってよいほど, 
この種のサブルーチンを「方言」として持っており, 
{\tt OSABRT} は単にそのコンパイラ固有のサブルーチン(方言)を
呼んでいるだけである. 
しかし, その「方言」を使わず, 
「電脳標準語」である {\tt OSABRT} を使って他のサブルーチンを書いておけば, 
{\tt OSABRT} を各コンパイラ用に書きなおすだけで, 
そのようなプログラム全てを移植できることになる. 
\vspace{1em}  

この移植のしやすさ(可搬性)こそ電脳ライブラリの最大の特徴であり, 
ライブラリ開発者が最も苦労した部分でもある. 
この特徴を最大限に生かすためにも, 
FORTRANの文法をよく理解して, 
不必要な「方言」を使わないよう心がけて欲しい. 
\vspace{1em}  

以下の節では不必要な方言を避けるために知っておくべき知識,知っていると
便利なことがら, そして, プログラミング上の盲点となるような落し穴につい
て多少の解説をしておく.

%-----------------------------------------------------------------------
\subsection{不定の概念}
  % \label{概要.FORT.不定}

FORTRAN 規格の基本的な概念に「不定」という概念がある. 
これは最も重要な概念の一つでありながら, 
最もわかりにくい概念でもある. 
FORTRANを勉強したことのある人ならば, 
文法書の中の
\begin{center}
{\bf ○○の時, 変数××は不定となる. }
\end{center}
という記述を見て, 
首をかしげた経験が1度はあると思う. 
最初にこれを読むと, 「××に乱数が代入される」とか
「数値がメモリ上から消滅する」ということを想像してしまうが, 
決してそういう意味ではない. 
大抵の場合, ○○の時, ××がどうなるか, 
コンパイラはちゃんと知っている. 
\vspace{1em}  

この奇怪な文章を理解するには, 
FORTRANの規格の文章が誰に対する文章であるかということを, 
よく理解しておく必要がある. 
FORTRAN規格というのは, 我々プログラマに対する文章であると同時に, 
FORTRANコンパイラをつくる人に対する文章でもある. 
したがって, この文章は2通りに翻訳できるように書いてあるのである. 
上記の文章を, 
コンパイラをつくる人に対する文章に翻訳すれば, 
\begin{center}
{\bf ○○の時, 変数××が占めていた記憶領域をどのように使ってもよい. }
\end{center}
ということになり, 
我々プログラマに対しては, 
\begin{center}
{\bf コンパイラをつくる人に, 上のように言ってしまったので, 
××がどうなるかわからない.}
\end{center}
ということになる. 
\vspace{1em}  

もし, この文章をコンパイラを「作った人」から「使う人」へのメッセージだと
解釈してしまうと, 
「自分で作っておきながら, わからない, どういうことだ」
と言いたくなってしまう. 
特に, コンパイラに付属の文法書を読んでいると, 
そのような錯覚に落ちいりやすいが, 
FORTRANの文法書とはあくまでも, 
\begin{center}
{\bf コンパイラを作る人と, それを使う人の
     \.両\.者\.の\.責\.任\.関\.係を明確にするための法律}
\end{center}
なのである. 
\vspace{1em}  

「不定」とは逆に, 
規格によって値が保証されている状態を「確定」という. 
規格上, プログラムの中で引用できる変数は「確定」された変数だけである. 
以下, 変数が「不定」となる例である. 

\begin{itemize}
\item {\bf 変数が未定義のとき: }
  \begin{quote}
        これは, 最もわかり易い「不定」の場合である. 
        変数に値が代入なければ, 
        当然その変数にどのような値が入っているかわからない. 
        \vspace{1em}

        コンパイラによっては全ての変数を初期化 (通常は0を代入) する
        ものもある. 
        FORTRAN規格はあくまで「不定」である (どうしてもよい) ので, 
        そのようなコンパイラでも規格に違反しているわけではない. 
        しかし, これは規格によって保証されているものではなく, 
        一種の「方言」であるので, 
        これに頼ってプログラムを書くと後で必ず後悔する. 
        \vspace{1em}

        最初から変数に値を入れておきたい時にはDATA文を使う.
        次の例を参照のこと.
  \end{quote}
\item {\bf サブルーチンの中のRETURN文またはEND文を実行したとき: }
  \begin{quote}
        サブルーチンの中で使われる変数は, 
        いくつかの例外を除いて, 
        サブルーチンの実行が終了した段階で「不定」となる. 
        \vspace{1em}

        これは少々わかりにくいが, 
        要するに, 同じサブルーチンを2回呼んだとき, 
        1回目で値が代入された変数を, 
        2回目に参照しようとしても, 
        1回目の値が保存されている保証はない, 
        ということである. 
        \vspace{1em}

        例えば, 次のプログラムを見て欲しい. 


\begin{Dprogram}
*------------------ main program ----------------
      DO 100 I=1, 100
        CALL BEAT
  100 CONTINUE
      END
*------------------------------------------------
      SUBROUTINE BEAT
      DATA N /0/
      SAVE

      N=N+1
      IF(N.EQ.10) THEN
        WRITE(6,*) ' I hate you !'
        N=0
      ENDIF
      END
\end{Dprogram}

        このサブルーチン{\tt BEAT} は呼ばれた回数({\tt N})を覚えていて, 
        10回呼ばれるごとにメッセージを出力するものである. 
        \vspace{1em}

        変数{\tt N}は{\tt BEAT}が呼ばれる前に, 
        DATA文によって 0 という値で確定している. 
        {\tt BEAT}が1回呼ばれると,  
        この変数{\tt N}に1が加えられるが, 
        もし, 9行目のSAVE文がなければ, 
        16行目のEND文を実行した段階で, FORTRANの規定により
        {\tt N}は不定となる. 
        \vspace{1em}

        SAVE文は, 
        サブルーチン内の変数の値を
        サブルーチンの実行が終っても保持するように
        指示する宣言文である.  
        このプログラムのように変数{\tt N}の値が
        次回の呼び出しまで保持されないと
        正常に動かないようなプログラムでは必ず書かなければならない. 
        \vspace{1em}

        コンパイラによっては (というより多くのコンパイラでは)
        サブルーチンの中で使われる局所変数を保存するが, 
        これも「方言」である. 
        \vspace{1em}

        ただし, DATA文で指定された変数は, 
        \.そ\.の\.値\.が\.書\.き\.換\.え\.ら\.れ\.な\.い\.限\.り, 
        RETURN文またはEND文を実行しても「不定」にはならない. 
        \vspace{1em}

        因みに, 地球流体電脳ライブラリの \GLPGETSET が, 
        掲示板の役目を果たせるのは, 
        このDATA文とSAVE文のおかげである. 
        (\ref{概要.FORT.GLPSET}節参照.)
        \vspace{1em}

        なお, SAVE文を指定すると, 通常, 実行ファイルのサイズは
        大きくなるが, 実行速度は若干速くなることが多い. 
  \end{quote}
\item {\bf 異なる型の変数が結合しているとき: }
  \begin{quote}
        異なる型の変数がEQUIVALENCE文などにより結合しており, 
        その一方の変数が確定したとき, 
        もう一方の変数は不定となる. 
        例えば, 

\begin{Dprogram}
      INTEGER IX
      REAL    RX
      EQUIVALENCE (IX, RX)
 
      RX = 1.
      WRITE(6,*) IX
      END
\end{Dprogram}

        というプログラムで, 
        実変数 {\tt RX} が代入されると, 整変数{\tt IX}の値は不定となる. 
        したがって, 不定の変数を引用しているWRITE文は, 
        文法違反である. 
        \vspace{1em}

        しかし, この時{\tt IX} は, ちゃんと「ある値」を持っている. 
        ただし, 整数や実数の表現形式がコンパイラによって異なるため, 
        この「ある値」が, 
        どのような値になるかFORTRAN規格からは予測できないという意味で
        「不定」なのである.  
        \vspace{1em}

        したがって, 
        文法上このプログラムの結果はわからないが, 
        通常は正常に動作して「ある値」を書き出すことになる. 
%        \vspace{1em}
%
%        この種の文法違反は, しばしば意識的に行われることがあり, 
%        地球流体電脳ライブラリでも, 
%        パラメータを管理するルーチン({\tt GLPGET/GLPSET})などで, 
%        意識的に行われている. 
        (\ref{概要.FORT.GLPSET}節参照.)
  \end{quote}
\end{itemize}

%-----------------------------------------------------------------------
\subsection{ENTRY文}
  \label{概要.FORT.ENTRY}

FORTRANでサブルーチンを書く時, 
いろいろな場合を想定して, 
なるべく汎用的なサブルーチンにしようとすると, 
引数がどんどん増えて困ることがある. 
大事な引数は2--3個なのに, 
どうでもよいような引数がずらずらと並んでいると, 
それだけで {\tt CALL .... }と書く手が重くなってくる. 
\vspace{1em}  

そのような時にENTRY文を使うと解決できる場合がある. 
ENTRY文とは, サブルーチンの途中から実行を始めるための文である. 

\begin{Dprogram}
*----------------- main program ----------------
      REAL X(10)
      .......
      CALL TABLE(X, 10)
      .......
      END
*-----------------------------------------------
      SUBROUTINE TABLE(X,N)
      REAL       X(N)
      DATA       IOU /6/
      SAVE

      WRITE(IOU,'(10F12.4)') X
      RETURN

      ENTRY IOUSET(IOU0)
      IOU = IOU0
      RETURN
      END
\end{Dprogram}

このサブルーチン{\tt TABLE}は
1次元配列{\tt X}を出力するものであるが, 
{\tt TABLE}だけを呼べば, 
DATA文で指定された{\tt IOU}の機番(6)に出力される. 
ファイルなど別の機番に出力させたい場合は, 
{\tt TABLE} を呼ぶ前に{\tt IOUSET} を呼んで, 
{\tt IOU}を書き換えておけばよい. 
つまり, 1つのサブルーチンに, 
印刷すべき変数だけを与える入口と, 
その他のパラメータを設定する入口の, 2つの入口を作るわけである. 
\vspace{1em}  

ここで, 注意すべきことはSUBROUTINE文またはENTRY文で指定された引数は, 
そのSUBROUTINE文またはENTRY文から入った時でないと使えないことである. 
例えば, このプログラムのENTRY文から入った時({\tt IOUSET}が呼ばれた時), 
変数{\tt N} を引用することはできない. 
同じように, 
{\tt TABLE}が呼ばれた時に{\tt IOU0}を引用することはできないので, 
{\tt IOUSET}が呼ばれたときに{\tt IOU}に代入しておかなければならない. 
また, このようなプログラムでもSAVE文とDATA文は不可欠である. 

%-----------------------------------------------------------------------
\subsection{GL{\it p}GET/GL{\it p}SETの構造}
   \label{概要.FORT.GLPSET}

電脳ライブラリの「パラメータ掲示版」である 
SYSLIB の \GLPGETSET は,
毎回指定する必要のないパラメータをうまく隠蔽して見えないようにしておく
という前述のテクニックを極限にまで利用したライブラリなのである.
\GLPGETSET は他のプログラムにパラメータを渡すだけで, 
あとは何もしない. 
その構造は,  以下のようになっている(実際のソースより簡略化されている).
\vspace{1em}

(注: Ver.5 の電脳ライブラリでは, 実行時パラメータによって
内部変数への介入が可能になったため \GLPGETSET は
内部でさらに下請けルーチンを呼ぶようになった.
以下では, Ver.4 の電脳ライブラリにおける \GLPGETSET を
用いて説明をおこなう. 
Ver.5 の下請けルーチンの構造は, 基本的に Ver.4 の
\GLPGETSET と同じである. )

\begin{Dprogram}
*-----------------------------------------------------------------------
*     GLPGET / GLPSET
*-----------------------------------------------------------------------
      SUBROUTINE GLPGET(CP,IPARA)
 
      CHARACTER CP*(*)
      PARAMETER (NPARA=17)
 
      INTEGER   IX(NPARA)
      REAL      RX(NPARA)
      LOGICAL   LX(NPARA)
      CHARACTER CPARA(NPARA)*8
 
      EQUIVALENCE (IX,RX,LX)
      SAVE
 
      DATA CPARA( 1)/'NBITSPW '/, IX( 1)/32/
      ........ 
      DATA CPARA(12)/'LMISS   '/, LX(12)/.FALSE./
      DATA CPARA(13)/'IMISS   '/, IX(13)/999/
      DATA CPARA(14)/'RMISS   '/, RX(14)/999.0/
      ........
 
      DO 10 N=1,NPARA
        IF (CP.EQ.CPARA(N)) THEN
          IPARA=IX(N)
          RETURN
        END IF
   10 CONTINUE
      エラー処理
      STOP
*-----------------------------------------------------------------------
      ENTRY GLPSET(CP,IPARA)
 
      DO 15 N=1,NPARA
        IF (CP.EQ.CPARA(N)) THEN
          IX(N)=IPARA
          RETURN
        END IF
   15 CONTINUE
      エラー処理
      STOP
      END
\end{Dprogram}

ここで, {\tt CPARA}という文字変数にパラメータ名が登録されており, 
その名前に対応した値が 
{\tt IX}, {\tt RX}, {\tt LX} という変数に入っている. 
(これら3つの変数はEQUIVALENCE文により結合されているので, 
実際には1つの記憶領域しか持たない)
\vspace{1em}  

さて, IFALIBの関数{\tt IMAX}は, 欠損値以外の最大値を求めるものであるが, 
{\tt IMAX}は欠損値を示す整数値を引数から得る代りに, 

\begin{Dprogram}
      CALL GLPGET('IMISS', IMISS)
\end{Dprogram}

という文を実行する. 
この時, {\tt GLPGET} では, {\tt CPARA}の中から{\tt 'IMISS'}という名前を探して
(24--29行目) その名前に対応する値{\tt IMISS}として返す. 
この時返される値は, DATA文によって指定されているが, 
必要であれば, あらかじめこれらの値を{\tt GLISET}で変更しておくこともできる. 
\vspace{1em}  

この方法が前の\ref{概要.FORT.ENTRY}節の方法
(各プログラムが{\tt IMISS} 設定用のENTRYを持つ方法)
より優れている点は, 
{\tt GLPGET}が持つ情報を複数のサブルーチンから参照できることである. 
例えば, 欠損値を示す値は何も初期値としては999が与えられているが, 
999がデータ範囲に入っているような場合には, 
これを変更しておかなければならない. 
そのような時にでも {\tt GLPSET} を一度呼ぶだけで, 
欠損値処理をする全てのサブルーチンの動作を制御することが可能になる. 
\vspace{1em}  

%なお, \GLPGETSET ではEQUIVALENCE文を使って
%実変数, 整変数, 論理変数の3つの型を1つのプログラムで扱っている. 
%これは, 前述の通り, 厳密にいえば文法違反である. 
%例えば
%
%\begin{Dprogram}
%      CALL GLRGET('RMISS', RMISS)
%\end{Dprogram}
%%
%という文を実行すると, 
%実引数{\tt RMISS}はサブルーチンの仮引数{\tt IPARA}と結合される. 
%これは, サブルーチンの実引数と仮引数は型が同じでなければならない, 
%というFORTRAN77の文法に違反する. 
%さらに, 26行目で{\tt IPARA}に{\tt IX(14)}を代入するが, 
%{\tt IX(14)}は定義されておらず, 
%その中身はEQUIVALENCE文で結合された{\tt RX(14)}であるので, 
%整数値としては「不定」である. 
%したがって「不定」の値を代入する26行目も文法違反ということになる. 
%\vspace{1em}  
%
%しかしながら, 
%{\tt IPARA}という整変数は, 
%単に値を{\tt RX(14)}から{\tt RMISS}に転送するためだけに使われ, 
%その中身は常に実数値としての意味を持っているので, 
%実際には, まず, 問題を起さない. 
%というより, 1語長のビット(32ビット)全てを使って整変数を表現する
%普通の計算機では, 
%どんなビットの組み合せでも整数値として解釈できてしまうので, 
%{\tt IPARA}の中身が整数として定義されていないこと, 
%すなわち「不定」であることを検出しようがない. 
%つまり, この文法違反を見抜くことはできないはずなのである. 
%\vspace{1em}  
%
%と言うわけで, これはほとんど完全犯罪なのであるが, 
%文法違反であることには変りはないので, 
%この真似をすることは, あまりお勧めできない. 
%ただ, {\tt GLPGET}{\tt /GLPSET}は非常によく使うルーチンであり, 
%MATH1以外のライブラリにも同様のルーチンが多数あるので, 
%その構造はよく理解しておいて欲しい. 

%-----------------------------------------------------------------------
\subsection{文字型変数とその他の変数の違い}
  % \label{概要.FORT.文字}

前節では実変数, 整変数, 論理変数を混同した書き方をしても, 
大きな問題にならなかった. 
これは, FORTRAN規格により, これら3つの型のデータが全て
「1数値記憶単位を占める」と規定されているからである. 
これに対して, 文字型のデータは1文字が「1文字記憶単位を占める」
と規定されている. 
多くの処理系においては「1数値記憶単位」は32ビットの「1ワード」に対応し, 
「1文字記憶単位」は8ビットの「1バイト」に対応するが, 
規格の中で具体的な長さは規定されていない. 
したがって, 文字型とその他の変数をEQUIVALENCE文で結合したりすると, 
規格上, 「数値記憶単位」が「文字記憶単位」のいくつ分に
相当するかわからないため, 
そのような結合は禁じられている. 
\vspace{1em}  

さらに, これは規格上の話だけではなく, 
上記のような1ワードが4バイトに相当する標準的な処理系で, 
ユーザーが「数値記憶単位」と「文字記憶単位」の対応をよく考えた上で
結合したとしても実害が生じる場合がある. 
これは, 
文字型変数が文字単位で任意の長さがとれることから, 
文字型変数の扱い方が, それ以外の変数(実数, 整数, 論理変数)と
大きく異なる場合があるからである. 
特にサブルーチンの引数の渡し方が異なる場合が多い. 
\vspace{1em}  

もともとFORTRAN66の規格には文字型変数はなく, 
文字を扱う場合にはキャラクターコード列(整数)として扱われていた
(4文字を1つの整数として扱っていた).
そのため4文字単位の長さの文字列しか扱えず不便であったが, 
FORTRAN77から文字型変数として正式に認知されるようになった. 
これに伴って, 任意の長さの文字列が扱えるようになるなど, 
文字の扱いが非常に楽になった反面, 
上記のような制約も増えた. 
もっとも, かなり多くのコンパイラでは文字型とそれ以外の変数の
結合が可能であるが, 
これも「方言」の一つである. 
\vspace{1em}  

実際の観測データなどを扱う分野では, 
文字と数字が混在したデータを扱うことも少なくないので, 
伝統的にこのようなEQUIVALENCE文を使っている場合もあるが, 
実際にこのような結合ができないコンパイラもあるので, 
注意が必要である. 
\vspace{1em}  

さらに, 文字型変数は他の型の変数と違って, 以下の例のように, 
同じ文字要素を式の右辺と左辺に書く(3行目)ことが禁じられている. 

\begin{Dprogram}
      CHARACTER*12 CX
      CX = 'DENNOU'
      CX = CX(1:6)//'DENNOU'
\end{Dprogram}

文字以外の変数では, 同様のことが許されているので, 
つい, このような代入文を書いてしまいそうになるが, 
このような場合には, 

\begin{Dprogram}
      CHARACTER*12 CX
      CX = 'DENNOU'
      CX(7:12) = 'DENNOU'
\end{Dprogram}

とすれば, 問題はない. 
\vspace{1em}

%-----------------------------------------------------------------------
\subsection{フォーマットの動的変更}
  % \label{概要.FORT.WRITE}

FORMAT文を用いてWRITE文などで出力する書式を指定する場合, 
書式の中に変数が使えないため, 
「プログラムの中で必要な桁数を判断して, 
出力する桁数を変える」という様な芸当ができない. 
このあたりが, 古い言語の堅さの1つなのであるが, 
「だからFORTRANはきらいだ」という前に, 以下の説明を読んで欲しい. 
\vspace{1em}

FORTRAN77では
WRITE文の書式識別子にFORMAT文の文番号ではなく, 
以下のように, 文字\.変\.数を指定することができる. 
この機能を使えば, 書式の動的な変更が可能になる. 

\begin{Dprogram}
      CHARACTER CFMT*12
      ...
      CFMT = '(T12, F5.2)'
      WRITE(6,CFMT) X
      ...
\end{Dprogram}

この例では, 文字変数{\tt CFMT}に定数を代入しているだけなので, 
動的に書式を変更するようにはなっていないが, 
{\tt CFMT}は\.変\.数であるから, 
プログラムの中で変更するのは容易である. 
\vspace{1em}

桁数などの数値変数を文字変数に書き込むには, 
やはりWRITE文を使う.
WRITE文の出力機番を書くところに文字変数を書くと, 
その文字変数を``内部ファイル''として, 
普通のWRITE文と同じ様に編集, 出力ができる. 

\begin{Dprogram}
      CHARACTER CFMT*12
      ...
      CFMT = '(T12, Fx.2)'
      WRITE(CFMT(8:8),'(I1)') N
      WRITE(6,CFMT) X
      ...
\end{Dprogram}

この例では, 整変数{\tt N}を文字変数{\tt CFMT}の8桁目に書き込み, 
{\tt X}を出力する際の桁数としている. 
このあたりのテクニックはFORTRAN66の時代には使えなかったので
知らない人も多いが, 
知っていると非常に便利である. 

%-----------------------------------------------------------------------
\subsection{多次元配列の記憶順序}
  % \label{概要.FORT.記憶}

FORTRAN77では多次元(7次元まで)の配列を定義できるが, 
実際の記憶装置は多次元構造を持っているわけではなく, 
1次元的に管理されている. 
多次元配列を1次元的に展開する順序は, 
FORTRAN77の規格で定めてあるので, 
多次元配列を1次元配列と結合する
(多次元配列に1次元配列の別名を与える)
ことが可能である. 
例えば

\begin{Dprogram}
      REAL         X(6), Y(2,3)
      COMPLEX      Z(3)
      EQUIVALENCE (X,Y,Z)
\end{Dprogram}

という場合, 
変数{\tt X, Y, Z}はどれも長さが6語(48バイト)の配列で, 
EQUIVALENCE文により同じ記憶領域を占める. 
この時, それぞれの変数の並び方は

\vspace{1em}  
\begin{center}
\begin{tabular}{|c|c|c|c|c|c|} \hline
{\tt X(1)}     & {\tt X(2)}     & {\tt X(3)}     & 
{\tt X(4)}     & {\tt X(5)}     & {\tt X(6)}     \\ \hline
{\tt Y(1,1)}   & {\tt Y(2,1)}   & {\tt Y(1,2)}   & 
{\tt Y(2,2)}   & {\tt Y(1,3)}   & {\tt Y(2,3)}   \\ \hline
{\tt Re(Z(1))} & {\tt Im(Z(1))} & {\tt Re(Z(2))} & 
{\tt Im(Z(2))} & {\tt Re(Z(3))} & {\tt Im(Z(3))} \\ \hline
\end{tabular}
\end{center}
\vspace{1em}  

という様に, 2次元配列{\tt Y}は添字の左側の数字から変るように
1次元的に展開される. 
また, 複素数データ{\tt Z}は2つの実数が並んだ形で展開される. 
したがって, {\tt X(3)} の数値は{\tt Y(1,2)}, {\tt Re(Z(2))} と
全く同じになる. 
\vspace{1em}  

この規則は多くのプログラムで積極的に使われており, 
多次元配列を1次元配列として扱ったり, 
複素数データを実数データを見なして処理したり
することが行われている. 
この規則は, 計算機のハードウェアに近い部分の規則なので, 
計算機によって異なるように思えるかもしれないが, 
FORTRAN77規格で定められた「標準語」である. 
\vspace{1em}  

%-----------------------------------------------------------------------
\subsection{サブルーチンへのデータ引渡}
  % \label{概要.FORT.引数}

引数によるデータの引渡では, 
前述の配列の記憶順序を意識したテクニックがよく使われる. 
MATH1のIFALIB, RFALIB, VRALIBなどでは, 
1次元配列の要素を飛び飛びに使うための引数({\tt JX}など)が必ずある. 
これは, 
多次元配列を同じサブルーチンまたは関数で処理するためのものである. 
これらのルーチンの動作を理解するために, 
以下のプログラムを見て欲しい. 

\begin{Dprogram}
      REAL X(100)
      ......
      CALL SUM(X, 100, 1, XSUM)
      ......
      END

      SUBROUTINE SUM(X, N, JX, XSUM)
      REAL X(*)

      XSUM = 0.
      DO 100 I=1, JX*(N-1)+1, JX
        XSUM = XSUM + X(I)
  100 CONTINUE
      END
\end{Dprogram}

このサブルーチンは1次元配列{\tt X}の和を求めて, 
{\tt XSUM}として返すものである. 
この同じサブルーチンを2次元配列{\tt Y} に対して

\begin{Dprogram}
      REAL Y(10,10) 
      ........
      CALL SUM(Y(2,1), 10, 10, YSUM)
      ......
      END
\end{Dprogram}

という様に使うと
サブルーチン{\tt SUM}は, 以下のように2次元配列要素{\tt Y(2,1)}を
先頭とする記憶領域を1次元配列{\tt X}に割り当てる. 

\vspace{1em}  
\begin{center}
\begin{tabular}{|c|c|c|c|c|c|c|c|c|c|c|} \hline
{\tt Y(2,1)} & {\tt Y(3,1)} & $ \cdots $   & {\tt Y(10,1)} & 
{\tt Y(1,2)} & {\tt Y(2,2)} & $ \cdots $   & {\tt Y(10,2)} &
{\tt Y(1,3)} & {\tt Y(2,3)} & $ \cdots $   \\ \hline
{\tt X(1)  } & {\tt X(2)  } & $ \cdots $   & {\tt X(9)   } & 
{\tt X(10) } & {\tt X(11) } & $ \cdots $   & {\tt X(19)  } &
{\tt X(20) } & {\tt X(21) } & $ \cdots $   \\ \hline
\end{tabular}
\end{center}
\vspace{1em}  

その1次元配列{\tt X}の要素を10 {\tt (JX)} 個おきに10個の和をとると, 
{\tt Y(2,1), Y(2,2), $\cdots$, Y(2,10)}
という具合に, 二次元配列 {\tt Y}の第2添字についての和をとることになる. 
\vspace{1em}

ここで, サブルーチンの中では配列{\tt X}の長さはわからず, 
単に, {\tt X(1)}を先頭とする1つながりのデータとして扱われていることに
注意して欲しい. 
配列がサブルーチンに渡されるとき, 
メインプログラムの配列の中身がサブルーチンの
配列の中身にコピーされるのではなく, 
メインプログラムの中の配列の番地だけが渡され, 
サブルーチンの方はその番地を基準に処理を行うのである. 
このようなことを意識してプログラムを書くと, 
少ない引数で柔軟な処理が可能になる. 
\vspace{1em}  

もう一つの応用例を紹介しよう. 

\begin{Dprogram}
      SUBROUTINE SUM2(X, IMAX, IX, JX, XSUM)
      REAL X(IMAX,JX)

      XSUM = 0.
      DO 100 I=1,IX
      DO 100 J=1,JY
        XSUM = XSUM + X(I,J)
  100 CONTINUE
      END
\end{Dprogram}

これは2次元配列{\tt X(I,J)}の一部の和をとるものであるが, 
これを

\begin{Dprogram}
      REAL X(102,102)
      ........
      CALL SUM2(X(2,2), 102, 100, 100, XSUM)
      ........
\end{Dprogram}

という具合に使うと, $102 \times 102$の2次元配列のデータのうち, 
周辺を残して真中の$100 \times 100 $の部分,
すなわち, (2,2)--(101,101)を対角線とする部分の和をとることができる. 
MATH1のルーチンで, このような使い方を想定しているものはないが, 
GRPH2のコンターを描くルーチンなどで応用すると便利である. 
\vspace{1em}  

なお, サブルーチンに渡すことのできる引数のなかに, 
「手続き名」がある. 
これは, 他の引数と異なり「変数」ではないので, 
動的な変更はできないが, 
これが指定できるおかげで, MATH1の{\tt VRFNA/VIFNA}の様に, 
サブルーチンを呼ぶ際に使うべき関数を指定することが可能になる. 
この場合, 実引数に指定する手続き名は, サブルーチンを呼ぶ
プログラムの中で EXTERNAL文またはINTRINSIC文により
手続き名であることを宣言しておかなければならない. 

%-----------------------------------------------------------------------
\subsection{EXTERNAL文とINTRINSIC文}
  % \label{概要.FORT.EXTERNAL}
EXTERNAL文は, 
そこに指定された関数名が外部関数であることを宣言するものであり, 
INTRINSIC文は, 
そこに指定された関数名が組み込み関数であることを宣言するものである. 
これらは, 上記のように関数名をサブルーチンの実引数として使用する時に
必ず必要なものであるが, 
それ以外も重要な意味がある. 
\vspace{1em}  

{\tt SIN}や{\tt COS}のような組み込み関数は, 
FORTRAN規格によって処理系が用意しなければならないが, 
FORTRAN規格はさらに処理系が規格外の組み込み関数を用意することを許している. 
したがって, 各処理系はFORTRAN規格に定められた組み込み関数よりも多くの
組み込み関数を持っているのが普通である. 
そして当然のことながら, それら拡張された組み込み関数の名前に関しては, 
処理系によって全く異なる. 
ということは, 
FORTRAN規格にない関数名で, 
EXTERNAL文やINTRINSIC文によって宣言されていない関数名は, 
処理系によって組み込み関数として解釈されたり外部関数として解釈されたり
することがある. 
\vspace{1em}  

例えば, 自分で外部関数を定義して, 
それを引用しているつもりでも, 
たまたまその処理系が同名の組み込み関数を持っていた場合には, 
ユーザー定義の関数ではなく, 
処理系が用意した関数が呼ばれてしまう可能性がある. 
関数名の長さは6文字以内と制限されている上, 
人間の発想も多様性に限りがあるので, 
この手の衝突は意外に多い. 
しかも, この種の原因でプログラムが暴走したりすると, 
その原因を突き止めるのはかなり難しい. 
(自分のプログラムそのものには全く誤りがないのだから)
\vspace{1em}  

そこで, 処理系に依存しないプログラムを書くためには, 
そのプログラム単位で引用している外部関数名は全て
EXTERNAL文に指定することが望ましい. 
また, FORTRAN規格にない処理系固有の関数を引用している場合には, 
INTRINSIC文に指定すべきである. 
\vspace{1em}  

なお, 当然のことながら, 
電脳ライブラリが用意する関数は全て「外部関数」である. 
処理系から見れば電脳ライブラリを含めて全て「ユーザープログラム」
であるから, 
自分が書かなかったからといって組み込み関数と混同することのないように
注意されたい. 

%-----------------------------------------------------------------------
\subsection{COMMON文}
  % \label{概要.FORT.COMMON}
前述のEQUIVALENCE文が同じプログラムの中の変数を結合させる
ものであったのに対し, 
COMMON文は異なったプログラム単位(サブルーチンなど)の中の
変数同志を結合させるものである. 
\vspace{1em}

初期のFORTRANでは引数によるデータの引渡しに時間がかかったため, 
COMMON文によりメインプログラムから
サブルーチンへデータを引き渡す方法が多用された歴史がある. 
しかし, 現在ではデータの引渡しに要する時間は, 
どちらの方法でもほとんど変らない. 
それより, COMMON文を使うと, 
どのサブルーチンで値が代入されているのかわかりにくくなってしまうので, 
可読性を高めるという見地から, 
COMMON文によるサブルーチンへのデータ引渡はお勧めできない. 
\vspace{1em}

電脳ライブラリでも余程特殊な理由(内部の作業領域確保など)がない限り, 
COMMON文は使用していない. 
各サブルーチンへのデータの引渡は全て引数を使っている. 

%-----------------------------------------------------------------------
\subsection{その他}
  % \label{概要.FORT.ETC}

その他, FORTRANでプログラムを書く場合の注意を以下に列挙する. 
\begin{itemize}
\item {\bf 整数のオーバーフロー : }
  \begin{quote}
      ほとんどのシステムでは, 
      整変数がオーバーフローしても一切の警告を発する事なく, 
      上位の桁を切捨てて何事もなかったかのように計算を続ける. 
      \vspace{1em}

      これは非常に危険な事であるが, 
      乱数を発生させるプログラムなどでは意識的にオーバーフローさせて
      いる場合もある. 
      \vspace{1em}

      とにかく, 大きな整数を扱う場合には, 
      注意が必要である. 
  \end{quote}
\item {\bf 整数の切捨て及び剰余 : }
  \begin{quote}
      実数値を整変数に代入すると, 当然小数点以下は切捨てられるが, 
      その切捨ては常に「0に近くなるように」切捨てられる. 
      これは, 正の範囲では数学的な切捨てと同じであるが, 
      負の範囲では異なる. ($-1.5$が切捨てられると$-1$になってしまう)
      また同様に, 
      組み込み関数の{\tt MOD}も負の範囲で数学的な剰余の定義と異なる. 
      \vspace{1em}

      数学的な切捨て及び剰余を求めたい場合には, 
      FNCLIB または INTLIB のルーチンを使う. 
  \end{quote}
\item {\bf 実数のべき乗 : }
  \begin{quote}
      べき乗計算${\tt X = B**N}$では, 
      基数{\tt B}の丸め誤差が{\tt N}倍になって現れるので, 
      丸め誤差だけでも{\tt N}が大きいとかなりの誤差を生じる. 
      さらに, 計算の過程で生じる誤差が加わると, 
      思わぬ誤差を生じてしまう. 
      しかも, 処理系によってこの誤差の生じかたが
      大きく異なるのでたちが悪い. 
      \vspace{1em}

      べき乗の精度が気になるところでは, 
      その部分だけ倍精度演算をするなどの安全対策を心がけた方がよい. 
      特に, 基数{\tt B}の精度に気を付けること. 
      たとえ, 1つの計算機で所定の精度が得られても, 
      他の計算機で同じ結果が得られる保証はない. 
  \end{quote}
\item {\bf 定数の精度 : }
  \begin{quote}
      FORTRAN 規格では
      「定数は, それを表す文字列そのものが\.型と値を決める」
      とされている. 
      したがって, 定数の書き方によって値だけでなく, 
      その精度も決められることになる. 
      例えば

\begin{Dprogram}
      DOUBLE PRECISION X
      ....
      X = 3.8*X*(1.-X)
      ....
\end{Dprogram}

      と書くと, せっかく倍精度計算をしているのにも関わらず, 
      定数 3.8 や 1. が単精度の定数なので, 
      全体として倍精度計算の精度が出ない. 
      正確にいうと
      3.8 という定数は{\tt X}との乗算が行われる前に, 
      倍精度実数に変換されるのであるが, 
      その倍精度実数の値はあくまで単精度の 3.8 であり, 
      倍精度の 3.8D0 とは値が異なる. 
      このようなところでは, 必ず

\begin{Dprogram}
      X = 3.8D0*X*(1.D0-X)
\end{Dprogram}

      と書かなければならない. 
      これは, DATA文中の定数も同じである. 
  \end{quote}
\end{itemize}
