#きっかけ
**「65歳からのプログラミング入門」**という記事を読み、次の言葉に、感銘を受けました。
歳をとってからプログラミングするのは難しいという人がいる。
若い人でも、歳をとっても興味があるかどうかだけだと思う。
そう。結局、年齢は、言い訳を作りたいだけなんですよね・・・
(少なくとも、僕にとってはそうです。)
そして、この記事で、言語処理100本ノック 2015 の存在を知りました。
これは、もう、やるしかないなと。
python、ちゃんと使ったことはないのですが、解きながら調べながら勉強する方向で挑戦することにしました。
#方針
全ての問題を解くことは、最低限、必ずやり遂げるのは大前提として。
でも、解くこと自体が目的ではなく、問題を解くことでPythonを使えるようになることを目標にしています。
ですので、ただ問題を解いて終りにならないよう、理解に至るまでの過程、リファクタリングの過程、異なるアプローチからの解き方があるケースでは複数の回答を、また、関連する内容についても言及していこうと思います。
#挑戦前の時点での自分のスペック
完了した時に、どの程度成長できたかを振り返るため、挑戦前の時点での僕のスペックを明記しておきます。(2019年5月7日時点)
- Pytonは2、3、どちらも使ったことが無い。
- JavaScript(主にGAS)を、約半年くらい。
- Rubyを、少し触ったことがある。
- Linuxは、「UNIXという考え方―その設計思想と哲学」を読んだ。(触ったことはありません。)
- 統計とか数学とか、高校を卒業して以来、考えたことも無い(文型出身)
- 英語は、毎日、Google翻訳のお世話になってます。(せっかくなので、同時に、ゆるく英語の勉強も始めました。)
#環境
- Python 3.7.2
- Win10
#ということで、問題に
##00. 文字列の逆順
文字列"stressed"の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.
##【作成したコード】
>>> word = "stressed"
>>> print(word[::-1])
desserts
以下を参考にさせて頂きました。
- Pythonでリストや文字列を逆順に並べ替え(reverse, reversed)
- Pythonのスライスによるリストや文字列の部分選択・代入
- 【Python】スライス操作についてまとめ
- Python チュートリアル
すごい・・・。こんなに簡単に書けるなんて・・・。
が、なぜ・・・?
動きが理解できない・・・
#動きを理解するに至った過程
上記の参考サイトから、次の過程で理解が深まりました。
##開始位置、終了位置、増分など、基本的な動き
Pythonのスライスによるリストや文字列の部分選択・代入より
開始位置startと終了位置stopを指定
スライスでは選択範囲の開始位置startと終了位置stopを[start:stop]のように書く。start <= x < stopの範囲が選択される。start番目の値は含まれるがstop番目の値は含まれないので注意。
>>> alf = "abcdefg"
>>> print(alf[1:5])
bcde
なるほど。
開始位置startと終了位置stopに加えて、増分stepも指定可能。[start:stop:step]のように書く。
>>> alf = "abcdefg"
>>> print(alf[1:5:2])
bd
なるほど。
startを含み、stopを含まないということは、2文字目~5文字目(6文字目の一つ手前)、インデックス値でいうと1~4(5の一つ手前)になるので、「a bcde fg」となり、2個ずつ進んでいくので「b c d e」となって、結果「bd」が出力されると。
なるほど。
##なぜ、stop番目は含まれないのか
この点は、【Python】スライス操作についてまとめ、そして、Pythonチュートリアルより
スライスの使い方をおぼえる良い方法は、インデックスが 文字と文字の あいだ (between) を指しており、最初の文字の左端が 0 になっていると考えることです。そうすると、 n 文字からなる文字列中の最後の文字の右端はインデックス n となります。例えばこうです:
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
なるほと!
<補足>
引用中の例えの部分の幅が上手く指定が出来ず分かりにくいのですが、引用元のサイトには正しい幅で分かりやすい例えが示されていますので、お手数ですが、是非そちらをご参照下さい。
##後ろから並べてゆく
これに関しては、上述のPythonのスライスによるリストや文字列の部分選択・代入より
増分stepを負の値で指定すると、後ろから逆順で要素を取得する。
startから逆向きに値を取得していくため、startのほうがstopより後ろの位置を示していないと空になってしまうので注意。
>>> alf = "abcdefg"
>>> print(alf[5:1:-1])
fedc
なるほど!
- stepが「-1」だから、後ろから一つずつ抽出。
- 後ろから抽出してゆくんだけど、startはに設定するのは前から数えたindex値になる。このため、「5」を指定してるので、前から数えて6文字目(index的には5)から始まるので「f」から始まる。
- stopも、位置指定としては前から数えたindex値なので、「1」の一つ手前のになる。後ろから数えて「1」の一つ手前は、前から数えてindex値2、つまり前から数えた3文字目の「c」までを出力。
- 結果「fedc」になると。
文字でにするとややこしい・・・
ややこしいけど、なるほど!
##最終的な理解
上記までを理解した上で、「Pythonでリストや文字列を逆順に並べ替え(reverse, reversed)」の、次の説明を読み、大変すっきりしました。
スライスは[start:stop:end]の形で範囲や増分を指定する。start, stopを省略すると全体を選択し、stepを-1とすると後ろから一つずつ要素を取得することになるので[::-1]とすると逆順に並べ替えられたオブジェクトが取得できる。
なるほど~。
書き方は簡単ですが、理解するまでには、かなり時間がかかりました。
#JS、Rubyと比較
参考に、JavaScriptとRubyでも試してみました。
【JavaScript】
const word = 'stressed';
let reversed = [...word].reverse().join('');
console.log(reversed) // => desserts
//もしくは
const word = 'stressed';
let reversed = [...word].reduceRight((acc, cur) => acc + cur);
console.log(reversed) // => desserts;
//もっと簡単な書き方がありましたらご指摘下さいませ。
【ruby】
word = 'stressed'
puts word.reverse # => desserts
なるほど。
この問題に関して言うと、下記のような感じですかね。
- jsは他の2つに比べると、ちょっと複雑
- 直感的には、Rubyが一番分かりやすいかな?
- Pythonは書き方がとっても簡潔(理解するのに手間取りましたが・・・)
#雑感
最初の問題から、Pythonの簡潔な記述にがっつり心をつかまれました。
もしかして、問題を作った方は、そのために、この問題を最初にしてるんですかね?
だとしたら、まんまと、その意図に引っ掛かりました。(笑)
ま、問題が進むにつれて難易度が上がり、何度も心を折られることになると思いますが・・・。
今後、解いた問題がある程度溜まったら、まとめのページも作ろうと思います。
誤りや、もっと簡潔な書き方がある、等々、ご意見がありましたら、ご指摘頂ければ幸いです。
#補足:画像処理100本ノック
**「画像処理100本ノック」**というのもあるんですね。
言語処理の方を最後までやり遂げることができたら、ぜひ、こちらにも取り組んでみたいです。
だいぶ、だいぶ、だいぶ、先の話になるとは思いますが・・・
#参考文献(問題とは直接関係のない部分で)
今回の問題とは直接は関係ないのですが、Qiitaに投稿するにあたって、以下の記事の内容を参考にさせて頂きました。
- [個人的な良い記事のガイドライン = 2ヶ月前の自分が泣いて喜ぶような記事を書く]
(https://qiita.com/jnchito/items/5c3eb3640ad57b3edc6c) - [Qiitaで記事を公開するときに気を付けるべきマナーについて 〜無断でネットや書籍の内容を丸写しするのはやめよう〜]
(https://qiita.com/jnchito/items/215c2d51599eb29adabc) - [Markdown記法 チートシート]
(https://qiita.com/Qiita/items/c686397e4a0f4f11683d)
記事の書き方や、書く上での姿勢・マナー、といった内容になりますので、個別にどの部分を、というのを抜き出すのが難しいのですが、全体に渡って参考にさせて頂きました。
今後も活用させて頂きたいと思います。
#まとめ(問題とは直接関係のない部分で)
プログラムに触れることになって約半年になりますが、その間、本当に多くのサイトで学ばせて頂きました。
その過程で、「勉強した内容をブログ等でアウトプットすることが大事」という意見を数多く見かけました。
ですが、ずっと面倒で(あと、おぼつかない知識で書いた拙いコードを世に晒すのが恥ずかしくて)躊躇していたのですが、今回、記事を書いてみて、なぜそれが大事か、ということがとてもよく分かりました。
今回のように、初歩中の初歩の内容であっても、「単に自分が理解して動くコードを書く」のと「人に読まれることを想定して記事を書く」こととの間には、理解の度合い・深度が全然違うのだ、ということを、記事を書く過程で身をもって知りました。
また、今回の記事の話ではないのですが、Qiitaに初めて投稿した際、自分よりも遥かに技術力の高い方からご意見を頂き、それが本当にとんでもなく勉強になりました。
ただ単に良いコードを見て真似るのと(←勿論このこともとても大事だと思いますが)、自分が書いたコードに意見を頂いて直してゆくのとでは、こちらも、学びの深度が全然違うのだ、ということを体験しました。
お忙しい中、サンプルコードを示してご意見を頂いた方には、本当に感謝の言葉しかありません。
ですので、散々、色々な所で言われてる話ではあるのですが、僕と同じように、面倒だったり、恥ずかしかったり、といった理由で記事を書くことを躊躇されている方がいらっしゃいましたら、ぜひ、恐れずに、どんどんアウトプットしてゆくことをお勧めしたいです。
そして、その際は、是非、上にあげた[ガイドライン]
(https://qiita.com/jnchito/items/5c3eb3640ad57b3edc6c)、[マナー]
(https://qiita.com/jnchito/items/215c2d51599eb29adabc)、[チートシート]
(https://qiita.com/Qiita/items/c686397e4a0f4f11683d)等を参照して頂ければと思います。
大変ですし、恥ずかしさもありすが、それ以上に得るものがずっと大きいと思います。
僕も、今後も続けていきたいと思います。