「プログラミング/共通知識」の版間の差分
→変数: 比較的古くからある言語では、大文字・小文字を区別しない傾向にあります。具体的には、Fortran・COBOL・Lisp・ALGOLやBASICは大文字・小文字を区別しません。理由は1つではありませんが、有力な理由の1つに小文字がコードがコード化されたASCIIが制定される1963年より前、あるいは同時期に言語設計と言語処理系の実装が行われたから、と考えられます。むしろC言語(1972)が比較的古い言語であるにもかかわらず小文字を区別していることが特異的といえます。 タグ: 2017年版ソースエディター |
→字句解析機・構文解析器: 構文解析器と字句解析器を取違える間違えがあった。;構文の衝突に関する言及。lex yacc あるいはキーワード解析の完全ハッシュ関数化に関しては、コラムにまとめる予定。 タグ: 2017年版ソースエディター |
||
212 行 | 212 行 | ||
この様に、'''言語処理系の主要な実装が'''あるいは多くの場合は最初の実装が、インタプリタなのかコンパイラなのかで分類しているので、例外が生じる余地があります。 |
この様に、'''言語処理系の主要な実装が'''あるいは多くの場合は最初の実装が、インタプリタなのかコンパイラなのかで分類しているので、例外が生じる余地があります。 |
||
== 字句解析器・構文解析器 == |
|||
==レキシカル・アナライザ== |
|||
== 字句解析器 == |
|||
ソースコードは通常の文字列ですが、文字列のママ構文解析を行うことは出来ないので、文字列からトークンを切りだします。 |
|||
ソースコードは通常の文字列ですが、まずは、空白での区切りや既知のキーワードあるいは区切子に鑑み、文法上の最小単位の列に還元します。 |
|||
⚫ | |||
この文法上の最小単位をトークンと呼びます。 |
|||
トークンとは、プログラミング言語のキーワード・識別子・デリミターなどのこれ以上分解すると意味が変わってしまう構文単位で、自然言語学の語に近い概念です。 |
|||
⚫ | |||
=== |
=== 構文解析器 === |
||
字句解析器の出力は、トークンの羅列(トークン列)です。 |
|||
このトークン列を先頭から読出し、プログラミング言語の構文に当てはまるか、パターンマッチングを行い、一致したパターンに従い内部表現(構文木など)に変換します。 |
|||
このようなプログラムのことを、トークン列 |
このようなプログラムのことを、トークン列から構文を解析するという意味で、構文解析器(こうぶんかいしゃくき;''parser'' パーサー)と呼びます。 |
||
構文解析にあたって、解析中のトークン列の注目している部分が、2つ以上の構文にマッチすることがあります(構文規の衝突;例えば懸垂if)。 |
|||
この様な場合は、優先順位や結合方向を文法で規定することにより、一意にマッチする構文が定まるよう、実装上の工夫が必要です。 |
|||
字句解析と構文解析を別々に行うことが可能なプリグラミング言語もありますが、多くのプログラミング言語では(文法に曖昧さがあったり、字句に対し意味論が重複しているなどの理由で)字句解析と構文解析は連携 |
字句解析と構文解析を別々に行うことが可能なプリグラミング言語もありますが、多くのプログラミング言語では(文法に曖昧さがあったり、字句に対し意味論が重複している<ref>例えば、C++では、キャストと関数呼出しあるいは初期化の構文に衝突があり、型や関数の定義状況との照会を行わないと、構文の特定ができません。</ref>などの理由で)字句解析と構文解析は連携し実装されています。 |
||
== 文法 == |
== 文法 == |
2022年6月30日 (木) 10:55時点における版
このページでは、プログラミングにおいて頻出である共通の知識を説明する。
概要
プログラミングは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
言語と言語処理系
コンピュータが認識できるのは機械語のみであることは先で述べました。しかしプログラミング言語は文法に沿った言語で書かれています。それらの書かれた文章をソースコードと呼びます。 プログラムを処理するためのソフトウェアを言語処理系と呼び、言語処理系はインタプリタとコンパイラに大別されます。
インタプリタ
インタプリタとは、プログラミング言語で書かれたソースコードや中間式を逐次解釈しながら実行するプログラムのことです。 スクリプト言語と呼ばれる言語は、おおむねインタプリタ方式を採用しています。 言語処理系の主要な実装がインタプリタなプログラミング言語をインタプリタ型言語(Interpretive language)と呼びます。
インタプリタ型言語には
などがあります。
手軽に書いてすぐ実行できるというメリットがある反面、プログラムの実行時に毎回解釈をする必要があるのでプログラムの実行速度はコンパイル方式のプログラミング言語に実行速度が劣る場合があります[4]。 インタプリタ方式を採用した言語の処理系は、ソースコードを直接実行するのでプログラムを書き換えると、即時に実行結果もそれに応じて変化します。
コンパイラ
ソースコードを予めに機械語(ネイティブコード)に翻訳し、機械語のファイルを実行するという方法もあります。 プログラムのソースコードコードを機械語に変換することを、コンパイルと言います。 コンパイルするためのソフトウェアのことをコンパイラといいます。 言語処理系の主要な実装がコンパイラなプログラミング言語をコンパイル型言語(Compiled language)と呼びます。 また、実行時コンパイラ(JIT; Just-In-Time Compiler)と区別するため、事前コンパイラ(AOT;Ahead-Of-Time)と呼ばれます。
コンパイル型言語には
などがあります。
コンパイル型言語では、ソースコードが書かれたファイルと、実行ファイルとは、別のファイルです。 そのためソースコードを変更してもコンパイルしなければ変更が反映されません。 コンパイル型言語の実行ファイルは、機械語で書かれています[5]。 コンパイル型言語のプログラムの実行では、機械語で書かれた実行ファイルをそのまま実行するので処理が高速です。 実行ファイルは、それぞれのハードウェアやオペレーティング・システムに合わせた形式になっています[6]。 Windowsをターゲットにした実行ファイルをGNU/Linuxにコピーしても実行できません[7]。 実行ファイルから元々のソースコードを取り出すこと (デコンパイル) は一般に困難です。 機械語にはそのハードウェア対する命令だけが書かれており、ソースコードは機械語には書かれていません[8]。 機械語に変換された実行ファイルは、配布するプログラムのソースコードを秘匿することが可能になる特徴があります。 プログラムのコードをコンパイルしたファイルを実行ファイルにまとめる前のファイルをオブジェクトファイル言います。 1つの実行ファイルが、複数のオブジェクトファイルから構成されることがあります。
(コンパイル) (リンク) ソースコード1 ⇒ オブジェクトファイル1 ⇒ 実行ファイル ソースコード2 ⇒ オブジェクトファイル2 ↑ : ↑ ソースコードn ⇒ オブジェクトファイルn ↑
という流れになります。
どちらでもない言語、両方できる言語
ここまでプログラミング言語をインタプリタ型言語とコンパイル型言語に分けて説明してきましたが、どちらにも当てはまらないものや、両方の方法で実行できるものも存在します。
中間コード型
ソースコードをコンパイルしてから中間コード・ファイルを生成し、実行は中間コード・インタプリタが行う形式があり、 この様な形式を中間コード型と呼びます。
中間コード型には
- UCSD p-System
- 対象言語:UCSD-Pascal
- Java
- MS-CLI
- 対象言語:C++/CLI, C#, F#, Visual Basicほか
などがあります。
Java
Javaはソースコードを、Java仮想マシンが解釈できる中間言語 (Javaバイトコード) にコンパイルします。そして実行時に、その中間言語を読み込みそれを実行するという仕組みです(中間コード・インタプリタ)。中間言語ファイルは機械語から独立しているため、どのハードウェアでも使うことができます。かわりにユーザーは、Java中間言語を実行するソフトウェア (JRE; Java Runtime Environment) をインストールします。そして、そのJavaのランタイムは、Windows、macOS、GNU/Linux系列といったそれぞれのOSごとで動作するように開発・維持されています。ユーザーから見れば、Javaのように中間言語を生成するプログラミング言語は、どのOSにも依存せず動作するので理想的なように見えます。しかし、ユーザーから見て統一的なAPIを提供しつつ、統一的なAPIとそれぞれのOSの違いを吸収する部分を実装することは非常に手間がかかります。
JVMを利用するJava以外の言語、Groovy, Scala, Kotlin, Clojureなども登場しています。
両方できる言語
また、コンパイルして実行することもインタプリタで実行も両方できる場合がある。Python、Go言語、Haskellは、その例です。
例外
- コンパイル型言語を逐次実行、あるいはインタプリタ型の言語をコンパイルして使用する場合
- コンパイル型言語に分類されるC言語のソースコードをインタプリタで逐次実行することがある。
- 逆に、インタプリタ型言語に分類されるのBASICのソースコードをBASICコンパイラでコンパイルし生成された実行ファイルを実行することもある。
この様に、言語処理系の主要な実装があるいは多くの場合は最初の実装が、インタプリタなのかコンパイラなのかで分類しているので、例外が生じる余地があります。
字句解析器・構文解析器
字句解析器
ソースコードは通常の文字列ですが、まずは、空白での区切りや既知のキーワードあるいは区切子に鑑み、文法上の最小単位の列に還元します。 この文法上の最小単位をトークンと呼びます。 トークンを切り出すプログラムのことを字句解析器(じくかいせきき;lexical analyzer レキシカルアナライザー)あるいはトークナイザー(Tokenizer)と呼びます。
構文解析器
字句解析器の出力は、トークンの羅列(トークン列)です。 このトークン列を先頭から読出し、プログラミング言語の構文に当てはまるか、パターンマッチングを行い、一致したパターンに従い内部表現(構文木など)に変換します。 このようなプログラムのことを、トークン列から構文を解析するという意味で、構文解析器(こうぶんかいしゃくき;parser パーサー)と呼びます。 構文解析にあたって、解析中のトークン列の注目している部分が、2つ以上の構文にマッチすることがあります(構文規の衝突;例えば懸垂if)。 この様な場合は、優先順位や結合方向を文法で規定することにより、一意にマッチする構文が定まるよう、実装上の工夫が必要です。
字句解析と構文解析を別々に行うことが可能なプリグラミング言語もありますが、多くのプログラミング言語では(文法に曖昧さがあったり、字句に対し意味論が重複している[9]などの理由で)字句解析と構文解析は連携し実装されています。
文法
代入
注釈: a = a + 1
が何を指すかご存知だろうか。a
が変数、 x = y
が変数x
に式y
の評価結果を代入していることが理解できるのであればこの文章は読み飛ばしても良い。
プログラミングと数学では、=
が指す意味が違う。数学では、=
は等号でありとすることで、とが等価であることを意味する[10]。プログラミングにおいてa = b
は変数a
に式b
の評価結果を代入する。たとえば、a = a + 1
で、a
の値が2
だったとすると、a = 2 + 1
となり、2 + 1
の評価結果3
がa
に代入される。
変数
なお、上記の説明で、当然のように「変数」という言葉を用いたが、この「変数」という用語も、数学とは扱いかたが微妙に違うので、気をつける必要がある。 変数名の命名規則については、それぞれのプログラミング言語のマニュアルを参照すること。 また、なお、たいていの言語では、大文字・小文字を区別する(case-sensitive)[11]。 C言語で例を示す:
- 例1
a = 2; a = A + 1;
以上のプログラムはエラーになる。a
とA
は区別され、A
が定義されていないためである。
変数a
を1増やしたいのであれば、例2のようにする。
- 例2
a = 2;
a = a + 1;
実行すれば、aの値は3になる。
静的型付け言語と動的型付け言語 |
プログラミング言語は、静的型付け言語と動的型付け言語に大別されます。両者の間で、代入や変数の意味論が異なるので明確に区別する必要があります。
BASICのような変数名に型情報を持った言語や、関数型言語の一部のように変数そのものを持たない言語もあります。 プログラミング言語の学習において、その言語のパラダイムにおける型システム・変数システム(オブジェクトシステム)の理解は初学時の重要な要素です。 |
実行順序
単文は出現順序が先の方から順に実行されることがほとんどである。文を区切るのは改行、あるいは;
(セミコロン) を使用することが多い。例2で示したプログラムなら、a = 2;
のあとに、a = a + 1;
が実行される。
数値
ソースコードで記述する整数リテラル、及び小数リテラルは、特に指定をしないかぎり、10進数である。整数と、(ほとんどの場合、IEEE 754に準拠した) 有限のケタの小数が、普通のプログラミング言語では使える。分数や平方根( など)などは、関数として用意されている場合でも、評価結果が近似される。また、小数も精度以下の部分は近似して表されます。
もしが に近似されたなら、コンピュータ内部では、ではなく、という値として表現されています。上記のように、本来なら数学的には無限のケタの平方根などの無理数を有限ケタの小数に近似してしまうため、誤差が発生する。このような、無限小数を有限小数に近似したために誤差の発生する現象のことを浮動小数点数による誤差という。
過去の編集で、浮動小数点数による誤差 をケタ落ちとしていましたが、桁落ちとは、丸め誤差を含む非常に近い大きさの小数同士で減算を行ったときに、有効数字が減る現象のことを言い有限の仮数部と指数部で表すことによる誤差とは違う概念です。また一般に浮動小数点数は2進数が採用されているので、十進数では有限桁数で表現できても浮動小数点数では循環小数になることがあります(例えば、 0.110 = 0.000110011001100…2)。
関連項目
脚注
- ^ フラッシュメモリーのMLC/TLCに代表されるw:多値論理など、特殊なアーキテクチャを採用しているコンピューターを除く
- ^ 実は多くのワープロソフトではテキストファイルを保存する方法が用意されており、それで保存すればプログラミングもできないこともありません。ただしワープロソフトはプログラミングを意図した設計になっていないので、ワープロソフトでやるメリットはほとんどありません。
- ^ macOS 10.15 Catalina(2019) からディフォルログインシェルが、それまでの bash から zsh に変わった。
- ^ インタープリタは実行時に「どの部分が多く実行されたか?」「分岐命令でどちらに多く分岐したか?」などの実行時統計情報を容易に手に入れることができ、これらに基づき実行時コンパイラ(JIT)による最適化が可能となりる。よって、一概にインタプリタ方式がコンパイル方式より実行速度が劣るとは言えない。
- ^ UCSD-PascalのP-Codeや、JavaのByte-codeの様な中間コードを生成し、中間コード・インタープリタが実行する言語処理系もあります。
- ^ 異なるハードウェアの実行ファイルは、通常は仮想機械環境を使わない限り実行できません。
- ^ 同じハードウェアでも異なったOSで相互に実行ファイルが実行できない理由は、いくつかありますが、実行ファイルの形式の違いとOSの提供するシステムコールの違いが主なもので、この2つを克服したWine(GNU/LinuxでWindowsの実行ファイルを実行可能にする環境)やWSL(WindowsでGNU/Linuxの実行ファイルを実行可能にする環境)では異なったOSの実行ファイルの実行を実現しています。
- ^ ただし、デバッグや動的リンクの為のシンボルは実行ファイルに含まれている場合もあり、デコンパイルの難易度を下げます。
- ^ 例えば、C++では、キャストと関数呼出しあるいは初期化の構文に衝突があり、型や関数の定義状況との照会を行わないと、構文の特定ができません。
- ^ 数学においては、
=
を等式ではなく代入に用いることもあり、コンテキストによって読み分けられる。 - ^ 比較的古くからある言語では、大文字・小文字を区別しない傾向にあります。具体的には、Fortran・COBOL・Lisp・ALGOLやBASICは大文字・小文字を区別しません。理由は1つではありませんが、有力な理由の1つに小文字がコードがコード化されたASCIIが制定される1963年より前、あるいは同時期に言語設計と言語処理系の実装が行われたから、と考えられます。むしろC言語(1972)が比較的古い言語であるにもかかわらず小文字を区別していることが特異的といえます。