Edited at

九九表で学ぶプログラミング

More than 1 year has passed since last update.


はじめに

プログラミング初心者が環境構築して「Hello, World!」などのサンプルコードを動かした後、次に何をやればいいかがわからないといったことをよく聞く。

それ以前に、どの言語を勉強した方がいいか?といった疑問もよく聞く。

そんなときに個人的にオススメしている九九表について紹介する。


最近は技術記事が豊富だし、ライブラリの充実した言語も多いため、とりあえず数行のコードをコピペして実行するところまでやってみて「よくわからないけどなんか動いた」で満足してしまって終わってしまう人も多いと思っている。

また、逆に分厚い参考書を買ってきて「if文とは~」「for文とは~」「ポインタが~」「オブジェクト指向~」のように基礎から順序立てて学習していくタイプの人もいるが、これはこれで構文や概念を理解することが目的になるのでなかなかプログラミングの楽しい部分に辿り着けず、つらみがあると思う。

そこで、個人的に初心者にオススメな学習法だと思っているのがアルゴリズムの実装を目的に学習することである。

アルゴリズムは基本的に言語に依存せず、必ずコードの細かい理解が必要になる。

また、色々なアルゴリズムに触れることでプログラミングに最も重要(だと思っている)アイデア力を養うこともできる。

簡単なアルゴリズムをいくつか理解できれば、プログラミングに必要な知識や考え方は十分に身に付けられるのではないかと思う。

ちなみに筆者はプログラミングを完全に理解しているレベルなので、プログラミングチョットデキルな人から見るとツッコミどころあるかもしれませんがご容赦を。

※最終的なコード

See the Pen bmKQYm by sensq (@sensq) on CodePen.


九九表のススメ

アルゴリズムを勉強すると良いとは言っても、世の中には数えきれないほどのアルゴリズムが存在している。

よく初心者向けで出てくるのが「探索」「ソート」「素数判定」などだが、経験上、いずれも初心者(≒一般人)には馴染みが無く、いまいち面白味に欠ける※1

より簡単で有名なところだとFizzBuzz問題があるが、これも一般的な日本人には馴染みが無く、剰余計算にも馴染みが無いので、やはり面白味に欠けるような気がしている。

(FizzBuzzが英語圏で本当に一般的に流行っていることなのか知らないが)

※1: 問題の意味や目的が理解しにくく、できたところで「ふーん」で終わってしまうような感じ。

そこで、初心者には以下の理由で九九表を作ることをオススメしたい。


  • 小学生以上なら誰でも知っていて馴染み深い(正解の出力がイメージしやすい)

  • 基礎的な構文だけで書ける

  • 九九の数字を可変にしたり、見栄えを綺麗にするなど、考えやすい拡張性がある

問題として九九表が載っている参考書などもあるが、大半が最低限の実装だけで終わってしまっているのが勿体無いと思う。


環境準備

プログラムを学ぶために必要なハードルの高い作業に環境構築があり、ここで挫折してしまう人も少なくない。

しかし、最近はオンラインでコードを書いて実行できるサービスが豊富になってきているため、プログラミングを学びたいだけであればこういったサービスを利用するのが最も手っ取り早い。

(WindowsならPowerShellも手軽に使えてオススメ)

例えばCodePenだと、以下の手順でJavascriptをCodePenのページ内で実行することができる。



  1. https://codepen.io/にアクセス

  2. 右上のCreate押下→New Pen押下

アカウントを作っておくとコードを保存して共有したりすることもできる。(今年からQiitaに埋め込めるようにもなった)

この記事では以降、以下のテンプレートのJS部分のqq_table関数だけを記載していくので、実際に自分でも試してみて下さい。

See the Pen template by sensq (@sensq) on CodePen.

※本当に動作に最小限必要な部分しか書いていません。特にHTML


九九表コード


レベル1(基礎)

まずは以下の出力を得ることを目的にしてみる。

  1  2  3  4  5  6  7  8  9

2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81

躓きポイントは3つ


  • 二重ループ

  • 改行

  • 文字フォーマット(パディング)

ループの処理される順番が理解できていれば全然難しくないが、このレベルが自力でできるだけでも個人的には「プログラミング初心者卒業」と言ってもいいと思っている。

また、文字のフォーマットは言語によって書き方が異なるため、コード暗記ではなく考え方を理解して言語に合わせて調べる必要がある。

【解答サンプル】※ここをClick

function qq_table(){

let res = ""
for(let row=1; row<=9; row++){
for(let col=1; col<=9; col++){
// 計算結果の出力(3桁になるようにスペースで右詰め)
res += (row * col).toString().padStart(3, " ")
}
// 内部のループが終わったら改行
res += "\n"
}
return res
}



レベル2(見出し)

次は以下の出力を得ることを目的にしてみる。

  |  1  2  3  4  5  6  7  8  9

------------------------------
1| 1 2 3 4 5 6 7 8 9
2| 2 4 6 8 10 12 14 16 18
3| 3 6 9 12 15 18 21 24 27
4| 4 8 12 16 20 24 28 32 36
5| 5 10 15 20 25 30 35 40 45
6| 6 12 18 24 30 36 42 48 54
7| 7 14 21 28 35 42 49 56 63
8| 8 16 24 32 40 48 56 64 72
9| 9 18 27 36 45 54 63 72 81

上と左に見出しを付けただけだが、色々なコードの書き方が考えられるため、意外と発想力が必要になって難しい。

正解は無いので、色々なパターンで書いてみるのもいいと思う。

参考までに、if文使わない場合と使う場合で2パターン書いてみる。

【解答サンプル】※ここをClick


if文無し

function qq_table(){

let res = ""

// 上の見出しを生成(3桁になるようにスペースで右詰め)
res += " |"
for(let row=1; row<=9; row++){
res += row.toString().padStart(3, " ")
}
res += "\n"

// 上の見出しの区切り線(手動で良い感じに個数を調整)
res += "------------------------------"
res += "\n"

for(let row=1; row<=9; row++){
res += row.toString().padStart(2, " ") // 左列の文字数
res += "|"
for(let col=1; col<=9; col++){
// 計算結果の出力(3桁になるようにスペースで右詰め)
res += (row * col).toString().padStart(3, " ")
}
res += "\n"
}
return res
}



if文有り

function qq_table(){

let res = ""

// 上の見出し作成のために0から始める
for(let row=0; row<=9; row++){
// 0の場合だけスペースを表示
if(row === 0){
res += " "
}
else{
res += row.toString().padStart(2, " ")
}
res += "|" // 左の見出しの区切り線
for(let col=1; col<=9; col++){
// 0行目のときだけ列番号をそのまま出力
if(row === 0){
res += col.toString().padStart(3, " ")
}
else{
// 計算結果の出力(3桁になるようにスペースで右詰め)
res += (row * col).toString().padStart(3, " ")
}
}
res += "\n"
// 0行目のときだけ区切り線も出力
if(row === 0){
res += "------------------------------"
res += "\n"
}
}
return res
}




if文無しの方がスマートに見えるが、上の見出し生成部分だけ表生成から独立しているのが若干違和感ある。

if文有りの方はちょっと理解がしにくいが、for文の中だけで表生成がすべてまとめられている。

個人的にはif文無しの方が理解も変更もテストもしやすくて良いと思うが、if文有りの方が高い理解度と実装力が求められるので、どちらも理解できると良いと思う。


レベル3(九九表→NN表)

次は以下の出力を得ることを目的にしてみる。

今までは9x9までで固定にしていたが、これを汎用的にNxN(Nは任意の整数)まで表示できるように拡張する。

  |   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15

---------------------------------------------------------------
1| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
2| 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
3| 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45
4| 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60
5| 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75
6| 6 12 18 24 30 36 42 48 54 60 66 72 78 84 90
7| 7 14 21 28 35 42 49 56 63 70 77 84 91 98 105
8| 8 16 24 32 40 48 56 64 72 80 88 96 104 112 120
9| 9 18 27 36 45 54 63 72 81 90 99 108 117 126 135
10| 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150
11| 11 22 33 44 55 66 77 88 99 110 121 132 143 154 165
12| 12 24 36 48 60 72 84 96 108 120 132 144 156 168 180
13| 13 26 39 52 65 78 91 104 117 130 143 156 169 182 195
14| 14 28 42 56 70 84 98 112 126 140 154 168 182 196 210
15| 15 30 45 60 75 90 105 120 135 150 165 180 195 210 225

これは入力と出力をどこまで想定して作り込めるか?が重要になってくる。

何を改良する必要があるのかは実行してみないとわからないことも多いので、

「デバッグ→問題点のリストアップ→解決方法の検討→テスト」といったサイクルを回す回数が必然的に多くなってくる。

まずは当然9で固定になっている部分を変数化する必要があるので、関数に引数を設定できるようにし、9を文字に置き換えていく。

【解答サンプル】※ここをClick


9部分を変数化

function qq_table(max){

let res = ""

// 上の見出しを生成(3桁になるようにスペースで右詰め)
res += " |"
for(let row=1; row<=max; row++){
res += row.toString().padStart(3, " ")
}
res += "\n"

// 上の見出しの区切り線(手動で良い感じに個数を調整)
res += "------------------------------"
res += "\n"

for(let row=1; row<=max; row++){
res += row.toString().padStart(2, " ") // 左列の文字数
res += "|"
for(let col=1; col<=max; col++){
// 計算結果の出力(3桁になるようにスペースで右詰め)
res += (row * col).toString().padStart(3, " ")
}
res += "\n"
}
return res
}

// ※以下の関数実行部分に任意の整数を指定すること
result = qq_table(15)




この状態で適当に引数の値を変えてみると、以下の2つが気になる。


  • 破線---の個数を引数に合わせて可変にしたい

  • 3桁以上の数字が隣と繋がっている(フォーマットのスペース数を引数に合わせて可変にしたい)

※破線の個数は言語によっては難しい場合もあるが、最近のJavascriptの場合はrepeatという関数を使うと簡単に実装できる。

どちらも引数の値から自動的に良い感じの値を算出して、その値を使うようにすることで実装できる。

【解答サンプル】※ここをClick


破線の個数とフォーマットの桁数を自動計算

function qq_table(max){

let res = ""
// 最大値(=maxを二乗した値)の桁数+1を桁数とする
let format_num = ( (max * max).toString().length + 1 )
// 破線の個数はmaxが1増える毎にformat_num分増える
let horizontal_line_num = (max * format_num)

// 上の見出しを生成
res += " |"
for(let row=1; row<=max; row++){
res += row.toString().padStart(format_num, " ")
}
res += "\n"

// 上の見出しの区切り線
res += "---" // 左見出しの桁数の分
res += "-".repeat(horizontal_line_num)
res += "\n"

for(let row=1; row<=max; row++){
res += row.toString().padStart(2, " ") // 左列の文字数
res += "|"
for(let col=1; col<=max; col++){
// 計算結果の出力
res += (row * col).toString().padStart(format_num, " ")
}
res += "\n"
}
return res
}

// ※以下の関数実行部分に任意の整数を指定すること
result = qq_table(15)




なお、どちらも元々固定値を埋め込んでいた部分であり、慣れてくると最初から後々のことを考えて変数化をしておく癖がついてきたりもする。

(同じ理由でもう一つ変数化しておいた方がいい部分があるが、今回は無視)

See the Pen bmKQYm by sensq (@sensq) on CodePen.


今回はここまでにするが、他にも色々と機能追加や改良が考えられる。

例えば、


  • バリデーションチェック(正の整数以外が入力されたらエラー文を出す)

  • 開始値も設定できるようにし、任意の値から任意の値までの表を出力できるようにする

  • せっかくJavascriptなので、入力フォームを作ってコードを書き換えなくても値を設定できるようにする(GUI化)

こんな感じで少しずつできることを増やしたり、日々の作業を自動化したりしていくと、割と効率良く楽しんでプログラムを学ぶことができる。

ただし、この方法で学習していくと「わからないことがわからない完全に理解した人(ややこしい)」になっていくので、やはり体系立てた書籍等での勉強も必要なのだと思う。