プログラミング学習は3歩進んで2歩下がると効率が良い?
ここで言う「3歩進んで2歩下がる」というのは「ある程度先に進んでから前に学んだことを復習してみる」といった意味でしょうか。
最近昔勉強したことを学び直していてタイトルのように感じたので、本記事ではその心について書いていきたいと思います。
プログラミング学習で難しいと感じた部分は「後で戻ってくる」
プログラミングを学んでいると理解が難しい機能や概念に出会うことがあるかと思います。
どこが難しいと感じるかは人それぞれですが、オブジェクト指向、クラスなどは多くの方が躓くポイントかと思います。
自分の場合、ある程度理解するまで色々調べてしまうことが多いのですが、そのような場合には、「こう書いたらこう動く」という動作面をとりあえず把握して先に進むのが良いのでは、と考えるようになりました。
自分でコードを書いてみると格段に理解が進む
というのも、プログラミングの場合はとりあえず自分の手元で何かしら動作するコードを書いて実行することができるので、そこでガチャガチャ色々いじってみる、という学び方ができるからです。
本のサンプルコードでも良いので、とりあえず動くコードを作って動作面から理解していく方が頭の中で考えるよりも理解が進みやすいのではないでしょうか。
本のサンプルコードを実際に打ち込んで動かしてみる、いわゆる写経という勉強法が有効と言われるのはこの辺りが所以かもしれません。
特にクラスを利用してオブジェクト指向のプログラムを作成する場合、ある程度複雑な機能かつ規模のあるコードを書いてみないとそのメリットは実感できないかな、と思います。
(自分がある程度クラスを使えるようになったのは仕事を始めてからでした。)
実際に仕事で開発に携わるのが一番学びにはなりますが、それはリスキーなので…
やはり学んだ内容を利用して、自分で簡易なウェブアプリなどを作ってみるのが良い経験になると思います。
自分で作ってみると、「ここはどうして動かないんだろう?」というエラー対処だったり、「こう書けたら楽なのにな…」という改善点が思い浮かんだりします。
個人的な経験ですが、そのような時こそ前に難しくて飛ばした部分にヒントがあることが多かったです。
例:C++を学び直してみると発見が多かった
(以下は具体例なので読み飛ばしていただいても大丈夫です。)
最近仕事の都合でC++を復習していたのですが、学生時代に独学していた時には詰まっていた部分が今だとすんなり理解できて自分でも驚いています。
C++を独学で勉強している時は、解説がオブジェクト指向関連になると途端に分からなくなっていました。
説明を読み、コードを書いてみればどのような動作をするのかは分かります。
しかし、なぜそのような機能が必要で、どのような時に使うのか、どうしてこのような回りくどい(と当時は感じていた)ことをしなくてはならないのか、といったことが分かりませんでした。
実際にそれらの機能を使う場面がイメージできなかったわけですね。
例えば、C++の仮想関数。
一週間で身につくC++言語の基本 第6日目:virtualと仮想関数
ざっくりとどのような挙動をするかだけ説明すれば、
親クラスのメソッドを呼び出したとき、子クラスのメソッドで上書きして呼び出す機能
ということになるでしょうか。
昔独学していた頃は何でこのような機能があるのか謎でしたが、
仕事でPythonを用いてデータ分析、機械学習モデルの構築などを行うようになって、その有用性が理解できるようになってきました。
例えば、データ分析用に様々なデータソースからデータを集めて、各々から集めたデータを処理して一つのデータにまとめるような場合を考えます。
この時、データソースごとにデータのフォーマットなどが異なっていてデータソースごとに処理を記述する必要があるとしましょう。
これを実現するソースコードは以下のように書けます。
(以下のソースコードはPythonで書いているので、厳密には仮想関数ではなくダックタイピングになります。しかし、C++の仮想関数でも類似の機能を提供できるという意味でここでは例として挙げています。)
def process_data(processor):
processor.process()
class processor_for_A():
def __init__(self, data_source):
...
def process(self):
# データソースAから取得したデータへの処理
...
class processor_for_B:
def __init__(self, data_source):
...
def process(self):
# データソースBから取得したデータへの処理
...
def main():
processorA = processor_for_A("data_sourceA")
processorB = processor_for_B("data_sourceB")
# processorのクラスによらずprocessメソッドを呼び出す
dataA = process_data(processorA)
dataB = process_data(processorB)
# dataAとdataBをまとめる処理
...
C++で仮想関数を用いる場合は上記の processor_for_A(B) クラスの親クラスに仮想関数 process() を実装する必要がありますが、同様の機能を実装できます。
蛇足ですが以下のPythonコードを動かしてみると理解しやすいと思います。
(Geminiさんに教えてもらいました。)
def draw_shape(shape):
shape.draw()
class Circle:
def __init__(self, radius):
self.radius = radius
def draw(self):
print(f"Circle with radius {self.radius}")
class Square:
def __init__(self, side_length):
self.side_length = side_length
def draw(self):
print(f"Square with side length {self.side_length}")
circle = Circle(5)
square = Square(10)
draw_shape(circle) # Output: Circle with radius 5
draw_shape(square) # Output: Square with side length 10
このように、「1つの関数で派生先の機能を切り替えながら利用できる」というのが大きなメリットになります。
こう書いておけば、今後別のデータソースCでも何かしらの処理を実装する必要が出た時も、データソースC用のクラスを別で実装し、main関数はその処理の詳細までは把握しないで良くなります。
とはいえ、「実際に実行されている関数がどれなのか?」が分かりにくくなってしまうため、利用には注意が必要です。
よくよく設計を練って使うべき場面を限定した方が良いでしょう。
仮想関数自体中々難しい概念だと思いますが、自分が出会った問題をこうすれば解決できる!!というのが分かると途端にイメージがわいて仮想関数というものがよく理解できるようになりました。
プログラミング学習はたまに振り返りを入れてみると良いかも?
まとめになりますが、プログラミングの学習を進めていると理解が難しい所にぶつかることがあると思います。
そのような時は思い切って理解を飛ばして先に進んでしまうのも一手です。
自分で様々なコードを書くようになった後に読み返してみると、目の前が開けたように理解できるかもしれません。
そしてそれを自在に使えるようになればさらなる高みを目指せるかも…?
この記事が気に入ったらサポートをしてみませんか?