コンテンツにスキップ

プログラミング/共通知識

出典: フリー教科書『ウィキブックス(Wikibooks)』

このページでは、プログラミングにおいて頻出である共通の知識を説明する。

概要

[編集]

プログラミングはWindows(ウィンドウズ)、macOS(マック)、GNU/Linux(リナックス)、ChromeOS(クロームブック)、*BSD(ビーエスディー)どのOSでも、行うことができる。 スマートフォン向けのOS、iPhone(アイ フォーン) や Android(アンドロイド)などで行うこともできる。

※注釈:以下では、電子計算機を指す用語として「コンピュータ」、「コンピューター」が混在している場合があるが、どちらも同じ意味である。

「プログラミング」とは?

[編集]

プログラム」はコンピュータにしてもらう指示をまとめたものです。「プログラミング」は、「プログラム」を作成することです。私たちはプログラムを作ってコンピュータに実行させることでコンピュータに仕事をしてもらいます。現代のコンピュータは、内部では0と1だけの2進数で動作しています[1]。2進数で表現されていることを特に「バイナリ」と呼びます。それに合わせて、指示は0と1のみで表現される「機械語」で与える必要があります。しかし機械語は直接読み書きすることは困難です。そこで、より私たちの話す言語に近い文法や単語からなる”記号”で指示を記述して、それをあとで2進数に訳して実行させるという方法が生まれました。この「翻訳する」作業を「コンパイル」と呼びます。

文字入力

[編集]

プログラミングをするからには、文字の入力ができなくては始まらない。以下は、文字に関する概念を簡単に述べる。

バイナリファイルとテキストファイル

[編集]

概要: バイナリファイルとテキストファイルは人間都合の呼びわけである。

コンピュータはHDD、SSDなどの記憶媒体にファイルを保存する。「テキストファイル」とは、文字として解釈することを想定されたファイルを指す。対となる概念として、「バイナリファイル」がある。Windowsではテキストファイルには.txtという拡張子を付けることが多い。プログラムを作るとき、拡張子は言語に応じたものを付けて区別する。

テキストエディタ

[編集]

概要: テキストエディタは怖くない。

文字を入力するときは、専用のアプリを起動する。以下に不完全なOSに付属しているエディターのリストを示す:

  • Windows: メモ帳 (notepad.exe)
  • macOS: vi, emacs
  • GNU/Linux: nano

さらに、emacsやvim、Atom、VSCodeもテキストエディタと呼ばれる。エディタによっては、シンタックスハイライトと呼ばれる色分けをする機能や、折りたたみ機能など、特色ある機能を搭載している場合がある。テキストエディタのインストール方法については、ここでは取り扱わないので、各エディタのマニュアルを参照すること。

ワープロソフトは使えない

[編集]

概要: マイクロソフト社のWord(ワード)のようなワープロソフトでは、テキスト以外に付属するデータがあるため、プログラミングには使えない。

ワープロソフトで保存されるのはバイナリファイルであることが非常に多い。ワープロソフトで文章を保存しても、文章がそのままテキストとして保存されるわけではない。そのファイルの拡張子を「.c」に書き換えてC言語として再解釈しようとしても、コンパイルエラーになってしまうだろう。なぜエラーになるかというと、ワープロソフトには、メタデータと呼ばれるフォントの種類、大きさ、色、位置関係の情報など、文字そのもの以外の情報もふくまれているからだ[2]

入力モード

[編集]

概要: 大半のプログラミング言語において、ほとんどの記号、数字は、半角英数字で入力しなければならない。表示させたい文章などは、その限りではない。

プログラム中に出現する記号は、ほとんどの場合、半角英数字で入力する必要がある。大抵のOSでは直接入力モードに切り替える必要がある。OSによっては、半角モードと直接入力モードとは、べつのモードの場合がありますので、気をつけましょう。なお、Windows7では、半角英数が直接入力です。

print("おはよう")

というコードがあったとしたら、「print」「(」「"」は直接入力モードで入力します。「おはよう」を全角モードで入力します。そして、ふたつめの「"」「)」は直接入力モードで入力します。つまり、例外的に日本語表示をしたい部分だけを除いて、原則的に直接入力モードで、入力することになります。

拡張子

[編集]

概要: ファイルの最後の「.」より後ろをw:拡張子と呼ぶ。ファイルの名前にはわかりやすくするために拡張子をつけておく。

拡張子(かくちょうし)はテキストエディタで保存する時つける必要がある。拡張子は、プログラミング言語の種類によって変わるため、それぞれのプログラミング言語についてのマニュアルを参照する。C言語で保存するには、「.c」を末尾につける。sampleという名前のファイルをC言語として保存する場合には、ファイル名をsample.cとする。

コマンドラインと慣れ親しむ

[編集]

概要: キャラクターインターフェイスのみ用意されている場合は、コマンドラインで諸々の準備を行わなければならない。

  • Windowsの場合: cmdまたはpowershell
  • macOS場合: /bin/zsh[3]
  • GNU/Linuxの場合: /bin/bashが多い
  • FreeBSD,NetBSD,OpenBSDなどのBSD系Unixの場合: /bin/tcshほか

真っ黒な画面、白い文字。恐怖心を覚えるかもしれないが、避けては通れない道である。プログラミング言語によっては扱う必要がないかもしれない。パッケージマネージャーによるインストールなどはここでは扱わないので、各OSのマニュアルを参照。

  • コマンドラインから抜ける: exit

プログラミング言語と言語処理系

[編集]

言語処理系とは、プログラミング言語で記述されたプログラムをコンピューターで実行するためのソフトウエア・システムです。 言語処理系は#インタプリター#コンパイラーに大別されます。

インタープリター

[編集]

インタープリターとは、コンピュータプログラムを解釈して実行するソフトウェアです。コンピュータが理解できる機械語にコンパイルすることなく、プログラムを直接実行することができます。

インタープリターは、プログラムの実行中に逐次的にプログラムのコードを読み込み、それを解釈して実行します。これに対して、コンパイラーはプログラム全体を処理して、機械語のオブジェクトコードを生成するため、実行が速くなりますが、実行前に完全なコンパイルが必要です。

インタープリターは主にスクリプト言語や、インタラクティブな開発環境で使用されます。スクリプト言語では、CやJavaなどのような静的型付け言語よりもコードが簡潔になる場合があります。また、インタラクティブな開発環境では、プログラムを逐次的に開発し、インタープリターのリアルタイムなフィードバックを受けられるため、効率的な開発が可能になります。

一方、インタープリターの欠点として、実行速度がコンパイラーに比べて遅いことや、プログラムが実行される際に繰り返し同じコードの部分を解釈するため、コードを効率的に実行することができないことが挙げられます。

ただし、インタープリターは実行時に「どの部分が多く実行されたか?」「分岐命令でどちらに多く分岐したか?」などの実行時の統計情報を容易に手に入れることができます。 これらに基づき実行時コンパイラー(JIT)による最適化が可能となります。よって、一概にインタープリターがコンパイラーより実行速度が劣るとは言えません。

言語処理系にインタープリターを採用している言語には

などがあります。

コンパイラー

[編集]

コンパイラーとは、プログラミング言語で書かれたソースコードを予め機械語に変換するソフトウェアのことです。 この処理をコンパイルと言います。主要な実装がコンパイラーのプログラミング言語をコンパイル型言語と呼びます。

利点:

  1. 実行速度:コンパイル型の言語は、ソースコードをコンパイルしてバイナリ形式に変換するため、実行速度が高速であります。このため、アプリケーションやゲームなどの高速処理が必要なソフトウェアを作成する場合、コンパイル型の言語が適しています。
  2. エラーチェック:コンパイルは事前に行われるため、文法エラーや型エラーなどの問題を事前に検出することができます。これにより、デバッグの時間を大幅に短縮することができます。
  3. 最適化:コンパイル型の言語は、コンパイル時に最適化処理を行うことができます。これにより、実行時に処理速度を向上させることができます。

欠点:

  1. 開発時間:コンパイル型の言語で開発を行う場合、変更を加える度にコンパイルを行う必要があるため、開発にかかる時間が長くなることがあります。
  2. ポータビリティ:コンパイル型の言語は、プラットフォーム依存コードを生成するため、OSやCPUごとにコンパイルしなければならない場合があります。これは、ポータビリティに影響を与える可能性があります。

言語処理系にコンパイラーを採用している言語には

などがあります。


バイトコード型

[編集]

プログラミング言語処理系における「バイトコード型」とは、ソースコードをバイトコードと呼ばれる中間言語に変換し、実行する方式をとることが特徴的です。バイトコードはCPUに依存しないアーキテクチャに従ったバイナリフォーマットであり、翻訳処理の後の直接実行ではなく、擬似的な実行環境であるバイトコード仮想マシンで実行されます。これにより、異なるプラットフォームやOSでも同じバイトコードを実行することができ、移植性が高くなります。PythonやJava、.NET Frameworkなどがバイトコード方式を採用しています。

バイトコード型には

  • Oコードマシン
    • 対象言語:BCPL
  • Euler P code
    • 対象言語:Euler
  • UCSD p-System
  • Java仮想マシン
  • 共通言語ランタイム
    • 対象言語:C++/CLI, C#, F#, Visual Basicほか

などがあります。

Pコードマシン

[編集]

pコードマシン(portable code machine)とは、pコード(仮想の中央処理装置(CPU)のアセンブリ言語またはマシンコード)を実行するように設計された仮想マシンのことです。

この用語は、Java仮想マシン(JVM)やMATLABの事前コンパイルされたコードなど、すべてのこのようなマシンに一般的に適用され、特定の実装にも適用されます。

最も有名な実装は、Pascal-Pシステムのp-Machineであり、特にUCSD Pascal実装があります。p-codeのpは、ポータブルよりも擬似(pseudo)であるという意味で解釈され、擬似マシンの命令を意味する擬似コード(pseudo-code)という用語も使われます。

このコンセプトはBCPLのO-codeとEulerのP codeとして、約1966年に初めて実装されました。

しかし、p-codeという用語は、1970年代初頭に初めて現れました。初期のp-codeコンパイラーには、Kesav V. Nori、Urs Ammann、Kathleen Jensen、Hans-Heinrich Nägeli、Christian Jacobiによる1973年のPascal-Pコンパイラーと、Niklaus Wirthによる1975年のPascal-Sコンパイラーがあります。

pコードに翻訳されたプログラムは、仮想のCPUの動作をエミュレートするソフトウェアで解釈するか、プログラムが実行されるCPUのマシンコードに翻訳して実行することができます。十分な商業的関心があれば、CPU仕様のハードウェア実装が構築されることもあります(例えば、Pascal MicroEngineやJavaプロセッサなど)。

Oコードマシン

[編集]

O-codeとは、BCPLコンパイラーによって生成される中間言語のことです。その後、このO-codeを特定のコンピュータで動作するためのマシンコードに再コンパイルします。このコンパイル方法によって、元のBCPLコンパイラーを新しいマシンにポーティングしやすくなり、広く使われるようになりました。このアイデアは現在の多くのコンパイラーでも使用されています。ただし、多くの場合、それらのコンパイラーは、元々の方法とは異なり、仮想マシンでO-codeの相当部分を解釈することがほとんどです。

O-codeマシンは、1960年代後半にMartin Richardsによって開発された仮想マシンで、BCPLのマシン非依存性を実現するために作成されました。O-Codeマシンのコンセプトは、BCPLコンパイラーを使用してO-codeを出力することです(Oはオブジェクトの略)。その後、O-codeは、通常はマシン固有のコードにコンパイルされます。このアイデアは、後に発行されたコンパイラーで使用されました。たとえば、一部のPascalコンパイラーではp-codeが使用され、Javaコンパイラーによって生成されるJVM用のバイトコードが使用されました。

O-codeは、BCPLコンパイラーの開発時に、一般的なコンパイルの問題と、マシン固有の実装の問題とを分離することができ、ポーティングが容易になりました。その結果、BCPLは多くのマシンで利用できるようになりました。

Euler P code

[編集]

P codeは、Euler言語のバイトコードであり、Eulerプログラムをコンパイルして生成されます。P codeは、Eulerインタプリタで実行されます。

Euler言語は、数式処理に特化したプログラミング言語です。P codeは、数式処理を高速化するために開発されました。P codeには、数値計算とシンボリック計算を両方サポートする命令が含まれています。

P codeプログラムは、16ビットの命令と、オペランドとして使用される1〜3バイトの定数で構成されており、一般的なプログラムに必要な命令をカバーしています。P codeには、演算、論理、制御、スタック操作などの命令が含まれています。命令が実行されると、スタックに結果がプッシュされます。スタックは、計算途中の値や戻り値を格納するために使用されます。

P codeは、コンパクトなバイトコード形式であり、高速な処理を可能にします。また、Eulerインタプリタは、P codeを直接実行できるため、コンパイル時のオーバーヘッドがなく、動的なコンパイルも可能です。

Java仮想マシン

[編集]

Java仮想マシン (JVM) は、Javaバイトコードを実行するための仮想マシンです。Javaバイトコードは、Javaプログラムを機械語に変換することなく、クロスプラットフォームで実行可能な形式であります。

JVMは、Javaプログラムが実行される際に、バイトコードを解釈し、ハードウェアやオペレーティングシステムに依存しない同じ結果を得ます。JVMは、メモリ管理、例外処理、動的なリンク、セキュリティなど、Javaプログラムの実行に必要なさまざまな機能を提供します。JVMは、Garbage Collectionを使用してJavaプログラムのメモリに自動的に割り当てられたオブジェクトを解放し、メモリリークやセキュリティ問題を回避します。

JVMは、Javaの重要な要素であり、Javaの大部分の魅力的な特徴を可能にしています。Javaのクロスプラットフォームの特性は、JVMによって実現されています。JVMは、Javaプログラムの実行に必要な環境を提供し、Javaプログラムが広く使用されている理由の1つです。

また、JVMを利用するJava以外の言語として、Groovy、Scala、Kotlin、Clojureなどが登場しています。

Java仮想マシンのアーキテクチャは、その後の共通言語ランタイムなどに影響を与えました。

共通言語ランタイム

[編集]

共通言語ランタイム(Common Language Runtime、略称CLR)とは、マイクロソフトが開発した、.NET Frameworkの一部である実行環境のことです。CLRは、プログラマがC#、Visual Basic .NET、F#などの.NET対応言語で開発したアプリケーションを実行するために必要な環境を提供します。

CLRは、様々な機能を提供しています。例えば、メモリの自動管理や、例外処理、スレッドの管理、セキュリティの管理などがあります。これらの機能によって、開発者はアプリケーションの開発において、より高い生産性と品質を実現することができます。

また、CLRは、プログラムの実行速度にも影響を与えます。CLRによって、プログラムがJIT(Just-In-Time)コンパイラーによってコンパイルされます。JITコンパイラーによるコンパイルによって、プログラムの実行速度が向上することがあります。

CLRは、Windows上で.NET対応アプリケーションの実行環境として使用されます。開発者は、Visual Studioなどの開発ツールを使って、CLRを使用したアプリケーションの開発を行うことができます。

分類上の例外

[編集]
コンパイル型言語を逐次実行、あるいはインタプリタ型の言語をコンパイルして使用する場合
コンパイル型言語に分類されるC言語のソースコードをインタプリタで逐次実行することがある。
逆に、インタプリタ型言語に分類されるのBASICのソースコードをBASICコンパイラーでコンパイルし生成された実行ファイルを実行することもある。

この様に、言語処理系の主要な実装があるいは多くの場合は最初の実装が、インタプリタなのかコンパイラーなのかで分類しているので、例外が生じる余地があります。

字句解析器・構文解析器

[編集]

字句解析器

[編集]

字句解析器( Lexical Analyzer ) とは、コンピュータプログラムの中で文字列からトークン列( Token Stream )を作るためのプログラムです。 字句解析は、コンピュータ言語の文法規則に基づいて行われます。字句解析の目的は、ソースコードをトークン列に分割し、それを後続の解析プロセスで利用することです。

字句解析器の主な機能は、入力文字列をトークン( Token )に分割することです。すなわち、入力文字列をトークンに分解し、それぞれに意味を持たせることです。 トークンは、予約語、演算子、識別子、定数などの要素に分類されます。字句解析器は、毎秒数百万回以上のトークンの判別を行うことができます。

一般に、字句解析器は次の手順に従って動作します。

  1. 入力文字列を1文字ずつ読み込む。
  2. 読み込んだ文字がトークンの開始文字である場合、そのトークンの種類を判定します。
  3. トークンの判定が終わったら、そのトークンをトークン列に追加します。
  4. 入力文字列の終端まで、1 - 3 の手順を繰り返す。

字句解析器は、プログラミング言語処理系の中で非常に重要な役割を果たしています。 例えば、コンパイラでは、字句解析器が入力ソースコードをトークン列に変換し、構文解析器( Parser )によって文法的に正しいかどうかをチェックします。 また、インタプリタでも、字句解析器が入力文字列をトークン列に分割し、それを解釈するプロセスで利用します。

構文解析器

[編集]

構文解析器( Parser )は、プログラミング言語などの形式言語を解析し、その構文構造を特定の形式(構文木など)で表現するシステムです。構文解析は、自然言語処理やコンパイルなどの分野で活用されます。

構文解析器は、トークンと呼ばれる基本的な言語要素を入力として取り、文法規則に基づいて構文解析を実行します。構文解析の結果、入力されたテキストが文法的に正しいかどうかが判断され、構文木などの特定の形式で表現されます。

構文解析器は、主に2つの方法で実装されます。一つは、手動で文法規則を記述し、それに基づいて構文解析を行う方法です。もう一つは、自動生成された構文解析器を使用する方法です。自動生成された構文解析器は、文法の定義から直接作成されます。

構文解析器は、プログラミング言語やデータベースクエリ言語など、様々な分野で使用されます。例えば、プログラムのコンパイルに際しては、ソースコードをトークンに分解し、構文解析器によって構文解析された結果をもとに、最終的な実行ファイルを生成します。

構文解析は、自然言語処理の分野でも重要な役割を果たします。例えば、文書を形態素に分割し、それらから構成される文節を特定するような解析を行うことができます。また、質問応答システムなどでは、ユーザーの入力を構文解析して、それに適切な回答を返すことが求められます。

文法の衝突
文法の衝突とは、同じ文法内に2つ以上の文を解釈するための構成要素がある場合に発生します。これは、句読点や語彙、文脈などから文の意味を解釈する人間の脳においても起こります。
例えば、「彼女は彼女が愛している男の子にプレゼントを贈った」という文には文法の衝突があります。この文の意味を考えると、2つの「彼女」は同一人物であると想定するのが自然ですが、文法的に正しく解釈すると、2つの「彼女」が別々の人物を指している可能性があります。同様に、「男の子に」という句も、誰が愛しているのかが明確ではないので、文法の衝突が起こります。
文法の衝突は、コンピュータプログラムのパースにおいても同様に問題を引き起こすことがあります。プログラムのパーサーは、入力文を構造化して解釈するのが目的であり、文法の解釈が曖昧な場合、正しい解釈ができないことがあります。このような問題を回避するために、プログラマーたちは文法の衝突が起こりにくいデザインを採用し、文法の正確性を確保するためにコンピュータプログラムを書きます。
懸垂IF
実際の文法の衝突例として懸垂IFをあげます。とは、次のようなコード例で見られるコンパイラの問題の1つです。
if (a == b)
    if (b == c)
        x = 1;
else
    x = 2;
このコードは a と b が等しい時、もし b と c も等しい場合、x に 1 を代入し、そうでない場合は x に 2 を代入します。
しかし、上のコードでは else が2番目の if 文に対してのみ関連付けられているため、想定している結果が得られない可能性があります。
コンパイラは、この構文を解析するためには、「elseは、より近いifと結合する」とし懸垂IF問題を解決したわけです。

文法

[編集]

変数と代入

[編集]

まず、プログラミングにおける「変数」とは、情報を格納するために使用されるメモリ上の場所を指します。 データを扱う必要がある全てのプログラムで変数が使用され、データを一時的に保持するために使用されます。

変数には値を代入することができます。このプロセスは「代入」と呼ばれます。代入演算子(=)を使用して変数に値を代入することができます。例えば、以下のようになります。

x = 10
変数xに整数10を代入します。ここで、xは変数名であり、10は値であり、=は代入演算子です。この3つはトークンですが、間にある空白はトークンではありません。

静的型付け言語の例を示します。C言語では、変数は使用する前にデータ型を宣言する必要があります。例えば、以下のようになります。

int x;
x = 10;
変数xをint型として宣言し、その後にxに値10を代入します。

一方、動的型付け言語の例を示します。Pythonでは、変数にはデータ型の宣言が必要ありません。以下のように使用できます。

Python
x = 10
print(x)   # 10
x = "abc"
print(x)   # abc
変数xに整数10を代入します。
次にxの値を参照すると 10 が返ります。
変数xに文字列"abc"を代入します。
次にxの値を参照すると、今度は "abc" が返ります。

このように動的型付け言語では、変数に代入する値の型は実行中にも変えることが出来ます。

静的型付け言語との違いを強調するため、「変数xに整数10を束縛します」のように「代入」ではなく「束縛」という用語を使うことがあります。

静的型付け言語と動的型付け言語
プログラミング言語は、静的型付け言語と動的型付け言語に大別されます。両者の間で、代入や変数の意味論が異なるので明確に区別する必要があります。
静的型付け言語
具体例
Fortran, COBOL, C言語, C++, Java, Go, Rust
特徴
変数は型を持つ
代入は、変数と結びついた記憶領域への右辺値のコピー
動的型付け言語
具体例
Lisp, JavaScript, Python, Ruby
特徴
変数に型はなく、変数に束縛された値(=オブジェクト, =インスタンス)が型を持つ
代入は、変数への値の束縛

BASICのような変数名に型情報を持った言語や、関数型言語の一部のように変数そのものを持たない言語もあります。

プログラミング言語の学習において、その言語のパラダイムにおける型システム・変数システム(オブジェクトシステム)の理解は初学時の重要な要素です。

実行順序

[編集]

文は出現順序が先の方から順に実行されることがほとんどです(逐次実行[4])。(C言語では)式の後に; (セミコロン) を後置すると文(式文)となります。

文や式の様な構文要素の分類は、プログラミング言語によって異なります。

数値

[編集]

ソースコードで記述する整数リテラルおよび小数リテラルは、特に指定がない限り、10進数であることが一般的です。また、ほとんどの場合、IEEE 754に準拠した有限の桁数を持つ浮動小数点数が使用可能です。浮動小数点数に関しては、有限の精度の近似値として表現され、誤差を含みます。

過去の編集で、浮動小数点数による誤差ケタ落ちとしていましたが、桁落ちとは、丸め誤差を含む非常に近い大きさの浮動小数点数同士で減算を行ったときに、有効数字が減る現象のことを言い有限の仮数部と指数部で表すことによる誤差とは違う概念です。また一般に浮動小数点数は2進数が採用されているので、十進数では有限桁数で表現できても浮動小数点数では循環小数になることがあります(例えば、 0.110 = 0.000110011001100…2)。

関連項目

[編集]

脚注

[編集]
  1. ^ フラッシュメモリーのMLC/TLCに代表されるw:多値論理など、特殊なアーキテクチャを採用しているコンピューターを除く
  2. ^ 実は多くのワープロソフトではテキストファイルを保存する方法が用意されており、それで保存すればプログラミングもできないこともありません。ただしワープロソフトはプログラミングを意図した設計になっていないので、ワープロソフトでやるメリットはほとんどありません。
  3. ^ macOS 10.15 Catalina(2019) からディフォルログインシェルが、それまでの bash から zsh に変わった。
  4. ^ 現実のプロセッサーは、主に実行効率のために、投機実行やアウトオブオーダー実行などで逐次ではなく並列で、またインストラクションの順序を組替えて実行しています。しかし、プログラマーから観た意味論は変更しない範囲で行われます(一部のRISCプロセッサーのディレイスロットの様な例外はあります)。