10
 構造体・型紙 





 
 
構造体の定義
 
 

 構造体は次のような形式で定義する。

構文=
<構造体名> 構造体             <変数名>  ・・・             <変数名>  ・・・         <集合名> ・・と ・・と ・・         <集合名> ・・と ・・と ・・     全体は         ・・・と ・・・と ・・・
 構造体は変数を寄せ集めたものである。この形式で多次元配列も定義できる。
 多次元配列との違いは、この寄せ集め方法が自由である点にある。きれいな配列構造を成している必要はなく、とにかく変数を好きな形に集め、さらにその集めたものを集める‥という方法で、最小構成の変数単位で、あるいはその上位の集合単位でアクセスできる。
 学校のクラスの成績簿を例にとってみよう。
 簡単にするため、生徒数は30人で、学科は、数学、英語、国語の3教科だけとすると、次の図のようなデータを想定することができる。

担任名
山田太郎
生徒名 数学 英語 国語
佐藤花子 75 80 80
・・・・
・・・・
〜略〜
・・・・

 この図を見ると、まず担任教師の名前の欄がある。次に 1 人につき、生徒名、および数学・英語・国語の書く成績の、合計して4項目からなる個人データがあり、この個人データが30人分集まってクラスの成績を構成していると考えることができる。
 この構成をプログラムとして記述したものが、次に示す構造体定義である。
成績簿は 構造体       担任名は 文字列実体 長さ 40桁           生徒名は 文字列実体 長さ 40桁           数学の成績は 変数           英語の成績は 変数           国語の成績は 変数       個人データは         生徒名と 数学の成績と 英語の成績と 国語の成績     全体は       担任名と 30名分の 個人データ。
 この定義例では、字下げや改行などを行なっているが、これらは任意であり強制されるものではない。ただし、構造を見やすくするためには、このような見掛けの工夫、とくに低位のレベルの定義を右の方に記述し、上位定義になるに従って左に記述する...といった表記を勧める。
 構造体定義は階層構造になっている。下位レベルは変数定義であるが、それを組み合わせた定義も入っている。上記の「個人データ」「成績簿」の二つは変数ではなく、変数の集まりである。これを「集合」と呼び変数と区別する。トップレベルとなる構造体名は集合名でもある。
 構造体の定義におけるポイントを下記に示す。




 上記のうち特に注意するのは、配列状のものを作る場合である。例えば、
数学の成績は 30人分の 変数
 と記述してはならない。変数定義の段階では普通に単純変数として定義し、その上位にて、その変数の集合を定義する形で個数指定することになる。


 
 
構造体のアクセス
 
 

 構造体のアクセスは大きく分けて、変数のアクセスと集合のアクセスに分けることができる。そして、そのおのおのについて、添え字が付く場合と、付けない場合がある。
 添え字が付くのは、その変数や集合が、その上位集合定義において「○○個の ・・」という形式で個数指定されている(=配列状になっている)場合である。
 先の成績簿を例にとれば、「担任名」は個数指定がないので添字無しである。この場合、アクセス時には、まったく普通の変数として引用することになる。つまり、
担任名を 表示し ・・・・
 というように記述してよい。
 次のものはいずれも添字付きでアクセスする必要がある。
生徒名(5番)を ・・し      ←変数 数学の成績(5番)を ・・し    ←変数 個人データ(5番)を ・・し    ←集合

 
構造体内に定義された集合をアクセス
 
たとえば、5番の生徒の情報をすべてクリアするのに、
生徒名(5番)を クリアし 数学の成績(5番)を クリアし 英語の成績(5番)を クリアし 国語の成績(5番)を クリアし
のように書いても良いのだが、以下のように集合のクリアを使えばもっと簡単になる。生徒1人分のデータ(集合)を定義したことによるメリットである。
個人データ(5番)を クリアし
 上記の集合としてのアクセスは、配列名を添字無しで記述したとの挙動と同じである。「クリア」のほかに「入れる」が使える。以下のよう書けば、個人データをまるごと別の個人データとして複写できる。
個人データ(5番)を 個人データ(6番)に 入れ
 集合を単に書いた場合(読み出し)においては、その集合の文字列情報(アドレスとバイト数をパックしたもの)がスタックに積まれる。つまり、上記例の代入は、
個人データ(5番)をつみ、  →文字列情報      (文字列情報が指すデータを) 個人データ(6番)に 入れる
というような内部処理として扱われる。


 
多次元配列を実現する場合
 
 次に、添え字が複数になる場合、つまり、多次元配列状になる場合の表記について説明する。
 まず、先ほどの成績簿を次のように拡張する。
 (成績簿を拡張したもの)
成績簿は 構造体       担任名は 文字列実体 長さ 40桁           生徒名は 文字列実体 長さ 40桁           数学の成績は 変数           英語の成績は 変数           国語の成績は 変数       個人データは         生徒名と 数学の成績と 英語の成績と 国語の成績     クラスデータは       担任名と 30名分の 個人データ   全体は     4クラス分の クラスデータである
上記で分かるように、「個人データは ・・・」の行までは先と同じであり、違うのは、その直後に「クラスデータ」という上位集合を定義した点である。「個人データ」の30名分の集合として「クラスデータ」という上位集合を定義している。
 上記の拡張版の成績簿では、「生徒名」、「数学の成績」、「英語の成績」、「国語の成績」、「個人データ」が多次元配列のような扱いとなるので、これらの要素の引用では添え字を2つ記述する必要がある。
 例えば、3組の5番の生徒の数学の成績は、
数学の成績(3組、5番)を 数値表示し ・・・
のような引用になる。
 ここで「大きな重みの添え字から並べる」という重要な規則を知っておいて欲しい。これは我々を実社会での、「何年、何組、何番」とか、「○○丁目、○○番地」というように、大きな区分から先に記述するという慣習に合わせたものである。


 
構造体の要素数
 
 第5章の配列の解説において、「要素数」という特殊単語の使い方を述べたが、これは構造体内の関数や集合に対しても適用することができる。

構文=
<要素名> 要素数 → <要素数>      注:「要素名」は、構造体あるいは型紙の変数名あるいは集合名
 配列の要素数は分かりやすいが、構造体の要素数は少し注意を要する。
 「○○ の要素数」と書いた場合、その要素を参照する直上の集合定義で幾つの個数を指定しているかを示すものとなる。もし個数指定が無く単に列挙された場合の要素数はとなる。
 さきほどの成績簿拡張版において要素数を参照した例を次に示する。
 注意するのは、「数学の成績の 要素数」が1だからといって、要素「数学の成績」をアクセスする際に添字が付かないわけではない点である。考え方としては、要素名「数学の成績」から上位にたどっていく際、最初に見つかった個数指定(=30名分)が「数学の成績」の右端の添字に書く最大値となる。そして、さらに上位 をたどっていくと、4の個数指定(=4クラス分)があるので、これも「数学の成績」の添字に対応し、右端から数えて2つ目となる。最終的に、
             ↓生徒番号(最大で30)     数学の成績(n、m)を ・・・           ↑クラス番号(最大で4)
のようになる。
 ちなみに、最上位集合である構造体名の要素数を参照するとが返される。


 
 
退避用の構造体定義と代入
 
 

 次のような特殊な構造体定義をおこなうことでできる。

構文=
<構造体名> 構造体 ○○ 同じサイズ。     注:「は」、「と」は必須送り仮名     注:「○○」は、既に定義されている構造体名あるいはその内部の集合名
 上の宣言により、定義済みの構造体あるいはその内部の集合と同じサイズの構造体を確保することができる。
 このような構造体は、単に領域を確保することだけが目的なので、その内部の明細を書く必要はない。
 例として次の定義を行なってみよう。
成績簿控えとは 構造体 成績簿と 同じサイズ。
 上の定義により、「成績簿」と同じサイズの構造体が新たに定義される。そのような構造体の使用目的は、構造体の全体をここへに代入することで、その内容を退避することにある。
 構造体同士は次のように丸ごと代入できる。
成績簿を 成績簿控えに 入れる


 
 
型紙
 
 

 構造体を定義すると、内部の変数や集合の組み合わせとして「型」の定義が行われるが、同時にそのための「領域確保」も行われる。この2つを分離し、「型の定義」と「領域確保」を別にするための概念が「型紙」である。
 この方法をとる利点としては、1つの型紙を使って複数の構造体を定義(実体を獲得)することができることが挙げられる。
他の言語、たとえばC言語で言うところの構造体は、Mindにおいては型紙に相当する概念であり、この点で用語の違いがある。

 
型紙の定義
 
 型紙は構造体の機能のうち、型だけを定義するものである。
 定義の規則はほとんど構造体定義と同じであり、次のような形式で定義する。

  (型紙の定義)

構文=
<型紙名> 型紙             <変数名>  ・・・             <変数名>  ・・・         <集合名> ・・と ・・と ・・         <集合名> ・・と ・・と ・・     全体は         ・・・と ・・・と ・・・
 型紙はそれだけでは実体を持たないので、上記と別に実体を確保するための宣言が必要となる。そのためには、「○○は 構造体」の拡張機能として以下のような記述を使う。

  (実体の定義)

構文=
<構造体名> 構造体 <型紙名>。

  (例)
 例として、Mindのライブラリ内に標準で定義されている型紙である「日時型」を示す。
            [型紙の定義] 日時型は  型紙  ↑型紙名    年は  変数         月は  変数         日は  変数         曜日は 変数      日付情報は 年と 月と 日と 曜日         時は  変数         分は  変数         秒は  変数      時刻情報は 時と 分と 秒    全体は 日付情報と 時刻情報。
 上記で「日時型」という型紙名を命名している。名前の末尾に付けた「型」は強制されるものではないのだが、それが型紙であること(構造体定義においては、型紙を参照した構造体定義であること)が一目して分かるので薦めたい。
 次いで、上の型紙を適用した構造体定義を以下に示す。
            [型紙の適用] 日時情報1は 構造体 日時型。 日時情報2は 構造体 日時型。             ↑型紙名
上のように、同じ型紙を使って複数の構造体(実体)を定義することができる。

 
型紙のアクセス
 
 前記例の「日時型」という型紙の要素をアクセスする場合であるが、もしこれが通常の構造体要素であれば、
(通常構造体の要素の場合) 年を 数値表示し ・・・
のように、単に要素変数を書けば良いのだが、要素変数が型紙のそれである場合は、次のように記述する必要がある。
(型紙要素の場合) 日時情報1の 年を 数値表示し ・・・
つまり、

構文=
<親構造体> <型紙要素>を ・・・       ↑親構造体に付ける送り仮名「の」は必須である
のような親子ペア表記が必要になるので注意したい。。
 型紙要素が配列状である場合は要素名に添字を付けることになるが、次のような表記となる。
成績簿型は 型紙     ・・・・     ・・・・。 ○○高校の成績簿は 構造体 成績簿型。 △△とは     ・・・・・     ○○高校の成績簿の 生徒名(5番)を ・・・            ↑添字のある型紙要素をアクセスする場合はこのように

 
親構造体のスタック渡し
 
 「<親構造体>の <型紙要素>」という親子ペアの表記が基本ではあるものの、親構造体(の情報)はスタックに積まれていれさえすれば良いので柔軟なプログラムを書ける。
 以下のプログラムは、成績簿の実体である構造体を(正確にはそれを指す情報を)スタックから渡され、その成績簿に属する生徒名をすべてリスト表示する処理単語の例である。(プログラムを簡単にするため、生徒数は構造体定義にある上限の30名と仮定する)
 (プログラム例)
生徒名一覧表示とは (構造体情報 → ・)         成績簿情報は 構造体情報     成績簿情報に 入れ     個人データの 要素数を     回数指定し     ↓生徒番号の表示         回数を 二桁で数値表示し 空白表示し         成績簿情報の 生徒名(回数)を 一行表示し     繰り返すこと。      ↑生徒名の表示 ○○高校の成績簿は 構造体 成績簿型。 △△とは     〜略〜     ○○高校の成績簿を 生徒名一覧表示する     〜略〜。
 上記では「構造体情報」という見慣れない変数型が使われている。これは文字列情報(定義文で言えば「○○は 文字列」)とほぼ同じものなのだが、特に構造体を指す情報であることが分かるようにこのようなデータ型名称を設けている。(構造体の実体がスタックに積まれるわけではなく、そこを指す情報がスタックに詰まれて本処理単語が呼び出されるという想定である)
 このような処理単語の作りにしておけば、型紙「成績簿型」を適用した複数の成績簿(の実体)に対して、1つの共通の処理単語のみで生徒名のリストを表示できる。


 
暗黙の構造体(型紙との連携で)
 
 プログラム中に型紙の要素名だけを(親構造体とペアにせずに)書くと文法エラーになり、以下のようなエラーが示される。
(ソースコード) 数学の成績(5)を 数値表示し      ↓コンパイル結果 (エラーメッセージ) 型紙は単独では参照できません。
 Mindの文法としてはこの通りなのだが、特別に、このような型紙要素の単独記述を可能にする方法があり、それが「暗黙の構造体」と呼ばれるものである。
 暗黙の構造体は次のように定義する。

構文=
<構造体名> 暗黙の構造体 <型紙名>。
 上記のように、型紙を適用した構造体定義と似ている。この定義をおこなうと、いま定義した構造体が、引用した型紙と結びついた暗黙の構造体として認識され、型紙要素を単独で書いた場合に、強制的にその構造体を親構造体として扱うようになる。
 Mind標準のライブラリで実際に使っているケースを例として示すことにする。第8章にある「日時を得る」の解説から再度抜粋する。


構文=
日時を得る → ・
 「日時を得る」の実行によってセットされる変数群は以下の通りである。

構文=
年は  変数。    (1900〜) 月は  変数。    (1〜12) 日は  変数。    (1〜31) 時は  変数。    (0〜23) 分は  変数。    (0〜59) 秒は  変数。    (0〜59) 曜日は 変数。    (1〜 7 ‥ 1=日曜、7=土曜)      上記は厳密には通常変数ではなく構造体の要素変数なのだが、      とりあえずは簡単に「通常変数として参照できる」と理解して欲しい。

 上の解説では、「年」や「月」といった変数は「とりあえずは簡単に通常変数として参照できる」と補足しているが、実はこれらの変数は以下のように定義された型紙の要素である。
日時型は  型紙         年は  変数         月は  変数         日は  変数         曜日は 変数      日付情報は 年と 月と 日と 曜日         時は  変数         分は  変数         秒は  変数      時刻情報は 時と 分と 秒    全体は 日付情報と 時刻情報。
 本来であれば、型紙要素の単独記述(たとえば「年」とだけ書くこと)は許されないのだが、暗黙の構造体の概念を使うことでそれが可能になっている。
 Mindの標準ライブラリ内には以下のような定義がおこなわれている。
日時情報は 暗黙の構造体 日時型。
 上記定義によって、構造体「日時情報」は、型紙「日時型」と結びつき、日時型の要素変数を単独記述した場合、たとえば、
年を 数値表示し ・・
のように書いた場合、結果的に次のように書いたのと同じに扱われるようになる。
日時情報の 年を 数値表示し ・・
 「年」や「月」といった変数が「とりあえずは簡単に通常変数として参照できる」のはこの仕組みがあるからである。つまり、本来は型紙を適用した構造体定義であるにもかかわらず、あたかも通常構造体と同じような扱いを受けるため、結果的に型紙要素を単独でも(=通常構造体の要素のように)書けるようになるのである。