はじめに
プログラミング初心者が様々なことを大量に覚えようとする前にまず認識すべき重要な点についてまとめてみました。
対象読者
プログラミングをしはじめた初心者または業務上やむなくプログラミングをしないといけない方(経験なし)で少し書いてみた感じの方
プログラミング言語のライブラリ構造とは
まずいったんプログラミング言語をめぐるワードについて押さえておきます。
プログラミング言語とは?
プログラミング言語とは、人間同士が意思疎通に用いる自然言語に対して、人間がコンピュータに手順を認識させ、人間側もどのような手順であるかを認識しておくため、ある程度人間にもわかるように自然言語の語彙も一部使った形式的な言語のことです。
プログラミング言語のライブラリとは?
コンピュータは0と1に相当する電磁気信号しか認識しないため、0と1の組み合わせからなるマシン語に変換する必要があります。変換される前のプログラミング言語状態のテキストファイルをソースコード、変換後のマシン語状態のバイナリファイルをオブジェクトコードという場合があります。それらのどちらをも再利用可能な形で整理したものがライブラリで、まさにプログラミング言語で書かれた書籍の図書館といった比喩が妥当でしょう。
ファイル格納物理パスとは?
コンピュータには5大装置と呼ばれる基本的なハードウェアが具備されていて、そのうちの記憶装置はプログラミング言語のライブラリの格納場所として使われます。
記憶装置には、メモリなどの内部記憶装置、ハードディスクやSSDといった外部記憶装置があり、通常プログラミング言語のライブラリは外部記憶装置に永続的に格納されており、必要に応じてメモリに一時的に読み込まれます。
外部記憶装置上の情報の格納単位をファイルといい、外部記憶装置上のプログラミング言語のライブラリをライブラリファイルということもあります。
まず、ここで問題になることは、外部記憶装置は永続的な保管装置であることから、プログラミング言語のライブラリファイルが多数、それ以外にも大量のファイルが保管されていることになります。
そこでメモリに読み込む際に、そのファイルは外部記憶装置のどこにあるの?といったことを解決する必要があり、一般的に現代のオペレーティングシステムはディレクトリと呼ばれる階層管理構造を採用しています。
これはまさに、ファイルまでの住所を国名、都道府県名、市町村名、番地、建物名、階・部屋番号などで指し示す住所録といった比喩が妥当でしょう。
ファイル格納物理パスとは、このディレクトリ階層の区切り文字を含むファイルまでの経路情報のことです。ディレクトリ階層のことを、紙のファイルを格納するものという比喩からフォルダということがあります。一般的には木構造となります。つまり、木の根を起点として枝葉までにさまざまな分岐がある構造です。
C:
└─developments
├─ConsoleApplication1
│ └─x64
│ └─Debug
└─cs10
└─9cc2cs
├─.vscode
└─bin
└─Debug
└─net6.0
プログラミング言語のライブラリ構造とは
プログラミング言語のライブラリをどのような構造で整理するかという問題があり、実は外部記憶装置上のどこにライブラリファイルが格納されているかを特定するということとは密接に関係しながら発展してきています。
プログラミング言語からマシン語へ変換するソフトウェアのことをコンパイラと呼ぶことがあります。また、プログラミング言語からマシン語へ変換することを伝統的にコンパイルすると言っていましたが、昨今は(といってもだいぶ昔からではありますが)、大量のライブラリファイルから情報を収集して変換するため、建築作業になぞってビルドすると言ったりすることも多くなっています。
コンパイラにとっては、コンピュータの外部記憶装置に格納されたライブラリファイルがどこにあるかといった情報も必要なのですが、またさらに、ライブラリファイルというファイル単位の中に、どのようなプログラミング言語的な要素、つまり関数や変数やその集合構造のクラスなどが、どんな風に存在しているのかといったことも、効率よくマシン語に変換するには重要な情報なのでした。
プログラミング言語の[名前空間]とは
そこで考えられたのが、プログラミング言語的な要素をディレクトリ階層と同じように、途中の階層名を区切り文字で区切って、木構造で管理することです。
この概念のネーミングが、なぜか住所録、図書館といった現実世界の実在の比喩からではなく、ネームスペースという情報処理用語として独自のものとなったため、その日本語訳も直訳っぽく「名前空間」となって直感的にはわかりにくいですが、プログラミング言語的な要素を特定する住所録みたいなものという認識で問題ないでしょう。
プログラミング言語の言語的な要素とは
ここでは、先に簡単に触れたプログラミング言語の言語的な要素について再確認します。
それは、プログラミング言語ユーザーがプログラミング言語を使って自分で定義する言語要素のことで、変数、関数・メソッド、変数の集合体の構造体、変数と関数・メソッドの集合体のクラスなどがあります。
クラス名の異なる同一名の変数、関数・メソッドはざらにあり、クラス名で装飾することで一意としていることは、同名の人物を姓で区別するのに近いイメージです。
クラス名が一意であればすべての言語要素を特定できますが、クラスもその上の継承元の概念からさまざまな同名のクラスが派生してきて、数が増えてくるとクラス名だけでユニーク性を保つことは困難になっていきます。
プログラミング言語の言語的な要素は厳密に一意に特定できなくてはならない
プログラミング言語的な要素は厳密に一意に特定できなくてはなりませんが、プログラミング言語的な要素を人物に例えると、同名の人物は姓で別人物と認識できますが、同姓同名の人物を個として特定するには住所などの階層情報が使えるのと同様です。
コンパイラはプログラミング言語毎のお約束の記法で名前空間を解釈して、ソースコード上に出現しているプログラミング言語的な要素の定義位置を特定します。
[名前空間]とファイル格納物理パスとの関係
プログラミング言語によっては、プログラミング言語の言語的要素の住所録みたいなものである名前空間と、プログラミング言語のライブラリファイルの住所録みたいなものであるファイル格納物理パスとを、直接対応付ける場合と対応づけない場合があります。
また、言語的な要素としてnamespaceというキーワードを使う使わないということから、ライブラリ管理の階層構造を名前空間と言わない場合がありますが、ここでは広くそのような構造を名前空間と称するとします。
直接対応付ける場合
C/C++の言語仕様の影響を受けているプログラミング言語にJavaがありますが、Javaは直接対応づけられています。
名前空間の階層構造の区切り文字と、オペレーティングシステムによっても異なるディレクトリ階層の区切り文字は異なりますが、ディレクトリ階層の階層名の順序で名前空間を定義する必要があります。
ただし、名前空間の起点となる階層は、ディレクトリ階層上はお約束に従った途中からとなります。
ソースコードもそうですが、コンパイルされたマシン語とプログラミング言語の中間的なオブジェクトコードのファイルも、ソースコードのファイルのディレクトリ階層と1対1でまず生成されます。
名前空間の起点となる階層は、ディレクトリ階層上はお約束に従った途中からとなっていますので、ソースコードのディレクトリ階層とオブジェクトコードのディレクトリ階層は上の階層では分かれています。Javaでは、後者のことをクラスパスと呼んでいます。
直接対応付けない場合
C/C++の言語仕様の影響を受けているプログラミング言語にC#がありますが、Javaより後に設計されたC#は直接対応づけられていません。
名前空間の階層構造はコンパイラが解釈するソースコードや構成情報だけの世界で、ソースコードファイルやオブジェクトコードファイルの格納場所とは無関係です。
オブジェクトコードファイルに対応するソースコードファイルが1対1なのか、1対多なのかも、コンパイラのオプションとなります。
プログラミング言語の例
ソースコード
"Hello World!"がコンソール出力されるJavaとC#のソースコードを例とします。1階層しかありませんが、"HelloWorld"が名前空間に相当します。クラス名はそれぞれ"Program"です。
また、それぞれの言語で、名前空間に相当する階層管理構造の指定はオプションで必須ではありません。
後述するオンラインコンパイラCompiler Explorer1 では、名前空間に相当するpackageを指定したJavaのソースコードは物理的な格納情報が必要となるためコンパイルできなくなります。
Java
Javaは名前空間の指定にnamespaceというキーワードを使わず、packageを使います。packageの管理構造は階層構造となっていますので、概念的には名前空間と言ってよいでしょう。また、Javaはpackageの管理構造が、先に説明しましたとおり、ファイル格納物理パスと関係づけられていますので、コンパイルするときや実行するときは注意が必要です。
package HelloWorld;
import java.lang.System;
/**
* プログラム型
*/
public class Program
{
/**
* メイン
* @param args 引数
*/
public static void main(string[] args)
{
System.out.println("Hello World!");
}
}
C#
C#は名前空間の指定にnamespaceというキーワードを使います。
using System;
namespace HelloWorld
{
/// <summary>プログラム型</summary>
public class Program
{
/// <summary>メイン</summary>
/// <param name="args">引数</param>
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
実行結果
Java
ソースコードをProgram.javaというファイル名をテキストエディタで開いてコピー貼り付けして、C:\developments\java20\HelloworldサブフォルダにUTF-8で保存し、コマンドラインからコンパイルして実行しました。
C:\developments\java20>javac Helloworld/Program.java
C:\developments\java20>java -cp . HelloWorld/Program
Hello World!
C:\developments\java20>
C#
C#はオンラインコンパイラCompiler Explorerで「Hello World!」出力されています。
Executor .NET 7.0.105 (C#, Editor #1)
.NET 7.0.105
Program returned: 0
Program stdout
Hello World!
おわりに
字ばっかりの手抜き記事になってしまいました。ごめんなさい