4.2 ユーザー定義関数

(1/1)
ユーザー定義関数
JavaScriptでは、プログラマが関数を定義することができる。前回の定義済み関数と区別する意味で、ユーザー定義関数と呼ぶ。

ここでは2次関数 \( 2x^2 - 4x - 6 \) や、うるう年判定処理を例題に、関数値を一覧表にしたりグラフ描画してみる。同時に、テンプレートリテラルや三項演算子について学ぶ。
自分で関数を定義できるようになると、プログラミングの範囲が飛躍的に広がる。

目次

サンプル・プログラム

2次関数

JavaScriptでは、プログラマが関数を定義することができる。前回の定義済み関数と区別する意味で、ユーザー定義関数と呼ぶ。
ここでは、2次関数 \( 2x^2 - 4x - 6 \) を定義してみることにしよう。
ユーザー定義関数
プログラム "quadraticFunction1.html" は、左図のようにxを入力して計算ボタンをクリックすると、ユーザー定義関数 quadra の計算結果を画面に表示する。

quadraticFunction1.html

  20: <script>
  21: /**
  22:  * 2次関数 2x^2-4x-6
  23:  * @param   Number x  xの値
  24:  * @return  Number f(x)の値
  25: */
  26: function quadra(x) {
  27:     let y = 2 * x * x - 4 * x - 6;
  28: 
  29:     return y;
  30: }
  31: 
  32: // メイン・プログラム ======================================================
  33: function quadraticFunction1() {
  34:     //変数宣言
  35:     let x = document.getElementById('x').value;     //xの値
  36:     x = parseFloat(x);                              //小数化
  37: 
  38:     //2次関数の計算結果
  39:     let y = quadra(x);
  40: 
  41:     //結果を表示する
  42:     document.getElementById('y').innerHTML = y.toString();
  43: }
  44: </script>

ユーザー定義関数は function を使って定義する。
function 関数名(引数1,引数2,...) {
    処理
}
ここでは、2次関数 \( 2x^2 - 4x - 6 \) を計算する式を関数 quadra として定義している。引数xは、関数に与えるxの値である。

関数の呼び出しは定義済み関数と同じで、
y = quadra(x);
のように書く。変数xの値に応じた関数 quadra の計算結果を変数yに格納する。

これまで説明しなかったが、メイン・プログラム自身がユーザー定義関数になっている点にも注目してほしい。
ここでは、ユーザー定義関数 main_quadraticFunction1 がHTML文の <input type="button" id="exec"> にある onClickイベントで呼び出される。

関数の結果を一覧表にする

関数の結果を一覧表にする
次に、2次関数 \( 2x^2 - 4x - 6 \) のxの値を変化させていき、計算結果を一覧表にしてみよう。

あらかじめ、xの最小値を x_min に、xの最大値を x_max に、xの刻み幅を dx に定数として定義しておく。forループ を使ってxの値を変化させながら、ユーザー定義関数 quadra を呼び出すことで一覧を作成する。

quadraticFunction2.html

  33: window.onload = function() {
  34:     //定数宣言
  35:     const minX = -2.0, maxX = 4.0;      //xの最小値,最大値
  36:     const dx    = 0.5;                  //xの刻み幅
  37: 
  38:     //TABLEタグ(テンプレートリテラルを利用)
  39:     let html = `
  40: <tr>
  41: <th>x</th>
  42: <th>f(x)</th>
  43: </tr>
  44: `;
  45: //(テンプレートリテラルを使わない)
  46: //  let html += '<tr><th>x</th><th>f(x)</th></tr>';
  47:     //2次関数を計算
  48:     let y;
  49:     for (let x = minXx <maxXx +dx) {
  50:         y = quadra(x);
  51:         html +`
  52: <tr>
  53: <td>${x}</td>
  54: <td>${y}</td>
  55: </tr>
  56: `;
  57: //(テンプレートリテラルを使わない)
  58: //  let html += '<tr><td>' + x + '</td><td>' + y + '</td></tr>';
  59:     }
  60:     //結果を表示する
  61:     document.getElementById('listFx').innerHTML = html;
  62: }

一覧表はHTMLのTABLEタグを使って実装している。
ここで、見出し行をテキストにして変数 htmlに代入するとするなら、
let html = "<tr><th>x</th><th>f(x)</th></tr>";
と書けばいいのだが、これでは、TABLEの列が幾つあるのか読みにくい。
そこで、テンプレートリテラルを用いて、HTML文を書くようにしてテキストを代入することにした。
let html = `
<tr>
<th>x</th>
<th>f(x)</th>
</tr>
`;
テンプレートリテラルは、JavaScript ES6(ES2015)以降で利用できるようになったもので、バックティックで囲まれた範囲 `...` を、そのままテキストとして扱う。改行やタブなどの制御文字が入っていてもかまわない。

さらに便利なことに、下記のように書くことで、JavaScriptの変数(定数を含む)の値x, yを直接、テキストの中に埋め込むことができる。
html += `
<tr>
<td>${x}</td>
<td>${y}</td>
</tr>
`;


テンプレートリテラルは、IEでは利用できない。
可読性が低下するが、IEでは次のように書かなければならない。
html += '<tr><td>' + x + '</td><td>' + y + '</td></tr>';
"quadraticFunction2.html" は、キーボードからの入力はない。画面がロードされたら、ユーザー定義関数 quadra を呼び出して一覧表示してほしい。
そこで、メイン・プログラムは window.onload イベントハンドラを利用している。このイベントハンドラに代入する関数は、画面がロードされるときに自動実行する。
ここでは、イベントハンドラに代入する関数に名前が無い(無名関数)になっていることに留意してほしい。JavaScriptでは、他所からユーザー定義関数を呼び出すことがなければ、このように無名関数にすることができる。

2次関数のグラフ

2次関数のグラフ
計算結果の一覧表ができたのだから、当然、グラフにプロットすることもできる。
グラフ描画のためにHTMLの canvas を利用する。
canvas は、画面の左上隅が原点 (0, 0) になる。また、グラフのマス目の大きさは指定したピクセル数に応じて可変となる。このため、xと quadra の結果に対していちいち座標変換をする必要があり、その処理の分だけプログラムが長くなる。
canvas の使い方については、「6.9 グラフィックキャンバスとヒアドキュメント」をご覧いただきたい。

quadraticFunction3.html

  33: window.onload = function() {
  34:     //定数宣言
  35:     const minX = -2.0, maxX = 4.0;      //xの最小値,最大値
  36:     const dx    = 0.1;                  //xの刻み幅
  37:     const minY = -8.5, maxY = 10.5;     //yの最小値,最大値
  38: 
  39:     //グラフ描画用canvas
  40:     let canvas = document.getElementById('graph');
  41:     let ctx    = canvas.getContext('2d');
  42:     //表示倍率
  43:     let maxWidth  = canvas.width;
  44:     let maxHeight = canvas.height;
  45:     let scaleX = Math.round(maxWidth  / (Math.abs(minX+ Math.abs(maxX)));
  46:     let scaleY = Math.round(maxHeight / (Math.abs(minY+ Math.abs(maxY)));
  47:     //原点の表示位置
  48:     offsetX = (0 - minX* scaleX;
  49:     offsetY = maxHeight - (0 - minY* scaleY;
  50:     //グラフ領域
  51:     ctx.fillStyle = 'white';
  52:     ctx.fillRect(0, 0, maxWidth, maxHeight);
  53:     //外枠
  54:     ctx.lineWidth   = 1;            //線の太さ
  55:     ctx.strokeStyle = 'gray';       //線の色
  56:     ctx.strokeRect(0, 0, maxWidth, maxHeight);  //外枠
  57:     //X軸・Y軸
  58:     ctx.strokeStyle = 'black';
  59:     ctx.lineCap = 'square';
  60:     ctx.beginPath();
  61:     ctx.moveTo(0, offsetY);
  62:     ctx.lineTo(maxWidth, offsetY);      //X軸
  63:     ctx.moveTo(offsetX, 0);
  64:     ctx.lineTo(offsetX, maxHeight);     //Y軸
  65:     ctx.stroke();
  66: 
  67:     //2次関数のグラフ
  68:     let x  = minX;      //xの初期値
  69:     let x0 = minX * scaleX + offsetX;                   //直前のx座標
  70:     let y0 = (0 - quadra(minX)) * scaleY + offsetY;     //直前のy座標
  71:     ctx.lineWidth   = 2;
  72:     ctx.strokeStyle = 'blue';
  73:     while (x <maxX) {
  74:         let x1 = x * scaleX + offsetX;                  //x座標
  75:         let y1 = (0 - quadra(x)) * scaleY + offsetY;    //y座標
  76:         ctx.beginPath();
  77:         ctx.moveTo(x0, y0);
  78:         ctx.lineTo(x1, y1);
  79:         ctx.stroke();
  80:         x0 = x1;    //直前のx座標入れ換え
  81:         y0 = y1;    //直前のy座標入れ換え
  82:         x +dx;    //次のx
  83:     }
  84: }

うるう年判定

うるう年判定
ユーザー定義関数は、2次関数のような数学的な関数だけでなく、何らかのプロシージャをまとめることで定義できる。

ここでは、「3.1 if~else文」で紹介したうるう年判定プロシージャをユーザー定義関数にしてみよう。

プログラム "isleap.html" は、開始年と期間を入力し計算ボタンをクリックすると、うるう年か平年かを判定し一覧表示する。
うるう年判定を行うユーザー定義関数 isleap は、西暦年yearを引数にして、「3.1 if~else文」で紹介したプロシージャをそのまま関数化している。戻り値は、うるう年の時はtrue、平年の時はfalseだ。

isLeapYear.html

  20: /**
  21:  * 指定した年がうるう年かどうかを求める.
  22:  * @param   Number year 西暦年
  23:  * @return  Boolean true:うるう年/false:平年
  24: */
  25: function isLeapYear(year) {
  26:     let ret;    //戻り値
  27: 
  28:     if (year % 400 === 0) {
  29:         ret = true;
  30:     } else if (year % 100 === 0) {
  31:         ret = false;
  32:     } else if (year % 4 === 0) {
  33:         ret = true;
  34:     } else {
  35:         ret = false;
  36:     }
  37: 
  38:     return ret;
  39: }

isLeapYear.html

  58:     for (let year = startyear < (start + period); year++) {
  59:         let ret = isLeapYear(year? 'うるう年' : '平年';

メイン・プログラムでは、ユーザー定義関数 isLeapYear の戻り値を受けて、trueの時は変数retに 'うるう年'を、そうでないときは '平年'を代入する。この代入処理に、三項演算子を用いている。
三項演算子は
(条件) ? 値1 : 値2
のようにして用い、条件がtrueの時に値1を、そうでないときに値2を返す。
if文のようにブロック文として複雑な処理を行う必要がないときに重宝する演算子だ。

returnのないユーザー定義関数の戻り値

JavaScript では、ユーザー関数で return を書かなくてもエラーにならない。

noReturn.html

  21: /**
  22:  * returnのないユーザー関数
  23:  * @param   なし
  24:  * @return  なし
  25: */
  26: function noReturn() {
  27: }

returnのないユーザー関数 noReturn の戻り値は undefined になる。

無名関数とアロー関数

円の面積を求める関数をユーザー定義してみる。
半径 \( r \) から円の面積を求める公式は \( \pi \times r^2 \) だから――

calcAreaOfCircle.html

  21: /**
  22:  * 円の面積を求める
  23:  * @param   Number r 半径
  24:  * @return  Number 円の面積
  25: */
  26: function areaOfCircle1(r) {
  27:     let y = Math.PI * Math.pow(r, 2);   // Math.PI * r * r でも同じ
  28:     return y;
  29: }

このような関数になる。べき乗関数 Math.pow() を使っても、\( r \) を2回掛けても、結果は変わらない。
この関数を前述の無名関数を使って表すと――

calcAreaOfCircle.html

  31: // 円の面積を求める(無名関数版)
  32: const areaOfCircle2 = function (r) {
  33:     let y = Math.PI * Math.pow(r, 2);
  34:     return y;
  35: }

このようになる。areaOfCircle2 には、右項にある無名関数の実体が代入されると考えてほしい。なので、areaOfCircle2 は変数ではなく定数として扱う。

calcAreaOfCircle.html

  37: // 円の面積を求める(アロー関数版)
  38: const areaOfCircle3 = (r) => Math.PI * Math.pow(r, 2);
  39: 

JavaScriptには アロー関数というユーザー定義関数を簡潔に記述するための構文が用意されている。これを使うと、上のような表記になる。無名関数と同じで、areaOfCircle3 には、関数式の実体が代入される。
アロー関数は、次のように記述する。
const 関数名 = (引数1, 引数2, ...) => 関数式;
アロー関数は、if文やfor文のような制御文を伴わない単一の式として表せるような関数を定義するときに用いられることがある。
念のため、3種類のユーザー関数を実行してみよう。もちろん同じ結果になる。

読みやすいプログラムとは

ここからは、読みやすいプログラムの書き方を紹介していくことにする。

第8章 読みやすいプログラム」であらためて整理するが、プログラムは論文と同じ論理的な読み物である。論文と同じで、査読(レビュー)されたり、ライブラリ参照されたり‥‥最初に書いたときより、むしろ稼働後に読まれることの方が多い
プログラミングを仕事とするときは、他人が読みやすいプログラムを書くことが求められる。プロジェクトや会社によってはコーディング規約を用意しているところもある。

万国共通のコーディング規約というものは無い。その組織が用意したガイドラインと考えてほしい。そのガイドラインを守らなくてもプログラムは動くのだが、チーム開発の効率アップのために用意される。
また、趣味で/個人でプログラミングを楽しむ方も、将来仕事をプログラミングに携わるかもしれないし、それでなくても、自分のプログラムを1週間後に見直したときに細かいロジックが分からなくなっているかもしれない。
というわけで、仕事にしても趣味にしても、読みやすいプログラムを書くことのメリットは大きい。

ここでは、よくあるコーディング規約から汎用性があり、かつJavaScript向きのものを取り出して、この連載独自のコーディング規約として紹介していく。

読みやすいプログラム:関数名

まず、関数の名前の決め方を取り上げてみよう。名前の決め方を命名規則と呼ぶ。

●関数の命名規則
  1. 関数の機能を動詞で表現する。
  2. 並べる順序は動詞+目的語(形容詞、名詞など)
  3. 発音しやすい英単語を並べる。
  4. 単語はローワーキャメルケースで並べる。
  5. 単語は最大3つ程度まで。
  6. 真偽値を戻す関数はis, can, hasではじめる。
ここでは、うるう年を判定する関数の名前を isLeapYear とした。英語でうるう年は leap year と綴る。判定結果が真偽値で返るので動詞 is ではじめて、leap year が目的語に相当する。
これを並べると isLeapYear となる。このとき、最初の単語の頭文字は小文字で、途中の単語の頭文字を大文字にする記述法をローワーキャメルケースと呼ぶ。ちなみに、最初の単語の頭文字も大文字で IsLeapYear とするのはアッパーキャメルケースと呼ぶ

英単語で命名することに抵抗があるかもしれないが、名前として日本語を使えるプログラミング言語は少ない(皆無ではないが)。もしローマ字表記を用いるとするならば、"uruudoshikadouka" となり、さすがに読みにくい。
英語で書くことのメリットは、目的語より動詞が先にくることだ。関数の機能を動詞で表現すると、関数名からすぐに機能概要を読み取ることができる。
また、発音しやすいという規則も大切だ。チーム開発しているときに、ディスカッションは音声によることが多い。このとき、名前の発音がしにくいと効率が落ちる。日本語で会話している中で英単語の名前が挙がると、関数名や変数名であることがすぐ分かる。
英語が苦手な方は、Google翻訳やDeepL翻訳を使ってみてほしい。たとえば、DeepL翻訳に「今年はうるう年ですか?」と入力すると、"Is this a leap year ?" という翻訳結果が返ってくる。ここから、isLeapYear を命名することができる。

英単語を並べる場合、単に leapYear だけでは関数の機能が分からない。
getLeapYear は、うるう年を取得するという意味になってしまい、判定機能ではなくなる。
真偽値を返す関数の場合、肯定の返す命名をする。メインプログラムの目的によっては、うるう年でない年を判定してtrueを返す関数が必要となることがあるかもしれないが、isnotLeapYear と命名して true が返る場合を考えると混乱する。素直に isLeapYear にして、メインプログラム側で false を判定するようにした方がいい。

なお、命名規則をアッパーキャメルケースに統一し、IsLeapYear と命名することも間違いではない。ただ、いずれ紹介することになるが、クラス名にアッパーキャメルケースを使用するので、ここでは関数名はローワーキャメルケースで書くものと覚えておいてほしい。
また、単語の区切りにアンダーバー _ を使い、is_leap_year のように命名する方式をスネークケースと呼ぶ。ここでは使わないが、Python ではスネークケースが好まれる。

読みやすいプログラム:関数の参照透過性

数学で言うところの関数とは、ある引数を渡すと、戻り値が一意に決まるものである。これを、プログラミングでは関数の参照透過性と呼ぶ。
一方で、同じ引数を渡しても、何らかの要因によって戻り値が変化するものは関数とは呼ばない。

ここで問題になるのが、関数の中で外部変数やAPI、インターフェースなどを参照し、その結果によって戻り値が異なる場合である。引数に連動して外部変数やAPI、インターフェースからの応答が異なるような仕組みであれば参照透過性は担保できるが、そうでないものは参照透過性がないということになる。参照透過性がないと、プログラムの流れを追うのが難しくなる。

isleapNow.html

  43: /**
  44:  * 今年はうるう年かどうかを判定する.
  45:  * 参照透過性がない関数.
  46:  * @param   なし
  47:  * @return  Boolean true:うるう年/false:平年
  48: */
  49: function isLeapYearNow() {
  50:     let ret;    //戻り値
  51: 
  52:     if (Year % 400 === 0) {
  53:         ret = true;
  54:     } else if (Year % 100 === 0) {
  55:         ret = false;
  56:     } else if (Year % 4 === 0) {
  57:         ret = true;
  58:     } else {
  59:         ret = false;
  60:     }
  61: 
  62:     return ret;
  63: }

isleapNow.html

  65: // メイン・プログラム ======================================================
  66: Now = new Date();
  67: let Year = Now.getFullYear();
  68: let ret = isLeapYearNow() ? 'うるう年' : '平年';
  69: document.write('今年は' + ret + 'である.');
  70: </script>

たとえば、このプログラムのユーザー関数 isLeapYearNow は参照透過性がない。引数がないのに、戻り値が一定していないためである。これは、関数外で定義した外部変数 Year を参照しているためだ。
関数 isLeapYearNow の動きを読み取るには、別の所にある変数 Year の挙動を追わなければならず、プログラムの可読性が低下してしまう。
そこで、変数 Year を、以前つくったユーザー関数 isLeapYear に渡してやれば参照透過性を担保できる。
APIやインターフェースから応答してきたデータも、いったん変数に代入しユーザー関数に渡してやることで、参照透過性を担保できる。

コラム:イベント駆動型プログラミング

同期と非同期
Webアプリケーションでは、ボタンをクリックしたり、画面をロードするというイベントに応じてプロシージャを実行することが多い。このようなプログラムを、イベント駆動型(イベントドリブン)と呼ぶ。JavaScriptとHTMLの組み合わせで、イベント駆動型プログラミングが容易にできるようになっている。
イベント駆動型プログラミングでは、そこで呼び出される処理(ハンドラ)が同期非同期かを意識する必要がある。
同期は、ハンドラの処理が終了するまでメイン・プログラムに戻らない。
非同期は、ハンドラの処理が終了するかどうかに関わらず、メイン・プログラムは並行して実行していく。

同期の問題点は、ハンドラの処理が遅くなると、その分、メイン・プログラムの実行再開が遅れ、画面がフリーズしたようになることだ。
では、非同期にすればいいかというと、今度は、メイン・プログラムの中でハンドラの処理結果を必要としているのに、それに間に合わず、メイン・プログラムが異常な動きをし始めることがある。

また、ハンドラに参照透過性がないと、メインプログラムや他のスレッドの影響を受けて動作が変わる場合がある。あらかじめ、そのことを設定に盛り込み、漏れなくテスト仕様を用意できているならともかく、設計もテストケースも複雑になるので、できる限り避けるべきである。

ハンドラが同期非同期かはプログラミング言語によって異なる。JavaScriptは原則として非同期であるため、メイン・プログラム側でハンドラの処理結果を待つ必要がある。
これまで例題で取り上げてきたプログラムはハンドラ(ユーザー定義関数)の処理が単純なので、意識して非同期処理は行っていないが、これから仕事としてプログラム開発する方は、常に同期非同期を意識するようにしてほしい。

コラム:様々なプログラミング言語でうるう年判定を書いてみた

まずは、1954年(昭和29年)にIBM 704にリリースされた世界最初の高水準プログラミング言語「FORTRAN」で書いてみよう。初期の FORTRAN は大文字と小文字の区別がなく、行頭から5文字目まではコメントや行番号用に空けておく。
!     うるう年の判定:FORTRAN
      SUBROUTINE IS_LEAP_YEAR(YEAR, RESULT)
      INTEGER YEAR, RESULT
 
      IF (MOD(YEAR, 4) .EQ. 0) THEN
         IF (MOD(YEAR, 100) .NE. 0 .OR. MOD(YEAR, 400) .EQ. 0) THEN
            RESULT = 1
         ELSE
            RESULT = 0
         ENDIF
      ELSE
         RESULT = 0
      ENDIF
 
      RETURN
      END
 
      PROGRAM MAIN
      INTEGER YEAR, RESULT
      YEAR = 2023
      CALL IS_LEAP_YEAR(YEAR, RESULT)
      IF (RESULT .EQ. 1) THEN
         WRITE(*,*) YEAR, '年はうるう年である.'
      ELSE
         WRITE(*,*) YEAR, '年はうるう年ではない.'
      ENDIF
      STOP
      END
つづいて、1959年(昭和34年)に文系事務員でもプログラミングできる言語としてアメリカ政府主導で開発されたプログラミング言語「COBOL」で書いてみた。
*> うるう年の判定:COBOL
IDENTIFICATION DIVISION.
PROGRAM-ID. LeapYearProgram.
 
DATA DIVISION.
WORKING-STORAGE SECTION.
01 YEAR   PIC 9(4).
01 RESULT PIC 9(4) VALUE 0.
01 DIV PIC 9(4) VALUE 0.
01 MOD PIC 9(3) VALUE 0.
 
PROCEDURE DIVISION.
    MOVE 2024 TO YEAR
 
    PERFORM IS-LEAP-YEAR.
 
    IF RESULT = 1
        DISPLAY YEAR "年はうるう年である."
    ELSE
        DISPLAY YEAR "年はうるう年でない."
    END-IF.
 
    STOP RUN.
 
IS-LEAP-YEAR.
    MOVE 0 TO RESULT
    DIVIDE YEAR BY 4 GIVING DIV REMAINDER MOD.
    IF MOD = 0
        ADD 1 TO RESULT
        DIVIDE YEAR BY 100 GIVING DIV REMAINDER MOD
        IF MOD = 0
            MOVE 0 TO RESULT
            DIVIDE YEAR BY 400 GIVING DIV REMAINDER MOD
            IF MOD = 0
                ADD 1 TO RESULT
            END-IF
        END-IF
    END-IF.
1970年(昭和45年)に、プログラミング教育を意識した言語として誕生した「Pascal」で書いてみた。FORTRANCOBOL とは違い、ブロックの概念が導入された。
{ うるう年の判定:Pascal }
program LeapYearProgram;
uses SysUtils;
 
function isLeapYear(year: integer): boolean;
begin
    if year mod 400 = 0      then isLeapYear := true
    else if year mod 100 = 0 then isLeapYear := false
    else if year mod 4 = 0   then isLeapYear := true;
end;
 
var
    year: integer;
begin
    year := 2024;
    if isLeapYear(year) then writeln(year, '年は うるう年である.')
    else                     writeln(year, '年は うるう年でない.');
end.
Pascal と同じ頃に登場した Forth (フォース) はスタック志向という点で他のプログラミング言語と一線を画している。計算式も逆ポーランド記法になる。記述がアセンブリに近いため、コンパイラ版は十分高速で、自分自身を記述できるチューリング完全性を備えていた。しかし、公式文法がなく、コード量も多くなるため、普及はしなかった。
下記プログラムではif文のスタックを明確にするためにインデントを設けているが、if文はthenで閉じる決まりなので、Python のようにインデントが必須というわけではない。変数を宣言することはできるが、計算する前にスタックにプッシュしなければならない。
\ うるう年判定:Forth
VARIABLE year       \ yearという変数を宣言
 
: is-leap-year ( year -- flag )
    dup 400 mod 0= if
        drop true
    else
        dup 100 mod 0= if
            drop false
        else
            dup 4 mod 0= if
                drop true
            else
                drop false
            then
        then
    then ;
 
: main
    2023 year !         \ 2023をyear変数に代入
    year @              \ 変数yearの値をスタックにpush
    is-leap-year if
        year @ . ." 年は うるう年である。" cr
    else
        year @ . ." 年は うるう年ではない。" cr
    then ;
 
main
Pascal より1年早く誕生したのが C言語である。データ型が明確という違いがあるものの、JavaScriptに似ている。
/* うるう年判定:C */
#include <stdbool.h>
#include <stdio.h>
 
bool isLeapYear(int year) {
    bool result = false;
    if (year % 400 == 0) {
        result = true;
    } else if (year % 100 == 0) {
        result = false;
    } else if (year % 4 == 0) {
        result = true;
    }
    return result;
}
 
int main(void) {
    int year = 2023;
    if (isLeapYear(year)) {
        printf("%d年は うるう年である.", year);
    } else {
        printf("%d年は うるう年ではない.", year);
    }
}
1983年(昭和58年)に C言語の改良版として登場した C++言語で書いてみる。三項演算子や文字列が扱えるようになり、JavaScriptに近い書き方ができる。
//うるう年判定:C++
#include <iostream>
 
bool isLeapYear(int year) {
    bool result = false;
    if (year % 400 == 0) {
        result = true;
    } else if (year % 100 == 0) {
        result = false;
    } else if (year % 4 == 0) {
        result = true;
    }
    return result;
}
 
int main(void) {
    int year = 2023;
    std::cout << year << "年は うるう年" << (isLeapYear(year) ? "である." : "ではない.") << std::endl;
}
C言語C++言語の後継を目指し2010年(平成22年)7月に登場した Rust で書いてみる。実行速度は C言語 と同程度と高速で、C言語 になかったメモリ安全性が保証されている。基本構文は C言語に似ているが、命令式がベースになっているため、関数の戻り値の記述が独特なものになっている。
//うるう年判定:Rust
fn isLeapYear(year: i32) -> bool {
    if year % 400 == 0 {
        true
    } else if year % 100 == 0 {
        false
    } else if year % 4 == 0 {
        true
    } else {
        false
    }
}
 
fn main() {
    let year = 2023;
    if isLeapYear(year) {
        println!("{}年は うるう年である.", year);
    } else {
        println!("{}年は うるう年ではない.", year);
    }
}
構造化プログラミングを書くことができるBASICとしてマイクロソフトが1991年(平成3年)にリリースした Visual Basic で書いてみる。関数を表す Function と、サブルーチンを表す Sub から成り、プログラム全体を Module であらわす。
Module CheckLeapYear
    Function IsLeapYear(ByVal year As Integer) As Boolean
        If year Mod 400 = 0 Then
            Return True
        ElseIf year Mod 100 = 0 Then
            Return False
        ElseIf year Mod 4 = 0 Then
            Return True
        Else
            Return False
        End If
    End Function
 
    Sub Main()
        Dim year As Integer = 2023
        If IsLeapYear(year) Then
            Console.WriteLine(year & "年は うるう年である。")
        Else
            Console.WriteLine(year & "年は うるう年ではない。")
        End If
    End Sub
End Module
統計解析向けのプログラミング言語しとて1993年(平成5年)8月に登場した R言語で書くとこうなる。後述する LISPの影響を受けており、代入を <- と書くところにこだわりが見られる。
# うるう年判定:R
isLeapYear <- function(year) {
    result <- FALSE
    if (year %% 400 == 0) {
      result <- TRUE
    } else if (year %% 100 == 0) {
      result <- FALSE
    } else if (year %% 4 == 0) {
      result <- TRUE
    }
    return(result)
}
 
# メイン処理
year <- 2023
if (isLeapYear(year)) {
    cat(year, "年はうるう年である。\n")
} else {
    cat(year, "年はうるう年ではない。\n")
}
CGIとして古くからWebアプリケーションに使われてきた Perlで書くとこうなる。
#うるう年判定:Perl
sub isLeapYear {
    my ($year) = @_;
    if ($year % 400 == 0) {
        $result = 1;
    } elsif ($year % 100 == 0) {
        $result = 0;
    } elsif ($year % 4 == 0) {
        $result = 1;
    } else {
        $result = 0;
    }
    return $result;
}
 
my $year = 2023;
if (isLeapYear($year) == 1) {
    print "$year 年はうるう年である.";
} else {
    print "$year 年はうるう年ではない.";
}
RubyPerlに似ている。
#うるう年判定:Ruby
def isLeapYear?(year)
    if (year % 400 === 0)
            result = true
        elsif (year % 100 === 0)
            result = false;
        elsif (year % 4 === 0)
            result = true
        else
            result = false
        end
    return result
end
 
year = 2023
result = isLeapYear?(year) ? 'である.' : 'ではない.'
puts "#{year}年は うるう年#{result}"
サーバサイド・プログラムとして人気のある PHPで書くとこうなる。変数名の頭に $ が付くのが特徴的だ。動的型付けなので、データ型まで含めて厳密に等価であることを計算するのに厳密等価演算子 === が用意されている。
<?php
//うるう年判定:PHP
function isLeapYear($year) {
    if ($year % 400 === 0)          $result = true;
    else if ($year % 100 === 0)     $result = false;
    else if ($year % 4 === 0)       $result = true;
    else                            $result = false;
    return $result;
}
 
$year = 2023;
$result = isLeapYear($year) ? 'である.' : 'ではない.';
print $year . '年は うるう年' . $result;
?>
サーバサイド・プログラムとして歴史の長い Javaで書くとこうなる。JavaScript と似た名前だが、別物であることが分かる。
//うるう年判定:Java
import java.util.*;
 
public class Main {
    //うるう年判定:Java
    public static boolean isLeapYear(int year) {
        boolean result = false;
        if (year % 400 == 0)        result = true;
        else if (year % 100 == 0)   result = false;
        else if (year % 4 == 0)     result = true;
        return result;
    }
 
    public static void main(String[] args) throws Exception {
        int year = 2023;
        String result = Main.isLeapYear(year) ? "である." : "ではない.";
        System.out.println(year + "年は うるう年" + result);
    }
}
JavaScriptをサーバサイドでも利用できるようにした TypeScriptで書くとこうなる。JavaScript と比べると、データ型が明示されている。
//うるう年の判定:TypeScript
function isLeapYear(year: number):boolean {
    let ret: boolean;   //戻り値
 
    if (year % 400 === 0) {
        ret = true;
    } else if (year % 100 === 0) {
        ret = false;
    } else if (year % 4 === 0) {
        ret = true;
    } else {
        ret = false;
    }
 
    return ret;
}
 
const year: number = 2100;
const isLeap: boolean = isLeapYear(year);
 
if (isLeap) {
    console.log(`${year}年はうるう年である.`);
} else {
    console.log(`${year}年はうるう年ではない.`);
}
2009年(平成21年)にGoogleが開発したプログラミング言語 Goで書くとこうなる。C++Java に似ているが、より簡明な書き方ができる。
//うるう年判定:Go
package main
import ("fmt")
 
func isLeapYear(year int) bool {
    var result bool
    if (year % 400 == 0) {
        result = true
    } else if (year % 100 == 0) {
        result = false
    } else if (year % 4 == 0) {
        result = true
    } else {
        result = false
    }
	return result
}
 
func main() {
    var year int
    year = 2023
    if isLeapYear(year) {
        fmt.Printf("%d年はうるう年です。\n", year)
    } else {
        fmt.Printf("%d年はうるう年ではありません。\n", year)
    }
}
AIや技術系の計算で圧倒的な存在感を示す Pythonで書くとこうなる。
#うるう年判定:Python
def isLeapYear(year):
    if year % 400 == 0:
        result = True
    elif year % 100 == 0:
        result = False
    elif year % 4 == 0:
        result = True
    else:
        result = False
    return result
 
year = 2023
if isLeapYear(year):
    result = "である."
else:
    result = "ではない."
print(year, "年は うるう年", result)
C言語に Smalltalk型のオブジェクトし高機能を持たせたプログラミング言語 Objecctive-C は1984年(昭和59年)に発表され、NeXTやmacOSの開発言語となった。
//うるう年判定:Objective-C
#import <Foundation/Foundation.h>
 
BOOL isLeapYear(NSInteger year) {
    BOOL ret = NO;
    if (year % 400 == 0) {
        ret = YES;
    } else if (year % 100 == 0) {
        ret = NO;
    } else if (year % 4 == 0) {
        ret = YES;
    } else {
        ret = NO;
    }
    return ret;
}
 
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSInteger year = 2024;
        if (isLeapYear(year)) {
            NSLog(@"%ld年はうるう年である.", (long)year);
        } else {
            NSLog(@"%ld年はうるう年でない.", (long)year);
        }
    }
    return 0;
}
Objective-C の後継として、2014年(平成26年)にAppleがiOSやMac向けのネイティブアプリケーションを開発するために発表したオープンソースのプログラミング言語 Swiftで書くとこうなる。書き方がだいぶシンプルになった。
//うるう年判定:Swift
func isLeapYear(_ year: Int) -> Bool {
    var ret: Bool
    if year % 400 == 0 {
        ret = true
    } else if year % 100 == 0 {
        ret = false
    } else if year % 4 == 0 {
        ret = true
    } else {
        ret = false
    }
    return ret
}
 
let year = 2023
if isLeapYear(year) {
    print("\(year)年はうるう年でらる.")
} else {
    print("\(year)年はうるう年ではない.")
}
関数型プログラミング言語の祖先 LISPで書くとこうなる。
;うるう年判定:Lisp
(defun isLeapYear (year)
    (if (zerop (mod year 400)) t
    (if (zerop (mod year 100)) nil
    (if (zerop (mod year 4))   t
    nil)))
)
(setq year 2023)
(if (isLeapYear year)
    (format t "~A 年はうるう年である.~%" year)
    (format t "~A 年はうるう年ではない.~%" year))
同じく関数型プログラミング言語 Haskellで書くとこうなる。
--うるう年判定:Haskell
isLeapYear :: Int -> Bool
isLeapYear year
    | (year `mod` 4 == 0) && (year `mod` 100 /= 0) = True
    | year `mod` 400 == 0 = True
    | otherwise = False
 
main = do
    let year = 2023 ::Int
    if isLeapYear year
        then putStrLn $ show year ++ "年はうるう年です。"
        else putStrLn $ show year ++ "年はうるう年ではありません。"
人工知能言語として一世を風靡した論理型プログラミング言語の Prologでは条件分岐の概念がなくなり、こんなプログラムになる。
%うるう年判定:Prolog
isLeapYear(Year) :- 0 is Year mod 400, !.
isLeapYear(Year) :- 0 is Year mod 100, !, fail.
isLeapYear(Year) :- 0 is Year mod 4, !.
main(Year) :- (isLeapYear(Year) ->
    format('~w 年はうるう年です。', [Year]);
    format('~w 年はうるう年ではありません。', [Year])
    ).
:- main(2023).
SQL はデータベースを処理するためのスクリプトだが、PROCEDUREを備えているRDBMSでは、プログラミング言語のようなことができる。下記は MySQL で実行できる。
--うるう年判定:MySQL
DELIMITER //
 
CREATE PROCEDURE isLeapYear(IN year INT, OUT message VARCHAR(50))
BEGIN
    DECLARE result BOOLEAN DEFAULT FALSE;
 
    IF (year % 400 = 0) THEN
        SET result = TRUE;
    ELSEIF (year % 100 = 0) THEN
        SET result = FALSE;
    ELSEIF (year % 4 = 0) THEN
        SET result = TRUE;
    END IF;
 
    IF result THEN
        SET message = CONCAT(year, '年はうるう年です');
    ELSE
        SET message = CONCAT(year, '年はうるう年ではありません');
    END IF;
END //
 
DELIMITER ;
 
SET @year = 2024;
SET @message = '';
 
CALL IsLeapYear(@year, @message);
 
SELECT @message;
(この項おわり)
header