Chienomi

プログラミングできるようになるにはどうすればいいか

プログラミング::beginners

なんか世の中ではこういうことを口にするエンジニアが結構いるらしい。

いや、エンジニアなのにそもそもプログラミングできないの? という疑問もあるのだが、世の中のIT企業的には要求点はそこではないらしい。

ここでは、一般的に言われていることとはかなり違う切り口でこの問に対する答を示していこうと思う。

前提

まず、プログラミングできるようになるにはプログラミングをしなければならない。

楽しいか、苦痛かはさておき、少なくとも膨大な努力をしなければならない。 そして、その努力は継続されなければならない。

既にプログラミングができる人は、決してなんらかの魔法によって突然プログラミングができるようになったわけではない。 プログラミングの経験を積み重ねたのだ。

つまり、努力したくない、プログラミングしたくない、労力を払いたくないという人がプログラミングできるようになる方法はない。

だから、「プログラミングできるようになるには」という話を、「努力せずに」といった前提を隠し持った上でしているのであれば、そんな方法はない。

そもそも、プログラミングというのはそれ自体が職業的行為ではなくて、日常的な生産である。 そして、多くの人にとっての趣味であり、楽しんでやっている人もとても多い。 楽器を弾かずに楽器が上手くなる人はいないし、物語を書くことなく名作が書けるようになる作家もいない。 プログラミングもまた同じことだ。

コードは料理に例えられる。消費される芸術だ。 プログラミングの上達は、料理の上達と似ている。 学び、実践、そして上達への強い意思がそれを成し遂げる。

ファーストステップ

第一に、コンピュータに対して関心を持つことである。

OSやシステム、メモリ、計算時間、コンピュータ資源、CPUといったものに対して関心を払う。 「なぜだろう」「どうなっているんだろう」「なにをしているんだろう」ということに対して全く興味を持たないのでは話にならない。

別の言い方をすれば、興味を持つことでコンピュータに対する基本的な操作を習得することにつながってくる。 それによって、プログラミングを始める下地ができあがるのだ。

その次が、「あなたの開発環境」を作ることである。 手ずからコードを書くことを前提として、それを書くための環境を作る。

Windowsユーザーであれば、Git for Windowsを導入することにより必要な全てが揃う。 Linuxユーザーであれば、メジャーなLinuxディストリビューションは、既に開発に必要なものを取り揃えているだろう。 個人的には、Windowsユーザーでもプログラミング学習用にLinuxを導入することを勧めたいところである。

Linux導入を推すのは、「そのほうが余計な手間や引っ掛かりポイントが消えるから」だ。 プログラミングができるようになる人は基本的に何も言わなくても自分でがんがん書いていくので、そのようなことを言う時点で、引っかかりポイントのたびに躓く可能性が高い。 もっとも、Linux導入ということでハードルが上がると、そういう人は「ハードルが上がると始められない人」でもあったりするのだが。

さて、そしてお気に入りの プログラミングに適した エディタを導入する。 Windowsならサクラエディタや秀丸エディタを推す人が多いだろうが、これらがプログラミング向きであるという意見には私は賛同致しかねる(もちろん、できないということはないし、好みに合うのであれば使えばいいが、プログラミングの労力を大きく軽減してくれるものではないと思う)。 一方、LinuxでVim, Emacs, Gedit, Pluma, Xed, Kwrite/Kateなどに好みのものがあるのであればそれを使えば良い(少なくともMousepadやLeafpadがプログラミング向きだとは私は全く思わない)。

私が推奨するのはAtom、あるいはVSCode(もしくはVisual Studio Code)である。 Sublimetextが好き、といったことがあれば、それも好きにするといい。

エディタを起動したら見やすいUIに設定する。 Windowsであるならばフォントファミリーを(追加のコーディングフォントを持っていないならば)Consolasに設定する。 AtomやVSCodeの場合は、カラーテーマによって言語ごとにシンタックスハイライトが効くかどうかに差があるので、実際のコードを見ながらできるだけハイライトされるカラーテーマを選ぶと良いだろう。 なお、私はRuby/Markdownを中心にVSCodeで書いているが、カラーテーマはDank Neon, Dracura, Monokaiなどを使っている。

そして、必要ならあなたが使いたい言語の処理系を導入する。

くれぐれも言っておくが、あなたがプログラミングを習得する際に使用すべき言語と、業務で使用するあるいは要求される言語は別である。 プログラミングやコンピュータのスキルと、仕事というものは完全に別の話だと認識することは不可欠だ

最初のコード

だいたい次のようなコードを書く。

print "Hello, world!\n"

これはPrelでもRubyでも動く。

$ perl hello.pl
Hello, world!
$ ruby hello.rb
Hello, world!

だが、こんなコードを書いて楽しいと思うのは難しすぎる。 私が最初に書いたコードはもうちょっと複雑だった。

あなたが最初にプログラミングの魔力を味わうなら、次のようなコードが良いだろう。 これは、ブラウザを開き(Firefox、あるいはGoogle Chrome/Chromium/Vivaldi/Operaである)、Ctrl+Shift+Iを押し、“Console”を選択しプロンプトに対して次のように入力するのだ。

この際、ページはどこでも良いのだが、できれば色々とロードして色々と余計なことをして色々とエラーを出すことのないページが良い。 Chienomiはその意味では最適である。

alert("こんにちは、" + prompt("あなたの名前を教えてください") + "さん")

これによって、「名前を訪ね、名前を含める形で挨拶する」という動作になる。

これは次の形になっている。

alert(...)

これで、ウィンドウダイアログによってメッセージを表示する。 括弧の中はメッセージの内容だが、それは

A + B + C

の形式になっている。これは全部連結されてひとつのメッセージになる。

さらに、Bは次の形式になっている。

prompt(...)

これは、質問ダイアログによって入力を求める。括弧の中身はその際に表示するメッセージになる。

この構造がわかっていれば、色々といじれるところがあるだろう。 最小の(退屈な)ものでは

alert("Hello, world!")

になる。

ここでは3つの要素を示した。どんどんいじって遊ぶといい。 「いじらなくても理解したからそのまま次に進もう」と考えてはダメだ。ここでいじって遊ぶことが何より大事なのだ。 逆に言えば、これでなんの感動もしないし、楽しくもないし、いじろうとも思わないという人はプログラミングに向いていない。 プログラミングは好奇心の向き方として明確な向き不向きがある行為であるから、向いていないなら早々に諦めたほうが賢いように思われる。

できれば何もみないでいじってほしいところだが、例示するなら次のようなことがある。

// 連結の例
alert("アルプス" + "山脈は" + "だいたい" + "大きい")

// 聞く場所を変える
alert("会社員の男性(" + prompt("あなたの年齢を教えてください") + ")" )

// 複数ヶ所を聞いてみる
alert(prompt("あなたの名前を教えてください") + "さん (" + prompt("あなたの年齢を教えてください") + ")")

プログラムらしさ

こうして「自分の書いたものが動く」という感動は得られただろうか。 だが、同時に問題点も感じられたはずだ。 例えば先程のコード

alert("会社員の男性(" + prompt("あなたの年齢を教えてください") + ")" )

について、入力できるのが数だけではないので、会社員の男性(に見える魔法使いの女性)みたいなことができてしまう。 そして、

alert(prompt("あなたの名前を教えてください") + "さん (" + prompt("あなたの年齢を教えてください") + ")")

に至っては非常に読みづらい。それにせっかく聞いた名前も再度利用することができない。

そこで登場するのが「変数」だ。 私は「変数」と「制御」のふたつがプログラムをプログラムたらしめる最大の要素だと思っている。

次のように書いてみる。

name = prompt("あなたの名前を教えてください")
alert("こんにちは、" + name + "さん")

2行になり、コンソール上では2度に渡って実行することになる。 だが、その分何をしているかがだいぶ明瞭になった。

そしてnameは再利用可能だ。

name = prompt("あなたの名前を教えてください")
alert("こんにちは、" + name + "さん")
alert(name + "さんは今日もプログラミングをがんばっていますね")

ステップアップ

「関数を覚えるべきか、構文を覚えるべきか」という質問は不毛である。 これは語学学習において「単語と文法どっちを覚えるべきか」と言っているのに等しい。当然ながら、これの答は「両方」である。

例えば、先程の年齢をあくまで年齢として答えさせたい場合、次のような書き方が考えられる。

var age
var intRE = new RegExp('^[0-9]+$')
while (!age) {
    age = prompt("あなたの年齢を教えてください")
    if (!(intRE.test(age) && new Number(age) < 200)) {
        age = null
    }
}

これを実現するために必要なのは、関数か、構文かと言われれば、両方というしかない。 つまり、あなたが必要とするもの、したいと考えることを、実現するために覚えると良いということだ。

プログラミングの入り口に立つにあたってポイントとなることはいくつかある。

  • 興味を持ったことを徹底的に追いかけるべきだ。例え現状自分には困難だと思ったとしても、それを達成しようという情熱が必要である
  • その達成しようとすることはグランドデザインではなく、個別的なものであるべきだ。ここでは「年齢として数値だけに入力を限りたい」をテーマにした
  • 小さなゲームを作ることを目標にすると良い。すぐにフィードバックが得られ、一歩ずつ前進できる
  • 小さな実用的プログラムを書くと良い。自分のしていることを意味を成すことを確認できる
  • 簡単な、わかりやすいコードを探して、そのコードを自分なりにいじってみることは理解の大きな助けになる

例えば、誰かに習うとか、業務で使うとか、学校に行くとか、どこかのサイトで学ぶとか、そういった手法に対する「正解」というものはない。 どのような方法で学んだところで、結局のところ「どうやって覚えたか」に従って知識やスキルに偏りは出る。

業務で覚えたプログラミングは業務で使う知識を覚えるだけになってしまうし、小さなユーティリティばかり書いていると計算量やスケールに関する感覚が身につかない。競技プログラミングで覚えたプログラミングはアルゴリズム優先の考え方になってしまい「何を作るか」という発想が磨きにくい。

結局のところ、「どこから入って、どう深め、どう拡げるか」の話である。 戦略はあったほうが良いが、始めるにあたって考えなければならないことでもない。 とにかく日々プログラミングするようになるところまで到達するのが先だ。

プログラム例

最近ちょっと何度か私の周りで話題に出たFizzBuzz。 3の倍数でFizz、5の倍数でBuzz、15の倍数でFizzBuzzと言い、それ以外ではその数を言うものだ。

foreach $i (1..100) {
    if ($i % 15 == 0)    { print("FizzBuzz\n"); }
    elsif ($i % 5 == 0 ) { print("Buzz\n"); }
    elsif ($i % 3 == 0 ) { print("Fizz\n"); }
    else                 { print("$i\n"); }
}

これはPerlコードである。 次に示すのは剰余を使わない、という縛りを加えたRubyコードの例。

fizzbuzz = Array.new(16)
fizzbuzz[3] = "Fizz"
fizzbuzz[6] = "Fizz"
fizzbuzz[9] = "Fizz"
fizzbuzz[12] = "Fizz"
fizzbuzz[5] = "Buzz"
fizzbuzz[10] = "Buzz"
fizzbuzz[15] = "FizzBuzz"

o = 1

(1..100).each do |i|
  puts(fizzbuzz[o] || i)
  o += 1
  o -= 15 if o > 15
end

Rubyで1行で書く(;if-elseも使わない)。

(1..100).each {|i| puts([[15, "FizzBuzz"], [5, "Buzz"], [3, "Fizz"]].select {|x| i % x[0] == 0 }.first&.[](1) || i) }

次はブラックジャックである。 ただし、スプリットやダブルダウンなどはない。

rand = Random.new

def calc(cards)
  ace = cards.include?(1)
  score = cards.map {|i| i >= 10 ? 10 : i}.sum
  if score < 11 && ace
    score + 10
  else
    score
  end
end

dealer = []
player = []

dealer << rand.rand(13) + 1
player << rand.rand(13) + 1
dealer << rand.rand(13) + 1
player << rand.rand(13) + 1

if dealer.include?(1) && dealer.sum > 10
  puts "Dealer made BLACKJACK! (#{dealer.inspect})"
  exit false
end

if player.include?(1) && player.sum > 10
  puts "You made BLACKJACK! (#{player.inspect})"
  exit
end

puts "Dealer cards are #{dealer.inspect} (#{calc(dealer)})"

while((score = calc(player)) < 21)
  printf("Current: %d (%s)\n", score, player.inspect)
  print "Hit? [Y/n]: "
  c = gets
  if c =~ /^[Nn]/
    break
  else
    newcard = rand.rand(13) + 1
    puts("You took #{newcard}")
    player << newcard
  end
end

if score > 21
  puts "BURST!"
  puts "YOU LOSE"
  exit false
end

while((score = calc(dealer)) < 17)
  printf("Dealer's Current: %d (%s)\n", score, dealer.inspect)
  print "Hit? [Y/n]: "
  newcard = rand.rand(13) + 1
  puts("Dealer took #{newcard}")
  dealer << newcard
end

if score > 21
  puts "BURST!"
  puts "YOU WON"
  exit
end

puts "Dealer standed at #{score}."
puts "YOU #{calc(player)} : DEALER #{score}"

case calc(player) <=> calc(dealer)
when 0
  puts "DRAW"
when -1
  puts "YOU LOSE"
  exit false
when 1
  puts "YOU WON"
end

初学向けの愚直なコードだが、意外とテクニカルな要素もある。

推奨する学習

学習したい言語が何であるかによらず、オライリーのプログラミングPerlは読むべき本だと思っている。 内容は些か古いが、プログラミングの極意が詰まっている。

ただ、テキストフォーマッタの話とか、オブジェクト指向周りの話はPerl以外では役に立たない内容なので学習する必要はないだろう。 それに、下巻はPerlを書かない人には必要ない。

もっとも、プログラミングPerlはやや難しい書籍という扱いである。 実際、「初めてのPerl」というその前段になるような本が出ているくらいだ。しかも、「続・初めてのPerl」なんて本もある。

ほとんどの場合、はじめての、とか、サルでもできる、みたいな本は役に立たない。 著者の実力が不足していて俯瞰的でなかったり、誤った知識前提に基づいて書かれていたりすることが目立つ。 どれほど初歩的な内容であったとしても、言語について幅広く、深く理解している人が書いた書籍が良いだろう。

これは、このようなタイトルの本は須らく役に立たない、ということを述べているのではない。単純に経験則として、地雷率の高いタイトルである、という意味である。

こうした「初心者向けの書籍」の中では、私は高橋茉奈さんの「やさしい」シリーズを強く推奨したい。 実践的なプログラミングに関する内容がないため、これを読んで覚えたところで何かがかけるようになるわけではないのだが、言語に関する基礎知識をつける上での初歩をしっかりじっくりやってくれる。

正直、コンピュータに関して良い書籍を見つけるのはとても難しい。 書籍でもウェブサイトでも、私なりに「良い書籍、良い文献」というのは挙げられるが、体系的学習方法として「これを使って学習するのが間違いない」という言い方ができるようなものはない。 (というより、そんなものがあるとしたら、Mimir Yokohamaでプログラミングのベーシックコースはなくしてしまうだろう)

また、直接的に関係(特に利害関係)のある誰かに教わるのは推奨できない。 そもそも教えられることは自身のスキルを上限とする、という問題がある上に、利害関係にあると、あまりにも成長してしまうと自分の立場を脅かす、というような危機感が働くため、「最適な教え方」をすることが難しいからだ。

ちょっと前までは「とにかくソースコードを読め」と言われたものだが、それも私は推奨しない。 読めるものならいいが、高品質で学習で読みやすいコードがあんまりないのだ。

だから、基本は「調べる」「書く」にあると言っていいと思う。

このとき何を調べるかは大事だ。信頼のおける、網羅的なドキュメントがあるものを読みたい。 例えばRubyであればるりまがある。 リファレンスとして読むことが多いだろうが、るりまにはRubyによるプログラミングの基礎が書かれており、概念さえ理解していれば非常に強力である。ただ、Rubyに関する詳しい説明はない。Rubyをステップバイステップで理解したいならば、オーム社から出ている「プログラミングRuby 1.9」を読むと良い…ということになるだろうが、こちらは2010年と古い本で、しかも既に絶版。これに変わる「これぞ」という本が残念ながら出ていないので、Perlのように「この本さえ読めば」というものがない。 ひょっとしたらRubyを「ちゃんと理解しようと」学ぶことは意外と難しいかもしれない。(ほとんどがRailsである大量の本から適切なものを引き当てることができるのならば良いのだが)

また、JavaScriptに関してはMDNが素晴らしい情報を提供してくれている。 るりまと比べても内容は専門性が高く高度なものだが、間違いなく「わからないことを調べる」という点において類を見ないものである。

PythonにはPythonチュートリアルがある。 このためにPythonで始める人も多いだろうが、これはチュートリアルと言うには説明が端折られていて、単独で理解に至ることができない。 本や外部リソースと併せて読むことが必要になる。 ただ、Pythonに関しては情報量も豊富なので学習に困らないという面もあるだろう。

逆に日本語ドキュメントに乏しいLuaは学習が難しい。Perlも、「プログラミングPerl」による学習に頼り切ってしまうもので、Perlに関する優れた公式の、あるいは公式に近いウェブリソースは残念ながらない。膨大なPerldocがあるのに……

もちろん、Mimir Yokohamaは優れた学習を提供する。 Mimir Yokohamaはどちらかといえば言語によらない、プログラミングの世界と概念を重視した授業を展開している。 このような観点からのプログラミング学習は大学などで行われているが、大学のものと比べ非常にpracticalであるため方向性はだいぶ異なる。

Mimir Yokohamaにとって生徒の成長、生徒の実績は純粋なプラスである。達成感という意味でも、プロモーションという意味でも、これ以上のものはない。 利害が対立しないというのは教える上で重要なポイントになりやすい。

残念ながらMimir Yokohamaのウェブサイトに掲載されているプログラミングに関するテキストはまだ中途半端な状態だ。 もしあなたが200万円くらいMimir Yokohamaに寄付すれば目覚ましい進捗を得られるだろう。 (ChienomiやMimir Yokohamaのウェブサイトに掲載している内容で、私はなにもリワードを得ていないのだ!1)

結局のところ、重要なのは何かを信じ込むのではなく、様々な知識の断片をかき集めながら、あなたの関心を補完して形にしていく必要がある。 現状、それができない者はプログラマたることができないと言っていいだろう。

つまり、別に本を読んでもいいが、同時に公式リソースなどを見ながらその意味や知識を正しく補完していくことが重要なのだ。 「ここでこう言っているが、つまりこれはこういうことだから、こういう話になるのだ。ということは、こうしたらきっとこうなるのだろう」と考えながら自分で生み出しつつ、そしてその結果を以てその仮定が正しかったか、誤っていたかということを理解に結びつけていく。

多少書けるようになったが、自分が書こうと思うプログラムがないのならば、プログラミングをパズルとして遊んでみるのはどうだろうか。 例えばAtCoder Beginners Selectionをやってみる。PaizaのC問題やB問題にトライしてみるのもいい。 これらの問題は、プログラミングに何かを生み出せることを意味するようなものではないが、「動くプログラムを書ける」ことを確認することに役立つ。

もちろん、計算量という課題に挑むパズルとしてより高度な問題に挑んでもいい。つまり、PaizaでAやS問題をやったり、AtCoderで水色を目指してみたりだ。 プログラミングは実用的な生産をしなければならないという制約があるわけではない。 私はpractial coderだから即時生産性のあるコードを好んで書くが、それが全てではないのだ。

そしてまた、前述の通り、自分が書きたいプログラムのためにそれに必要なスキルを身に着けながら書く、というのも良い方法だ。 それならば強力な公式リファレンスがあればそれを頼りに書き上げることもできるだろう。 ただ、アドバイスするならば、最初からあまり大きなものを作ろうと考えないほうが良い。サンプルで出したブラックジャックも、最初のほうで取り組むには重すぎる。

また、いきなり全てを備えたものを書かなくても良いだろう。コード片を書いていけばいい。 例えばブラックジャックならば、カードを取るところだけ書いてみる、ということだ。 だんだん問題点を直したり、機能を拡充したりというのはまさにステップバイステップの学びになる。

card = 0
rand = Random.new
while card <= 21
  next_card = rand.rand(13) + 1
  card += next_card > 10 ? 10 : next_card
  printf("%d (+%d)\n", card, next_card)
end

「そんなことよりお前はどうやって覚えたのか」

私に関しては、本に書いてあるコードを写経して覚えた。 というか、まだ時代的にコードは出版されることが多かった(ディスクに入った状態ではなく、本に印刷してプログラムが売られていた)頃だし、そういうことがしやすい環境にあった。

あとは、「こういうの作りたい」「こういうことができるようになりたい」「これがしたい」を原動力に覚えた。

ただ、それだけだ。

Newbieのために

いつでも忘れないでほしい。

プログラミングは楽しい。

それは誰かのためではなく、あなたにとって最高の趣味となりえる、この上ない遊びとなりうるものであり、同時にあなたの生産性を高めて時間を生み出し、またあなたの身を助ける芸となりうるものなのだ。

こうしなければならない、というような意識にとらわれる必要はない。 無限に湧き上がる好奇心を追いかけていれば、プログラミングスキルは自然とついてくるものだから。