見出し画像

【 超 重 要 】小学生でも分かる!Pythonプログラミング - for文 / 多重ループ / iterable_object / range関数 / list関数

← preview

next →


useless

「無駄」って、一体何だろう?


今回は、この九九の表を
作ってみたいと思いまーす。👇

完成イメージ

どうすれば作れると思いますか?
考えてみましょう。

今まで得た知識では...
「ひたすら81個のprint文を書く」とかしか
思いつかないでしょうか...。

print(1*1)
print(1*2)
print(1*3)
...(中略)
print(5*1)
print(5*2)
...(中略)
print(9*7)
print(9*8)
print(9*9)

何という無駄な労力でしょう。

そういえば、今までの記事でも
とても面倒な記述がありました。

同じような記述を繰り返している。
これは面倒だ。ホセ・メンドーサ。

>> list_a * list_b ...?

例えば、リスト×リストをしたら
もっと効率良く九九の表を作れないだろうか?

a = [1,2,3,4,5,6,7,8,9]
b = [1,2,3,4,5,6,7,8,9]

print(a*b)  # >>> Errorになっちった。

発想はいいですね。

しかし、リストとリストの計算は、
値を1個1個取り出さないとする事ができません。

エラー
になってしまいます。

リスト + リスト あるいは
リスト × 整数(回数) ならできるのですが...

a = [1,2,3,4,5]
b = [1,2,3,4,5]

print( a + b ) 

# … [2,4,6,8,10]
👆こういう結果を期待してたのに...

# >>> [1,2,3,4,5,1,2,3,4,5]
👆実際はこうなってしまいます。
ただの連結だ!!

"リスト同士の連結" は出来ても、
"リスト同士の計算" は出来ない
のです。


 >> list型のオブジェクトを計算に使いたい

「 効率の悪い行為を繰り返す 」というのは、
人間にとってかなりの苦行です。

面倒な事を機械に任せるために
プログラミングをしてるのに、

手入力で何十、何百回もの
print文を書くなんて愚の骨頂。

スプーンで油田を採掘して
醤油ちゅるちゅるでちゅるちゅるして
石油を採取するようなものです。

「3度目は言わせないでくださいよ」
と頼んだはずだ。

何度も言わせるってことは......無駄なんだ。
無駄だから嫌いなんだ。無駄無駄...。
ジョジョの奇妙な冒険 第5部 黄金の風 『黄金体験 (ゴールド・エクスペリエンス)』

先ほどアイディアとして出た
リスト×リスト」なのですが、
非常に惜しいんですよ。

リストは「list型オブジェクト」という
まとまった1つのオブジェクトです。

リストはlist型オブジェクトという「まとまり」。

a = [1,2,3,4,5]
b = [1,2,3,4,5]

👆この変数a,bが預かっているのは
リスト全体のオブジェクトIDであって
リスト内の値のオブジェクトIDではない!

中に何が入っているのかは
取り出してみないと分からない。

だから "[リスト]自体" は計算に使えない。

リストの中の値を計算に使いたいなら
値を1個1個取り出そう!!

ということです。

どういう手法であれ
"取り出せれば" 計算に使える。

しかしながら...
index[n]で取り出す手法を使うと...

a = [1,2,3,4,5]
b = [1,2,3,4,5]

print( a[0] * b[0] )
print( a[0] * b[1] )
print( a[0] * b[2] )
print( a[0] * b[3] )
待て待て待て待て
待て待て待て待て
待て待て待て待て
待て待て待て待て

待て待て待て待て!!!!

これじゃ81個のprint文を
書くことになるじゃないか!!

...というところで「 for文 」の出番です。


for ...

repeat...

>> for i in [ iterable_object ] :

〔 for文 ( フォーぶん ) 〕

「for文」とは、同じ処理を一定回数
繰り返す
ための命令文です。👇

for i in animal:

リストの要素が順番に
改行されて出力されていますよね。

これはどういうことでしょうか?

この図を見てください。👇

"A" のオブジェクトID を「 i 」へ代入して処理を実行
"B" のオブジェクトID を「 i 」へ代入して処理を実行
...
"E" のオブジェクトID を「 i 」へ代入して処理を実行


見てわかる通り、
変数「 i 」に何度も
オブジェクトIDを代入していますね。

1つの変数に代入できるのは
1つのオブジェクトIDだけなのに。

と思うかもしれませんが、こう考えて下さい。

私たち人類は、
家族が5人だった時に
お風呂を5つも作るだろうか?


ってね。

1つのお風呂に
順番に入ればいいじゃないですか。

前の人が使い終わったら
次の人が使えばじゃないですか。


animal = [ "こぶた""たぬき""きつね""ねこ" ]

for i in animal :
  print( i )
  #処理...
  #処理...
  #処理...
  #処理...
  1. animal から値が1つ取り出される

  2. 取り出された値のオブジェクトIDが変数 i に代入される

  3. for i in animal : 以降の処理が行われる

  4. 一連の処理が終わったら 1. に戻る

  5. animal に格納されている値が尽きたら終了

このように

1つの変数に対して
1つのオブジェクトID
を預ける

という決まりを守りつつ、

1つの変数を使い回すことによって
効率良く処理を行なっていますね。

私は → ( 最初の値のIDは )
お風呂に → ( 変数に 
入って → ( 代入して 
身体洗ったから → ( 処理し終わったから )

次の人 → ( 次の値のIDを )
お風呂に → ( 変数に )
入って → ( 代入して )
身体洗って → ( 処理を始めて ) 

っていうのと似ています。

ちなみに、
変数に1つ値が代入されれば
処理が1回始まるので、

変数に代入した値は
別に使わなくても大丈夫です。

1,2,3は別に使ってない。

>> multiple_loops = 多重ループ

multiple_loops.

〔 multiple_loops ( マルティプル_ループス ) 〕

例えば、規模を少し小さくして...

a = [1,2,3,4,5]
b = [5,6,7,8,9]


この2つのリストを使って掛け算表を作る事を考えましょう。

"リストから値を取り出さないと掛け算できない"

というPythonルールを考慮するなら...

aから「 」を取り出して...

bから「 」を取り出して掛ける
bから「 」を取り出して掛ける
bから「 」を取り出して掛ける
bから「 」を取り出して掛ける
bから「 」を取り出して掛ける
aから「 」を取り出して...

bから「 」を取り出して掛ける
bから「 」を取り出して掛ける
bから「 」を取り出して掛ける
bから「 」を取り出して掛ける
bから「 」を取り出して掛ける
aから「 」を取り出して...

bから「 」を取り出して掛ける
bから「 」を取り出して掛ける
... (中略)
bから「 」を取り出して掛ける

これをa,bそれぞれの要素が尽きるまで
繰り返せばいいわけですよね。

つまり、

aから値を取り出すfor文」と
bから値を取り出すfor文」の

2種類のfor文
が必要ということであります。

多重ループ

👆このように、

for文の処理内容の中に
別のfor文を書くこと


「 多重ループ(multiple_loops) 」と言います。

この多重ループを使う事によって、
九九の表が作れそうですね。

a = [1,2,3,4,5,6,7,8,9]
b = [1,2,3,4,5,6,7,8,9]

リストの内容を変更すれば、
9×9=81までの一覧が出てきます。

やってみましょう。

でーきた できた

iterable

stairs.

>> iterable_object = 反復可能なオブジェクト

〔 iterable_object ( イテラブル_オブジェクト ) 〕

for文
を使うときは、
"何か"
から値を1つ1つ取り出して
そのオブジェクトIDを変数に代入する
必要があります。

その「 値が取り出される"何か" 」のことを
「 反復可能なオブジェクト 」と言います。

「 iterable_object ( イテラブルなオブジェクト ) 
と書かれる事も多いですね。

そのため今後 [ iterable ] などと表記する事があります。

(※僕独自の表記です。真似する必要はありません)


separate...

反復可能なオブジェクト[ iterable_object ]
とは何でしょう?

値を1つ1つ取り出す事ができるオブジェクト」のことを
反復可能」と言っているのですが、

少しモヤッとするというか、
具体的にどんなオブジェクトなのか分かりにくい。

僕なりの判断基準ではありますが、
もう少し具体的に書くと...

'''
複数の値が区切られているオブジェクト

値を区切って分けることができるオブジェクト

index[n]で要素を呼び出す事ができるオブジェクト
(辞書の場合はindex[key])
''''

こうですね。

複数の値が既に区切られている、
あるいは値を区切っても問題ないのなら
それらを個別に取り出す事だってできますよね。

っていう理屈です。

取り出す要素が何であっても、
[反復可能なオブジェクト]から
「1つ」要素を取り出す事ができたのなら、
for文以降の処理がちゃんと「1回」行われます。

プログラムは動いているが、何も起こらない。
宇宙空間でタンクが真空の水鉄砲を撃っているみたいだ。

>> [ iterable_object ] 一覧

死ぬ死ぬ

for i in array :

👆これを詳しく書くと

for ( 値を受け取る変数 ) in [ iterable_object ]  :

となります。

以下はその [ iterable_object ] 部分に
実際使う事ができるオブジェクトの型たちです。


● シーケンス型(sequence)

  • range型オブジェクト (※説明あり)

  • " str型 " オブジェクト

  • [ list型 ] オブジェクト

  • ( tuple,型 ) オブジェクト

○ 非シーケンス型(non-sequence)

  • { dict:型 } オブジェクト

  • { set型 } オブジェクト

※ 「シーケンス型」とは、順序を持つ型の事です。
dict型、set型と共に別記事で詳しく説明します。

「非シーケンス型(順序がない)」であっても
イテラブルなオブジェクトが存在するので
混同しないように注意してください。


>> not_iterable = イテラブルではない

True love is inseparable.
(真の愛は分つ事ができない。)

int / float / bool 型のオブジェクトは、
反復可能オブジェクトではありません。

これには少し気をつけたいですね。

何故なのかは、
考えればすぐ分かると思います。

「12345」を「1,2,3,4,5」のように
区切る事ができないから
です。

もし整数や浮動小数点数を区切れるのなら、

「 1+2+3+4+5 = 12345 」
「 1+2+3+.+4+5 = 123.45 」

👆これが成り立つ
という事になってしまいます。

あり得ません。
数学者に12345回殴られます。


一方 "str"型オブジェクトは、反復可能になっています。

「"12345"」と
「"1"+"2"+"3"+"4"+"5"」が同じだからですね。

"12345"」「str(12345)」のような
文字列(str型)オブジェクト
であれば、

値を区切って1つ1つ取り出す事ができる。

だから"str"型オブジェクト

[ iterable_object ] 
なのです。

str型オブジェクトでは
1つ1つ値を取り出す事ができている。

ちなみに、
str型オブジェクト特定の文字の取り出しは、
リストやタプルなどと一緒。

index[n]で呼び出す事ができます。


文字と配だもんね。

1個1個を繋げるからこそ列になるのだわ。

love = "アイシテル"

print( love[0] )  # >>> ア
print( love[1] )  # >>> イ
print( love[2] )  # >>> シ
print( love[3] )  # >>> テ
print( love[4] )  # >>> ル

index[n]で格納されている要素を
呼び出せるならば、
そのオブジェクトは [iterable_object] である

という判断基準は間違ってはいなさそうです。

※ しかし、これはあくまで「 十分条件 」です。
index[n]が使えないけれど、
[iterable_object]であるオブジェクトは存在します。

例えば、非シーケンス型であるdict型、set型は
順序が存在しないため、list_name[3] のような 
index[n] は通用しません。


range

range.

>> range = 範囲

〔 range ( レンジ ) 〕

「九九」の一覧表を作るには
「1〜9」の数字が必要ですよね。

a = [1,2,3,4,5,6,7,8,9]
b = [1,2,3,4,5,6,7,8,9]

このようにリストタプルを作って
for文の多重ループを使ってやれば、

作りたかった九九の一覧を
作ること自体はできましたよね。

九九の一覧作れたよ。

しかし...もしも、

「『100×100=10000』までの一覧表を作ってよ」

と言われたらどうしますか?

こんな感じでさ。


a = (1,2,3,4,5,6,7,8,9,10,.....,97,98,99,100)
b = (1,2,3,4,5,6,7,8,9,10,.....,97,98,99,100)

このように1〜100までの
リストやタプル
を手入力で作りますか?

普通作りません。
馬鹿馬鹿しいですよね。

バッカみたい。

そこで使うのが、「 range関数 」です。


>> range( start , stop, step )

range関数とは、

特定の範囲
連続した整数の列
特定の範囲一定の差分がある整数の列
得る時に使われる関数です。

カンタンに書くと、

a = [0,1,2,3,4,5,6,7,8,9,....,97,98,99,100]
b = [1900,1910,1920,...,2000,2010,2020]
c = [-10,-5,0,5,10,15,20,25,30,35]
d = [1,2,4,8,16,32,64,128,256,512,1024]
e = [0.99, 0.87, 0.75,...,0.39, 0.27, 0.15, 0.03]

👆こんな数列を、手入力しなくても
生み出してくれる大変便利な関数です。


「 range( 開始位置, 終了位置, 差分 ) 
この順番で値を入れて使います。

これからは

  • 開始位置 → start ( スタートポイント )

  • 終了位置 → stop ( ストップポイント )
    人により → end ( エンドポイント )

  • 差分   → step (※今回は説明しません)

このように表記したいと思います。


range関数の( )内に書けるのは

整数( int型 ) のオブジェクトだけです。

例えば、浮動小数点数( float型 ) などは
使う事ができません。

int型以外は使えません。


もし使いたい場合は、
int関数を使って変換してあげれば大丈夫です。

int関数で変換

実は「 bool型オブジェクト 」も
int型として判断できるんですよ.....。

だから比較演算子を用いた式も

range関数に入れる事ができるんですよね...。

range( True, 10 ) !!??!?
range( False, 10 )!!!?!?!

「a==2」「a==3」と書かれてますが、
これは「代入」ではなく「比較」です。

つまり、「a==2」の比較結果(真偽値)である
「True」か「False」が書かれたのと一緒の意味になります。

じゃあそれって
どういう意味なのと思いますよね。

int( True ) / int( False ) と書くとわかるんですが

int型 / float型に変換

True == 1
False == 0

このようになっている事がわかります。

「 True は 1 と等価 」
「 False は 0 と等価 」です。

「1」や「0」と同じものとして扱えるので

range( int( a==2 ) ) のように書かずに

range( a ==2 ) と書けます。

実際の現場で積極的に使われてるかは分かりませんが
覚えておきましょう。


「 range( 12 ) 」のように

「 start 」は省略することもできます。

その場合は、startが自動的に「 」になります。

range( 12 ) → range( 0, 12 

 stop 」だけは省略できないので
必ず設定しましょう!

では試しに「 print( range( 12 ) ) 」を
実行してみましょう...👇

ん?

整数列が表示されるのかと思いきや

 range(0, 12) 」と表示されました。

そうなんですよね。

range型オブジェクトは
リストやタプルではない
ので、

こういう結果が返ってくるのは仕方ありません。

range関数で知る事ができるのは、
あくまで「範囲」です。

しかし、そのままでは
どんなものか説明しにくいので、
オブジェクトの型を変換したいと思います。


>> list( ) = list関数 /
>> tuple( ) = tuple関数

list関数 / tuple関数とは、
( )の中身をリストやタプルに変換する関数です。

list( ) / tuple( ) の( )内に入れるのは、

[ iterable_object ] 
でなければなりません。

int( ) / float( ) / str( ) / bool( ) 
などの関数と一緒に説明しなかったのはそういう理由です。

[イテラブルなオブジェクト]が何なのか
知らずには扱えませんから。

[ iterable_object ] でないと
list関数 / tuple関数 は使えない!

👆これは結構間違えがち。注意です。

>> start / stop

how many cars?

では、list関数を使って

range型オブジェクト
リストに変換します。

 range( 0,10 ) 」でやってみましょう。

できまし..... え...?????

list( range( 0, 10 ) ) 

>>>  [ 0, 1, 2, 3, 4, 5, 6, 7, 8, ]

という結果が返ってきました。

エンドポイントの「10」どこに行った?
って思いません?

...君のような勘のいいガキは嫌いだよ

何でこんな仕様???

おかしくないか???

Python制作者頭悪くないか????

Python制作者は頭悪いィッ!!!!


Pythonの仕様に不満ができました。
頭の悪いヤツはPython開発者なんかやめてまえ!!!

それをきっかけに、
僕たちは開発者をブッ倒し、
Pythonの仕様に好きに変えられる権限を掌握した....

...としましょう。


とある整数列を用意してみました。👇

array = [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]

この整数列を range( ) で表す時に、
start, stopをどう書く仕様にしますか?

以下の4候補から選んで下さい。

※「 i 」はリストの各要素です。

  1. range( 1, 13 ) … 1 < i < 13 ( start超過 / stop未満 )

  2. range( 1, 12 ) … 1 < i ≦ 12 ( start超過 / stop以下 )

  3. range( 2, 13 ) … 2 ≦ i < 13 ( start以上 / stop未満 )

  4. range( 2, 12 ) … 2 ≦ i ≦ 12 ( start以上 / stop以下 )

頭の悪い人間が作った
Pythonでは「3」の仕様
ですが、

頭の良い僕は「4」の仕様が一番良いと思います。

13, 14, 15, 16, 17 ] <<< range( 1317 )

1234, 1235, 1236 ] <<< range( 12341236 )

ほら、start と stop 
そのまま最初最後の数字になってて
分かりやすいですもんね。

じゃあ、「4」の仕様にしましょう!

こんな分かりやすい仕様にしない
Python開発者は頭悪いですよね。

Python開発者に言ってやりましょうよ

「なんでお前が正しいんだ???」
「お前が正しいコンキョを言えェ!!!!!」

ってね。


あれ、ところで...
arrayって要素の数いくつでした??

array = [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]

えーと...

12 - 2 = 10」だから「10個」....

じゃない!!!?「11個」やんけ!!!

ハァ?!!!?! ウッッザ!!!!!


.......そうなんですよね。

range( 5 ) と書くと、
スタートポイントが自動的に「 」になり、
range( 0, 5 ) 
の扱いになりますよね?

そしたら、我々の選んだ仕様の場合、

0, 1, 2, 3, 4, 5 ]

👆こんな整数列が出来上がりますよね?

要素数を数えてみてください。
「 6個 」なんです。

本当だからだ!!!!!!

...じゃない

本当だ!!!!!!!

つまり、「 range( n ) 」と書いた時に

「 n個 」の要素が出てくると思いきや
 n+1個 の要素が出てきてしまうのです。


これは面倒ですよ。

処理を 10回繰り返す 命令をする時に

for i in range( 10 ):
     # 処理内容

👆普通はこう書きます。

ですが我々が仕様を勝手に決めたせいで、

for i in range( 10 - 1 ):
     # 処理内容

👆こう書かないと
いけなくなってしまいました。

失 脚 不 可 避 ★


まあ...どの仕様でも、
我々が今まで自然数「1」を使って
1つ(のオブジェクト)」「1回(の処理)」
と数えてきたものを

CPUと同じようにバイナリ「0」を使って
1つ(のオブジェクト)」「1回(の処理)」を数えない限り、
この問題は解消されませんけどね。

start以上 / stop未満
正しい姿はこうです。

start以上 / stop未満 

この仕様になったのは
色々な理由が考えられます。

例えば、Pythonでは

list( range( 0, 0 ) ) >>> [ ]

👆この記述で [空リスト] が作れます。

[空リスト] とは「要素が0個」の状態ですよね。

start以上 / stop以下 という「4」の仕様
[空リスト] を作るためには、

list( range( 0-1 ) ) >>> [ ]

👆 このように「-1」を書く必要が出てくる。
なんか分かりにくいしカッコ悪い。

というのがネット界隈(?)で
よく言われる理由の一つです。

Python開発者は頭悪くなかった。

俺が悪かったよ...


>> 僕の解釈

THE END.

※ 読み飛ばして結構です。

range関数は「 start以上 / stop未満 」
ということに関して。

間違えやすいのでイメージで補足をしたいと思います。

例えば、「range(0,10)」だと
「end_point」は「10」になりますよね。

「end」とは「終わり」なわけですが、
「死」みたいな意味もあります。
「1〜9」が道路で「10」から先は崖(奈落)と捉えれば
使える道路は「1〜9」ということになりますよね。

「stop」の場合でも、
「10から先に規制線が張られている」
「10より先に行ってはいけない」と解釈すれば
行けるのは「1〜9の範囲」ということになります。

「end」から先は「終わりの始まり」。
「stop」から先は「立入禁止区域」。

「『end_point』に入ったら死んだも同然」
「『stop_point』に入ろうとしたら手前で止められる」
と思っていれば間違えにくいのではないでしょうか。


Program : range型オブジェクトを使用したfor文の多重ループで九九の表を作る

先ほど、range型オブジェクト

list関数 / tuple関数に変換しました
よね。

range(12)

list関数 / tuple関数に変換できるのは
[ iterable_object ]だけってことは、

list関数で変換できたrange型オブジェクトは
[ iterable_object ]である

ということになりますね。

つまり、

for i in range( n ):

という記述をする事ができる
ということです。

演繹法だ 演繹法。

では最後に「 for i in range(n): 」を使って
九九一覧を作ってみましょう。

for i in range( ): の多重ループ

「無駄だ」と思うレベルの長い説明になりましたが、

愚直に「for i in range( ):」や
「list( ) / tuple( )」だけ覚えるのではなく、

どんな型が使えるのか
[ iterable_object ] とはなんぞや

という事をしっかり抑えた上で使った方が
圧倒的に応用が利きますし、
エラーが起きてもすぐ原因が分かって対処できます。

後になって学び直すくらいなら
最初からしっかり理解している方がいいですよね。
ってことです。

次回もfor文に関する記事になります。

次の記事へ。

この記事が気に入ったらサポートをしてみませんか?