[ 前へ | 目次 | 次へ ]
Japanese English

モジュールとライブラリ

ここでは、モジュールの概要と、ライブラリのインポートについて扱います。

スポンサーリンク


モジュールとライブラリ

ライブラリとは

ライブラリとは、よく使われる処理や値などがまとめて記述された、部品的なプログラムの事です。

一言でライブラリといっても、メモ書きのような小さなライブラリもあれば、逆にかなり複雑高度な機能を提供してくれるライブラリもあります。 しかし共通するのは、「別のプログラムから呼び出されて、部品として使われる」という点です。

モジュール

さて、これまでの内容では、プログラムは全て1 枚のファイルに記述し、処理内容もその中で完結していました。 しかし、ライブラリを使用したプログラムの場合は、いくつものファイルに記述された処理が、組み合わさって動作します。

この時、各ファイルの記述内容を、1 枚のファイルに結合して実行する事もできます。 しかし、それでは同じ変数名や関数名が競合していた場合に不都合が生じます。 そのため、通常は、ファイルごとに独立した、「 モジュール 」という形で読み込まれて実行されます。

このように書くと抽象的で分かり辛いですが、 VCSSL では基本的に1 枚のファイルが 1 つのモジュールに自動対応する仕組みになっているため、 モジュールとはそれぞれのファイル内容の事だと思っても問題はありません。 A という名前のファイルに書かれたプログラムは「 A モジュール 」という具合です。

実行モジュール と ライブラリモジュール

実行モジュールとは、これまでのように(別のモジュールから使用されるのでは無く)、 普通に直接実行されるモジュールの事です。

それに対してライブラリモジュールとは、そのものを直接実行されるのでは無く、 別のモジュールから読み込まれて使用される、部品的なモジュールの事です。

ライブラリのインポート

ライブラリモジュールに用意された関数を、 別のモジュール(別ファイルに記述されたプログラム)から使用するには、 そのモジュールの先頭領域でimport 宣言を行います:

import ライブラリパス;

ライブラリパスは、実行モジュールからの相対パスを、 ドット記号「.」区切りで指定します。 また、拡張子「.vcssl」は不要です。 ライブラリモジュールのファイルが実行モジュールと同じ場所(フォルダ内) に存在する時は、ライブラリモジュールのファイル名を記述するだけで使用できます。

例えば実行モジュールと同じフォルダ内にある「 TestLib.vcssl 」を使用するなら:


import TestLib;
ImportLib.vcssl

これで、lib.vcssl に記述されている全ての関数と定数が利用可能になります。

また、実行モジュールから見て、「aaa 」フォルダ内の、さらに「bbb 」フォルダ内にある「 TestLib.vcssl 」を使用するなら:


import aaa.bbb.TestLib;
ImportAaaBbbLib.vcssl

とします。

別モジュールの関数や変数を使用する

別モジュールの関数や変数を使用するには、同じ関数名や変数名が競合していない場合、 何も特別な事をする必要はありません。これまでと変わらず、普通に変数や関数を呼ぶ事ができます。

例えば、実行モジュールA から、モジュールB の関数を呼ぶ場合は、以下のようにします:

- 実行モジュール A ( A.vcssl ) -


import B; // ライブラリモジュール B を読み込む

fun();
A.vcssl

- ライブラリモジュール B ( B.vcssl ) -


void fun() {
	print("B.fun が呼ばれました");
}
B.vcssl

上の例で、実行モジュールA ( A.vcssl ) を実行すると、 VCSSL コンソールに

B.fun が呼ばれました

と表示されます。

モジュール A にはfun 関数は宣言されていませんが、fun( )と呼び出しています。 そこでモジュールB に宣言されているfun 関数が呼ばれたわけです。

モジュールの優先度

上に示した例では、関数や変数の名前(関数の場合は引数仕様も)が競合している場合、 問題になる事があります。

例えば、以下のように記述し、実行してみてください。

- 実行モジュール A ( A.vcssl ) -


import B; // ライブラリモジュール B を読み込む

void fun() {
	print("A.fun が呼ばれました");
}

fun(); // ※ fun 関数の名前はモジュール A と B で競合している
A.vcssl

- ライブラリモジュール B ( B.vcssl ) -


void fun() {
	print("B.fun が呼ばれました");
}
B.vcssl

上の例で、実行モジュールA ( A.vcssl ) を実行すると、 VCSSL コンソールに

A.fun が呼ばれました

と表示されます。 つまり、モジュールB のfun 関数では無く、モジュールA のfun 関数が呼ばれたわけです。

このように、関数を呼び出す場合、自身のモジュール内に宣言された関数が最優先されます。

そして、自身のモジュール内に見つからない場合、 import しているモジュール内が探されます。 複数存在した場合は、後にimport されたものが優先されます。

それでも見つからないという場合は、別モジュール間のimport などによって、 処理系に読み込まれた全てのモジュールから探されます。 優先度は、後に読み込まれたものが高くなります。以下に例を挙げます:

- 実行モジュール A ( A.vcssl ) -


import B; // ライブラリモジュール B を読み込む

fun();
A.vcssl

- ライブラリモジュール B ( B.vcssl ) -


import C; // ライブラリモジュール C を読み込む
B.vcssl

- ライブラリモジュール C ( C.vcssl ) -


void fun() {
	print("C.fun が呼ばれました");
}
C.vcssl

上の例で、実行モジュールA ( A.vcssl ) を実行すると、 VCSSL コンソールに

C.fun が呼ばれました

と表示されます。

このように、モジュールA からはモジュールB しかimport していないにも関わらず、 モジュールC の fun 関数を呼ぶ事ができています。 これは、モジュールB がさらにモジュールC をimport していて、処理系がモジュールC の存在を知っているためです。

ただしこの機能は、モジュール数が増加すると優先度の把握が難しくなり、可読性も低下します。 そのため、原則として、使われる側のモジュールは、 使う側のモジュールで明示的にimport しておく事が推奨されます。 つまり上の例では、モジュールA でも「 import C ; 」しておく事が推奨されます。

所属モジュールの明示

これまでに示した、優先度による自動的なモジュール判定は、小規模なプログラムの場合には便利です。

しかしモジュールの数が多くなってきたり、第三者が開発したライブラリモジュールを組み合わせて使用するような場合、 どのモジュールにどういった名前・引数仕様の関数があるのかを、常に完璧に把握しておくのは困難です。 その場合、関数名の競合が発生しているのを見逃してしまうと、 意図していたのと異なるモジュールの関数をコールしてしまうミスを招きかねません。

このような場合、関数名や変数名の前にドット記号「.」区切りでモジュール名を明示する事ができます:

- 実行モジュール A ( A.vcssl ) -


import B; // ライブラリモジュール B を読み込む
import C; // ライブラリモジュール C を読み込む

B.fun();
C.fun();
A.vcssl

- ライブラリモジュール B ( B.vcssl ) -


void fun() {
	println("B.fun が呼ばれました");
}
B.vcssl

- ライブラリモジュール C ( C.vcssl ) -


void fun() {
	println("C.fun が呼ばれました");
}
C.vcssl

上の例で、実行モジュールA ( A.vcssl ) を実行すると、 VCSSL コンソールに

B.fun が呼ばれました
C.fun が呼ばれました

と表示されます。

このようにモジュールを明示する事で、競合を見落として意図しないモジュールの関数を呼んでしまうのを防げる上に、 可読性も向上します。

アクセス修飾子

上で述べた所属モジュールの明示は、意図しないモジュールの関数・変数を呼んでしまう(アクセスする)事を防ぐ手段ですが、 いわば「 呼ぶ側 」の手段です。

これに対して「 呼ばれる側 」でアクセスを制御する手段も存在します。それがアクセス修飾子です。 VCSSLのアクセス修飾子には、「 private 」と「 public 」の2種類が存在します。

例えば、モジュール外からは呼んでほしくない関数・変数や、全く呼ぶ必要が無い関数・変数があったとします。 こうした場合、関数・変数宣言の先頭に「 private 」と記述する事で、モジュール外からは呼べなくなります(存在しないのと同一に扱われます)。

逆に、関数・変数宣言の先頭に「 public 」と記述すれば、モジュール外から自由に呼ぶ事ができます。 ただし、VCSSLではこれまでのように普通に関数や変数を宣言すると、標準で public になります。

具体的な例を見てみましょう:


// この変数はモジュール外からアクセスできない
private int a;

// この変数はモジュール外から自由にアクセス可能
public int b;


// この関数はモジュール外から呼べない
private void funA() {
	println("funA が呼ばれました");
}

// この関数はモジュール外から自由に呼べる
public void funB() {
	println("funB が呼ばれました");
}
Access.vcssl

上の例では、変数 a 及び関数 funA はアクセス修飾子「 private 」が付いているので、モジュール外から呼ぶ事はできません。

逆に、変数 b 及び関数 funB はアクセス修飾子「 public 」が付いているので、モジュール外から自由に呼ぶ事ができます。

ところで、「 public 」をわざわざ付けても、普通にアクセス修飾子を省略して宣言した時の挙動と同じなので、機能的にはあまり意味はありません。 しかしながら明示的に付けておく事で、「 private 」を付け忘れたのでは無く、安心して外部から呼んでも大丈夫な変数・関数である事を表明する事ができ、可読性に貢献します。

モジュールの唯一性

モジュールには唯一性が保たれます。具体的には、複数回import されたモジュールでも、 その実体は 1 つしか無いという事が保証されます。例えば以下のように記述し、実行してみてください:

- 実行モジュール A ( A.vcssl ) -


import B; // ライブラリモジュール B を読み込む
import C; // ライブラリモジュール C を読み込む

C.x = 2;

B.fun();
A.vcssl

- ライブラリモジュール B ( B.vcssl ) -


import C; // ここでもライブラリモジュール C を読み込む

void fun() {
	println(C.x);
}
B.vcssl

- ライブラリモジュール C ( C.vcssl ) -


int x = 0;
C.vcssl

このプログラムを実行すると、VCSSL コンソールに

2

と表示されます。 これはつまり、モジュールAからimport したモジュールC と、モジュールBからimport したモジュールCは、 同一のものであるという事です。

ライブラリモジュール内での相対パスの扱い

ライブラリモジュールの中から、何らかのファイルへのアクセスを行う場合は、ファイルパスの指定に注意が必要です。

というのも、ライブラリモジュールからファイルへアクセスする際のファイルパスには、そのライブラリから見た相対パスでは無く、実行モジュールから見た相対パスを指定しなければならないからです。

例えば、ライブラリの中でさらに別のライブラリをインポートする場合、ライブラリパスには、 実行モジュールから見た相対パスを指定する必要があります。 さらに、ライブラリの中でファイル入出力関数を使用する場合や、画像のロード等を行う場合にも、実行モジュールから見た相対パスを指定する必要があります。

各モジュールのグローバル領域実行順序

本文書の前半でもずっと行ってきた通り、VCSSL では、モジュールのグローバル領域に通常の処理を記述する事ができます。

この時に問題になるのが、各モジュールのグローバル領域が、どのような順序で実行されるかです。 VCSSL では、import の階層をツリー状に表した際、深い階層から順に実行されていきます(実行モジュールが最も浅い階層となります)。 同じ深さの階層では、先にimport されたものから順に実行されます。

グローバル定数の初期化順序

const キーワードを付けて宣言された変数は、初期化後に値を変更できない定数となります:


const int CONSTANT_VALUE = 255;
Const.vcssl

グローバル領域でこのように宣言された定数、いわゆるグローバル定数は、 グローバル領域の実行よりも早いタイミングで初期化されます。

そのため、グローバル領域の実行が始まった時点では、 全てのモジュールのグローバル定数は初期化されています。

別のファイルの内容を、1つのモジュール内に埋め込む include

import に似ている機能に、include というものがあります。使い方は、import の変わりにそのまま include と記述するだけです。

include は、別ファイルを独立したモジュールとして読み込むのではなく、 ファイルの記述内容を、その場所にそのまま埋め込む機能です。 つまり、include したファイルと、include されたファイルは、内容が結合され、同じ1 つのモジュールとして読み込まれます。

この機能は、似ているけれども微妙に異なるモジュールが複数必要な場合などに、 共通部分を別ファイルに記述して使用するためにサポートされています。 VCSSL では、同一モジュール内で、同名・同引数仕様の関数を複数宣言した場合、 後に(ファイル下方で)宣言したもので上書きされます。 これを利用して、include したファイルの関数を上書きし、微妙に異なるモジュールを作る事ができます。



スポンサーリンク



Japanese English
この階層の目次
RINEARN からのお知らせ
※ VCSSL は RINEARN が開発しています。

VCSSLのサポートAIが登場! ただし実用品質には ChatGPT 有料アカウント(Plus)での利用推奨
2025-08-19 - プログラミング言語VCSSLについての質問対応や、コーディング作業を手伝ってくれるAIさんが登場しました! 使用までの流れや推奨事項を解説し、実際の回答例や生成コード例などもたくさん紹介します!

各ソフトやVCSSLの英語版ドキュメント整備がほぼ完了
2025-06-30 - RINEARNでは2年前から、AIの補助による英語版ドキュメントの大幅拡充計画を進めてきました。今回、主要ドキュメント&コンテンツの英訳がほぼ完了し、一応の目標水準に達しました。詳細をお知らせします。

VCSSLの最新版をリリース: 外部プログラムとの連携機能を少し強化、他
2025-05-25 - VCSSL 3.4.52をリリースしました。外部プログラム(C言語製の実行ファイル等)との連携機能を少し強化し、文字化け対策やOS判別などを可能にしました。他にも細かい機能追加があります。詳細をお知らせします。

VCSSLの最新版をリリース、Java 24上での非互換な挙動を対処
2025-04-22 - VCSSL 3.4.50をリリースしました。Java 24環境上でのネットワークドライブ関連のファイルパス解決で、従来環境とは異なる挙動が生じていたのを解消しました。詳細をお知らせします。

リニアングラフやVCSSLの最新版をリリース、目盛りの位置や内容を自由に指定可能に!
2024-11-24 - リニアングラフ3D/2Dを更新し、自由な位置に、自由な表記内容の目盛りを描けるようになりました! 併せて、Java言語やVCSSLでの、プログラム制御用APIも拡張しています。詳細をお知らせします。

Exevalator 2.2 をリリース、TypeScript 対応によりWebブラウザ上で動作可能に
2024-10-22 - オープンソースの式計算ライブラリ「Exevalator(エグゼバレータ)」の2.1をリリースしました。新たに TypeScript に対応し、Webブラウザ上での式計算にも使えるようになりました。詳細を解説します。

アシスタントAI作成の舞台裏(その2、作成編)
2024-10-12 - アシスタントAIの作成方法解説の後編です。実際にChatGPTの「GPTs」機能を用いて、アシスタントAIを作成する手順や、独自の知識をもたせたり、精度を出すためのノウハウなどを解説しています。

アシスタントAI作成の舞台裏(その1、基礎知識編)
2024-10-07 - アシスタントAI作成方法解説の前編です。今回はまず、アシスタントAIを作る前に抑えておきたい、基礎知識を延々と解説しています。そもそもLLM型AIとはどんな存在か? RAGとは何か? 等々です。

ソフトの利用をサポートしてくれるアシスタントAIを提供開始!
2024-09-20 - RINEARN製ソフトの使い方の質問応答や、一部作業のお手伝いをしてくれる、アシスタントAIを提供開始しました。ChatGPTアカウントさえあれば、誰でも無料で使用できます。使い方を解説します。

Exevalator 2.1 をリリース、新たに Visual Basic に対応
2024-07-28 - オープンソースの式計算ライブラリ「Exevalator(エグゼバレータ)」の2.1をリリースしました。今回から、新たに Visual Basic(VB.NET)でも使用できるようになりました。詳細を解説します。