「新・明解Pythonで学ぶアルゴリズムとデータ構造」で勉強日記#4

[前回の続きです](https://qiita.com/kato_squalldeka/items/30204050ab7507e0671e)
【出典】[「新・明解Pythonで学ぶアルゴリズムとデータ構造」](https://www.amazon.co.jp/%E6%96%B0%E3%83%BB%E6%98%8E%E8%A7%A3Python%E3%81%A7%E5%AD%A6%E3%81%B6%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%81%A8%E3%83%87%E3%83%BC%E3%82%BF%E6%A7%8B%E9%80%A0-%E6%96%B0%E3%83%BB%E6%98%8E%E8%A7%A3%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E6%9F%B4%E7%94%B0%E6%9C%9B%E6%B4%8B/dp/4815603197/ref=sr_1_2?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1BG3JS7CKMZA0&keywords=Python%E3%81%A7%E5%AD%A6%E3%81%B6%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%81%A8%E3%83%87%E3%83%BC%E3%82%BF%E6%A7%8B%E9%80%A0&qid=1644336329&sprefix=python%E3%81%A7%E5%AD%A6%E3%81%B6%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%81%A8%E3%83%87%E3%83%BC%E3%82%BF%E6%A7%8B%E9%80%A0%2Caps%2C212&sr=8-2)

今回で第1章にあたる部分が終了です。


#繰返しのスキップと複数のrangeの走査

走査の意味を調べてみましたが、よくわかりませんでした。ので、参考程度に載せておきます。
そう‐さ【走査】
［名］(スル)テレビカメラなどで画像を多くの点や線状に分解し、それぞれの輝度・色相・色度などを順次に電気信号に変換すること。また逆に、受像機で電気信号を画像に組み立てること。
【出典】[デジタル大辞泉](https://www.weblio.jp/content/%E8%B5%B0%E6%9F%BB)
だそうです。
繰り返しのスキップというとなんだかどう使うんだろうかという気はしますが、いわゆるcontinueを使った文みたいですね。ここでは8をスキップして表示する処理をしています。

```python:list1-19
#1から12までを8だけスキップして繰り返す（その１）

for i in range(1, 13):
    if i ==8:
        continue
    print(i, end=' ')
print()
```
ここで、もし8という決まった数字がわかっている場合はlist1-20のように書き換えも可能です。

しかし、入力する数字が定まっていない場合はifとcontinueを使う必要があるみたいですね。


```python:list1-20

#1から12までを8だけスキップして繰り返す（その2）

for i in list(range(1, 8)) + list(range(9, 13)):
    print(i, end=' ')
print()
```

リストを生成してそれぞれを結合して表示させる処理ですね。
list関数にrange関数を入れてるのを見て、こういうのさらっと書けるようになりたいななんて思いました。関数の使い方はExcelとかをイメージして使っていけばできるようになりますかね…？
最初のうちは整理する意味で一つずつ変数を定義して入れ子のように組んでもいいのかもしれないですね。（どちらが整理しやすいかはわからない）


|**コラム1-12 値比較演算子の連続適用とド・モルガンの法則**|
|-|

数学Aでやる集合についての話ですね。ここで高校数学が活きてくるというのはなかなか熱いと思います。
さて、List1C-2に示すのは、「2桁の整数値」を読み込むプログラムです。

```python:List1C-2
# 2桁の正の整数値(10～99)を読み込む

print('2桁の整数値を入力してください。')

while True:
    no = int(input('値は：'))
    if no >= 10 and no <= 99: #変更する文
        break
print(f'読み込んだのは{no}です。')
```
このプログラムでは、andを使っていますね。and や or はそれなりにPC触ってると常識ではあるけど、どのタイミングで使うのが有効なのかいまいちわかっていない…。
でもド・モルガンの法則といわれるとベン図書けばいいのかなあって感じですかね。

**ド・モルガンの法則を適用**
a and b = not(not a or not b)
a or b = not(not a and not b)
二重否定ってやつですね。

```python:List1C-2ド・モルガンの法則を適用
# 2桁の正の整数値(10～99)を読み込む

print('2桁の整数値を入力してください。')

while True:
    no = int(input('値は：'))
    if not( no < 10 or no > 99): #変更した文
        break
print(f'読み込んだのは{no}です。')
```
なんかnoが頻出するので見づらい気はしますが、not関数を用いることで割とすっきりしたイメージですね。
使い分けは自由ですが、記号なんかはタイピングするのなかなか面倒な気もするので私はnot関数使っている方が好きです。

**構造化プログラミング（整構造プログラミング）**
単一の入口点と単一の出口点とをもつ構成要素だけを用いて、階層的に配置してプログラミングを構成する手法のことで、順次、選択、繰返しの３種類の制御の流れを利用する。

#多重ループ
いよいよプログラミング感出てきましたね。名前だけでもプログラム感出てます。
list1-21では、2重ループ？をしています。for文の中にfor文を入れます。
ちなみに多重ループは2重ループや3重ループなどがあるのでそれらの総称を指します。（たぶん）

```python:list1-21
#九九の表を表示

print('-' * 27)
for i in range(1, 10): #行ループ
    for j in range(1,10): #列ループ
        print(f'{i * j:3}', end='')
    print()
print('-' * 27)
```
for文は何度やってもイメージがしづらい気がします。そこがまだ初心者の感覚なんだろうなと…。

list1-21ではつぎの順で処理されています
※()内は(始値,増値,終値)の順です。
①行ループ（i:1, 1, 9）
②列ループ（j:1, 1, 9）
③i * jを3桁で表示
④列ループ
⑤改行
⑥行ループ
つまりjの列をiの回数行表示させるということですね。
iが1の時、jは1から9までをとり、処理が終わったらiに2を入れて、jを1から9まで入れ込んで・・・の繰り返しをする形ですね。
カレンダーとか作れそうな気がします。（カレンダーモジュールあるけど・・・）


|**コラム1-13 random.randint**|
|-|
 乱数を生成するrandom.randint(a, b)はa以上b以下の乱数を生成してa以上b以下の整数値の中から無作為に1個の整数値抽出してその値を返却する。
さらっと書かれていたのでよくわからないですが、注意書き程度に書いておきます。

#直角三角形の表示
二重ループを応用すると図形の表示が可能になります。
ここではアスタリスクを羅列して描画します。

```python:list1-22
#左下側が直角の二等辺三角形を表示

print('左下直角の二等辺三角形')
n = int(input('短辺の長さ：'))

for i in range(n):
    for j in range(i + 1):
        print('*', end = '')
    print()
```
ちょっと整理すると、n=5としたときrange(5)なので、0,1,2,3,4をiに入れ込んでいく…。
i, j =(0, 1),(1, 2),(2, 3),(3, 4),(4, 5)ということですね？
(range関数がn-1の数字をとることは分かるんですが、組み合わさっていくと？が浮かんでくるようなこないような…。)

そして応用として以下

```python:list1-23
#右下側が直角の二等辺三角形を表示

print('右下直角の二等辺三角形')
n = int(input('短辺の長さ：'))

for i in range(n):
#変数名を_にすることで、ループ本体の中でカウンタ用変数の値を使わないことを伝える。
    for _ in range(n - i - 1):　#1
        print(' ', end = '')
    for _ in range(i + 1): #2
        print('*', end = '')
    print()
```
list1-22は左揃えでしたが、ここでは右揃えをしています。
ここではforが3つ出てきますね。インデントに注目すると、同じところで揃えられているので、#1,#2の順番で並列に処理されます。（これも一応2重ループという扱いでいいのかな？）
仮にforの中にforを入れて、さらにforを入れた場合、*がprintされ終わってから空白がprintされるという流れになるので三角形ではなくなりますよね。（#1に#2を内包させた場合。）
インデントの大事さと、for文の仕組みがうっすらわかるような文だなと思いました。

ちなみに、1-22のjも_に変更可能です。

|**コラム1-14 変数の補足**|
|-|
「pythonでは、データ、関数、クラス、モジュール、パッケージなどあらゆるものがオブジェクト」だそうで、
「オブジェクトは型を持つとともに記憶域を占有している」らしいです。
「pythonの変数は値を保持しないため、変数とは値を格納する箱のようなものであるという例えが当てはまらない」とのことで、ちょこちょこ解説で使われるあれは、不適切だったんだなと…。こういう情報は結構大事ですよね。
「変数は、オブジェクトを参照する、すなわち、オブジェクトに結びつけられた名前に過ぎない」（タグ付けくらいの感覚らしい）
「すべてのオブジェクトは、記憶域を占有してい型を持つだけでなく、他のオブジェクトと識別できるようにするための識別番号すなわち同一性もっている」（つまりIDみたいな割り振りがされてるってことかい？興味深い話デスネ）

```python:ID変数のイメージ
 n = 17
id(n)

id(17)

#17というint型オブジェクトにｎという名前を与えるイメージ。
```
それぞれ、同じIDが出力されます。つまり17という数字にもIDというものが割り振られていて、n = 17と書くと17のIDに対してnを紐づけするといったイメージでしょうか？

```python:list1C-3
#関数内外で定義された変数とオブジェクト
#オブジェクトと変数の識別番号を表示

n = 1 #広域変数（関数内外で利用できる）

def put_id():
    x = 1 #局所変数（関数内のみで利用できる）
    print(f'id(x) = {id(x)}')
    
print(f'id(1) = {id(1)}')
print(f'id(n) = {id(n)}')
put_id()
```

記憶域期間という概念がpythonにはない。とのことで…処理が終わった後も使うことができるらしいです。逆に言えば同じ名前を使うときは消す処理とか再定義みたいな処理がいるわけですね。



```python:list1C-4
#1から100まで繰返して表示

for i in range(1, 101):
    print(f'i = {i:3} id(i) = {id(i)}')
```
上記を実行すると1から100までのIDを表示します。
具体的に何に使えばいいのかはわかっていませんが、参照するときに、同じIDを使っているんだぞーという確認ができるという話でいいですかね？


さて、以上で「第1章　基本的なアルゴリズム」が終了しました。

次は「第2章　データ構造と配列」について学びます。

ありがとうございました。