プログラミング言語を独学で学び始める時のコツ
アイデア記事(ポエム)に限りなく近い技術記事かもしれません.
コンピュータの分野では,プログラミング言語やドメイン固有言語,独自のパラダイムをもつライブラリなどが膨大に存在し,また,新しく生まれ続けています.全てを網羅する必要はありませんが,状況に応じて新しく学ぶ必要が出てくることは多いと思います.
この記事は,プログラミング経験者が別の言語を新しく独学で学ぶ時のコツというか考え方について,筆者なりにまとめたものです.といっても,次の一文に要約されてしまいますが.
『サンプルコードは入力と出力をセットにして作成してから実行を確かめる』
ここでいう入力と出力は,必ず同じ種類のものを指します.REPL(Read-Eval-Print Loop,対話環境)を用いるのであれば対話による入出力,コンパイル/スクリプト実行であれば標準入出力,ファイル操作であればファイル入出力,GUIであれば画面出力と対応する各種入力,といった感じです.
Haskellを例に,具体的に述べていきます.
REPLを用いる場合
Haskellの処理系であるGHC (Glasgow Haskell Compiler)にはGHCiというREPLがあり,関数型言語ということもあって,プログラムを手軽に解釈実行できます.たとえば,"Hello, World!"の表示(とREPLの開始・終了)は次のように実行できます.
C:\Users\TAKIZAWA Yozo>ghci
GHCi, version 8.10.2: https://www.haskell.org/ghc/ :? for help
Prelude> "Hello, World!"
"Hello, World!"
Prelude> :q
Leaving GHCi.
C:\Users\TAKIZAWA Yozo>
この時,出力のための関数や構文を使いたくなるかもしれません.Haskellでは,副作用を伴う演算である『I/Oアクション』のひとつに,引数を文字列として出力するprint
があり,文字列のみを入力した場合と同じ表示が得られます.このように書くと,初めてHaskellに触れた人には,文字列表示がとても複雑な処理に見えます.実のところ,実際その通りです(苦笑).
Prelude> print "Hello, World"
"Hello, World"
Haskellは純関数型言語のひとつであり,副作用を伴う関数処理,すなわち,同じ引数の値を用いた処理の結果が状況によって変化する関数は想定されません(参照透過性). print
は関数ですが,文字列表示はあくまで副作用であり,何か処理を行った戻り値ではありません.そして,関数print
の戻り値には特に意味がなく,通常の関数処理とは異なる扱いとなります.これが,複雑な処理に見える理由であり,また,入力処理についても同じ『I/Oアクション』のひとつとして処理を定義・記述することになります.
…といったことは後回しにして,基本的な構文等をまず最初に覚えたいのではないでしょうか.REPLを用いた環境では,出力方法だけを最初に覚える必要はなく,あらためて入力方法を覚えようとする時に一緒に学んだ方がわかりやすくなります.ちなみに,Haskellでの関数定義およびその利用方法は,REPLで次のように確認することができ,同じく出力方法は必要ありません.
Prelude> func x y = if x == y then "Yes" else "No"
Prelude> func "Hello" "World"
"No"
Prelude> a = "World"
Prelude> func a "Hello"
"No"
Prelude> func a "World"
"Yes"
コンパイル/スクリプト実行の場合
プログラミング言語によってはREPL(対話環境)が利用できず,コンパイルやスクリプト実行しかできない場合があります.HaskellのGHCについても,利用環境によってはコンパイラしか利用できないことがあります.
その時には,出力方法だけでなく入力方法についても最初に覚えてしまいましょう.Haskellの場合は,表示関数print
に加えて文字列一行入力getLine
を利用すると,とりあえず文字列のみの入出力が可能となります.
main = do
x <- getLine
print x
$ ghc hello.hs
[1 of 1] Compiling Main ( hello.hs, hello.o )
Linking hello ...
$ ./hello
Hello, World! (← 入力)
"Hello, World!"
ここで,main
は(C言語のmain関数と同じく)最初に実行するべき処理を示しています.do
は,次の行から字下げして記述されている複数のアクションを,ブロックとしてまとめる時に使うキーワードのひとつです.=
によって,do
によるブロックがmain
の処理であることを示しています.
なお,一文であればブロックにする必要がないこともあり,"Hello, World!"の表示のみを行うのであれば,次のように記述することができます.
main = print "Hello, World!"
$ ghc hello.hs
[1 of 1] Compiling Main ( hello.hs, hello.o )
Linking hello ...
$ ./hello
"Hello, World!"
より簡潔に表現できますが,では,REPLの時のように,続けて関数定義およびその利用方法を確認するにはどのようにすれば良いでしょうか?入力方法がわからないままであれば,次のように,様々な引数の値を埋め込んで実行することになり,また,プログラム記述と表示結果の対応がとりにくくなります.あるいは,プログラムの書き換えと実行を繰り返すことになるでしょうか.
func x y = if x == y then "Yes" else "No"
a = "World"
main = do
print $ func "Hello" "World"
print $ func a "Hello"
print $ func a "World"
$ ghc func.hs
[1 of 1] Compiling Main ( func.hs, func.o )
Linking func ...
$ ./func
"No"
"No"
"Yes"
ここで,$
は( )
を省略した記法で,たとえばprint func a "Hello"
と書くとfunc
がprint
の引数とも解釈できてしまい,また,print
は引数をひとつしか取らないため,エラーとならないよう(func a "Hello")
と括る代わりに$
を用いています.
次は,getLine
を併用したサンプルコードの例です.
func x y = if x == y then "Yes" else "No"
main = do
a <- getLine
b <- getLine
print $ func a b
$ ghc func.hs
[1 of 1] Compiling Main ( func.hs, func.o )
Linking func ...
$ ./func
"Hello" (← 入力)
"World" (← 入力)
"No"
$ ./func
"World" (← 入力)
"World" (← 入力)
"Yes"
もちろん,表示のみのタイプであっても,どの記述の処理結果かを表示する記述を加えて見やすくするといった工夫をすることもできます.ただ,上記の例について言えば,print
は必ず改行してしまうため,対応する記述の表示には別途putStrLn
を用いる必要があるとか,a = "World"
をdo
ブロック内に書いてプログラムコードの方をわかりやすくしたい場合はlet
を用いる必要があるとか,どうにも『次のステップ』に回したくなる事柄が増えてきます.
雑記
C言語を学び始める時の定番である"Hello, World!"プログラムもいくつかの『おまじない』(#include
,void
,return
等)が含まれており,詳細は後回しとしつつ基本的な構文(セミコロンを付ける,ダブルクォートで括る,など)をまず理解する,という流れで学んだ人が多いかと思います.この方法自体は秀逸であり,『実行すればなんらかの反応がある』ことでプログラミングの実感を得るという意味では,代入や演算,制御構文から学ぼうとするよりはるかに学習効果が高いのは確かです(『C以前』は,そういった言語仕様から学び始めることが多かったのも確かです).
ですが,後回しの事柄を扱うことが保証されている学習コースで学ぶ場合はともかく,独学で最初から『おまじない』として覚えることが多いと,意味もわからずコピペと修正を繰り返すクセがついてしまい,とりあえず動けば『おまじない』の部分は理解しないまま放置されることがあります.また,コンピュータという機械はプログラムに沿って動く言語処理システムであり,そのプログラムも本質的には入出力を伴う言語処理システムとなります.そのことを意識しながら学んでいくためにも,最初から出力に偏った言語学習は避けた方が良いのではないかと思います.
備考
記事に関する補足
- 雑記の最後はなんか大げさに聞こえるかもしれませんが,(別サイトの記事でも触れているように)当方の立場上,毎年悩んでいる事柄だったりします.『原理とかどうでもいいから,役に立つ技術(という名のコピペ元)をさっさと教えろ!』という言い方もされることがあり….
更新履歴
- 2020-11-07:初版公開
Discussion