40のおっさんのPython学習記録

20年以上前、学部の必修のC言語が全く理解できずに同級生に放り投げ、その後コーディングから遠ざかったガチ文系のおっさんが、ふと思い立ってPythonに挑戦しています。

小休止

おっさんです。

おっさんは今月末で今の会社を辞めます。で今月後半は有給取得で長い旅に出ます。プラス今週末は糞田舎に帰ったり、前半も引継ぎで何気に忙しかったり、飲みの誘いが多かったりするので、もくもく行ったり自習したりとか無理っすね。

ということで今月はプログラミングの勉強は一切放棄するので、このブログの今月の記事はこれだけになります。

フィボナッチ数列と、関数の再帰的定義とグローバル変数

おっさんです。

今読んでるMITの教科書は基本書なので新しく学ぶことはそれほど多くはないだろうから練習問題をとこうと思っていたのですが、関数の再帰的定義はこれまで読んだ3冊の入門書にはなかったアイデアでした。例えば下のフィボナッチ数列を計算するコードでfib(n)=fib(n-1)+fib(n-2)と再起定義するだけでいいってのは、へー、こんなので動くんだ、と素直に驚き。ただ、ぐぐると、フィボナッチ数列再帰で計算するのは非常に効率が悪いので、他のアルゴリズムを使ったほうがいいみたい。

def fib(n):
    """Assumes n int >= 0
    Returns Fibonacci of n"""
    if n == 0 or n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)

def testFib(n):
    for i in range(n+1):
        print('fib of', i, '=', fib(i))

それとスコープとローカル、グローバル変数の話で、関数の内側のスコープの変数でもglobalを置くことでグローバル変数になり、他の関数の内側からでも参照できるってのは新たな発見でした。

def fib(x):
    """Assumes x an int >= 0
       Returns Fibonacci of x"""
    global numFibCalls
    numFibCalls += 1
    if x == 0 or x == 1:
        return 1
    else:
        return fib(x-1) + fib(x-2)

def testFib(n):
    for i in range(n+1):
        global numFibCalls
        numFibCalls = 0
        print('fib of', i, '=', fib(i))
        print('fib called', numFibCalls, 'times.')

結局

おっさんです。もくもく会に来ています。

先日の負数の平方根を求めさせる問題、後のページの別の例題にて、負数の場合はそんなものは存在しないと条件分岐させるコードが出てきました…そらそうだよな…Python複素数処理を習ってない前提で何だこの問題はと思っていたのですが、やっぱそうだよね…なんでこんな問題を出すんだよ…

とりあえず前に進みます…

どないせいっちゅーねん

おっさんです。ゴールデンウィークだけどもくもく会に来ています。明後日のもくもく会にもいきます。参加する方よろしくお願いします。

下のコードは2分法と言い、ある数の平方根の近似値を求めるのに、大きい数字から初めて、ループのたびに探索すべき範囲を半分に削減しながら進む、というもので、それ自体は特に難しくもなく、ほー、そうけ、以上の感想もないのだけど、問題はこのコードに付随した指練習で、xを負数にした場合はどうなるか、負数の平方根を求めるようにするにはどうすればよいかというもの。ヒントでlowを変化させようとか書いてあるけど、下のコードは複素数に対応させるためには1から書き直す必要があるじゃん…どうしたらいいのよ…ていうか問題設定間違ってるんじゃないの?複素数平方根のlowってなんじゃそれ?としか思えんのだけど。誰か答えわかります?答えのないテキストを解いていくことの限界を既に感じているおっさんです。

x = 25
epsilon = 0.01
numGuess = 0
low = 0.01
high = max(1.0,x)
ans = (high + low)/2

while abs(ans**2-x)>=epsilon:
    print('low =', low, 'high=', high, 'ans=', ans)
    numGuess +=1
    if ans**2<x:
        low = ans
    else:
        high = ans
    ans = (high + low)/2
print('numGuess=', numGuess)
print(ans, 'is close to square root of', x)

10個整数を入力し最大の奇数を判定する問題、ただしパッケージ使用不可

おっさんです。MITのテキストは如何にも理系の教科書っぽくて、これまで読んできた入門書とは毛色が全く違って正直苦戦しています。

この数週間、おっさんは以下の問題にずっと苦しんでいました。10個整数値を入力しその中で最大の奇数を判定すると言う問題なのですが、条件があって、if、else、while、for文のみが使用可能です。この条件さえ外れてしまえば他のパッケージや関数を使えばおっさんでも短いコードはかけます。こんな感じです。

# MITテキストp.24指練習
# 10の整数の中で最大の奇数を判定する
import numpy as np
from numpy.random import *
numlist = list(randint(0,50,10))
print(f"入力された数列は:{numlist}")

revlist = np.array(sorted(numlist, reverse=True))

try:
    print(f"最大の奇数は{revlist[revlist%2==1].max()}でした。")
except:
    print("奇数が入力されてへんのちゃうんけボケ南港に沈めるぞコラ")

でも、こういう基本に立ち返った問題って非常に難しく、これよりも遥かに長ったらしいダラダラとしたコードを書いてムダなループ、無限ループを発生させて苦しんでいました。先週のもくもく会でmaxintを置くべしとヒントを貰って1歩進んで、ようやく今日マトモな短いコードとして完成しました。やっぱりおっさんはアホでセンスが無いです。

# MITテキストp.24指練習
# 10の整数の中で最大の奇数を判定する
import numpy as np
from numpy.random import *
numlist = list(randint(0,50,10))
print(f"入力された数列は:{numlist}")

a = 0
maxint=0
while a<10:
    if numlist[a]%2 == 0:
        a+=1
    else:
        if maxint > numlist[a]:
            a+=1
        else:
            maxint = numlist[a]
            a+=1

if maxint == 0:
    print("奇数が入力されていませんでした。")
else:
    print(f"最大の奇数は{maxint}でした。")

おっさん、自信を無くすの巻。

おっさんです。平日夜のもくもくに参加しています。ここはメンター的な方がいらしてて、素人の質問にも答えてくれる型式の場です。

入門書の2冊めを一応終わった感じもするので、3冊目は以前にも紹介したこれを進めていこうと思います。これは練習問題もあるので、練習問題を解いていく感じにしようかと思っています。

で今日は2章をやっているのですが、1問目の指練習問題のx、y、zの中で最大の整数を求める問題なんですが3時間目いっぱいかかってしまいました…やっぱおっさんセンスないんちゃうんかなあ…

# MITテキストp.18指練習

import numpy as np
x=int(input("正の整数xを入力しろやボケカス;"))
y=int(input("正の整数yを入力しろやボケカス:"))
z=int(input("正の整数zを入力しろやボケカス:"))

numlist = [x,y,z]
revlist = np.array(sorted(numlist, reverse=True))
print(revlist)

try:
    print(revlist[revlist%2==1].max())
except:
    print("奇数が入力されてへんのちゃうんけボケ南港に沈めるぞコラ")

新たな気づき(Chapter 13)

おっさんです。ヒノキの花粉症にやられており思考能力の低下が著しいです。おっさん、出張用のスマホAT&T iPhone 5をアンロックしたものを使い続けてきたんだけど、もう6年前の機種ということもあり、各種の動作が異様に遅くもう限界なので新しいスマホ買ったんです。BlackBerryのKeyONEというモデルのデュアルSIM機ですわ。日本で買うより安かったのでアメリカのAmazonで買うたわけですわ。

これが悲しいことに、SIMを指してもセルラーネットワークを安定して掴んでくれず、大半の時間は電波がありません状態なんですわ…アメリカのAmazonで買うてしもたせいで、返品がめんどくさそうでおっさん途方にくれています。どないしたらええねん…

ということで今日はファイル操作について学習しました。

  • open()は絶対/相対パスだけでなく、パスを読み込んだオブジェクトでも指定可能。
  • ファイルを開いたら、その開いた中身を使って処理をする前に.close()で閉じるのが鉄則。
  • 一方、with open() as構文を使うと、close()処理は必要ない。
file = "./fox.txt"
with open(file) as fileobj:
    text = fileobj.read()
    print(text)
  • コマンドライン引数を利用したい場合は、sysモジュールのargv属性の利用が必要なのでimport sysが必須。
  • コマンドライン引数とは、python code.py hoge hageの、hoge hageのみならず、code.pyも含む。
  • sys.argvにコマンドライン引数が渡される。上記の通りコードファイルが第1引数なのでargv[0]。hogeを利用したければargv[1]、hageを利用したければargv[2]で指定。
import sys
if len(sys.argv)<2: #1個目の引数はコードファイルなので、2個目にあるはずのパス名がない場合の処理
    print("読み込むファイル名を指定して下さい。")
    sys.exit()

file = sys.argv[1]  #[0]はコードファイルなので、[1]でパス名を取る
with open(file) as fileobj:
    text = fileobj.read()
    print(text)
  • read(x)とすると、x文字読む。もう一度同じオブジェクトにread(x)するとその次のx文字を読み込む。
file = "./fox.txt"
with open(file) as fileobj:
    while True:
        text = fileobj.read(10)  #10文字ずつ読むことを繰り返す
        if text:                 #文字列があれば出力
            print(text)
        else:                    #文字列がなくなったら終了
            break
  • readline()は1行ずつ読み込むことのできるメソッド。1行終わると次の行に進む。
file = "./rihaku_kanshi.txt"
with open(file) as fileobj:
    while True:
        line = fileobj.readline() #1行ずつ読み込む
        aline = line.rstrip()     #読み込んだ行の最後にある改行を削除
        if aline:
            print(aline)
        else:
            break
  • 上記を李白漢詩を読み込んだら以下の通りになる。改行コードを削除しないと行間に更にもう1行含まれる間延びした出力になるので要注意。
花間一壼酒  花間一壺の酒
獨酌無相親  独り酌んで相親しむもの無し
舉杯邀明月  杯を挙げて明月を迎え
對影成三人  影に対して三人と成る
月既不解飮  月既に飲を解せず
影徒隨我身  影徒らに我が身に随う
暫伴月將影  暫く月と影とを伴い
行樂須及春  行楽須らく春に及ぶべし
我歌月徘徊  我歌えば月徘徊し
我舞影零亂  我舞えば影零乱す
醒時同交歡  醒むる時は同に交歡し
醉後各分散  酔うて後は各分散す
永結無情遊  永く無情の遊を結び
相期遥雲漢  相期す遥かなる雲漢に
  • open()で読み込んだオブジェクトそのものが行単位で要素を取り出せるイテレータなので、next(fileobj)で1行ずつ取り出せる。以下のコードで同じことができる。
file = "./rihaku_kanshi.txt"
with open(file) as fileobj:
    while True:
        try:
            line = next(fileobj)
            aline = line.rstrip()
            print(aline)
        except StopIteration:
            break
  • for-in enumerateを使うと次のように書ける
file = "./rihaku_kanshi.txt"

with open(file) as fileobj:
    for i, line in enumerate(fileobj,1): #iの開始番号を0でなく1にするために,1を付加
        if line == "\n":
            continue
        aline = line.rstrip()
        print(f"{i}:{aline}")

出力結果は以下のような感じ。

1:花間一壼酒  花間一壺の酒
2:獨酌無相親  独り酌んで相親しむもの無し
3:舉杯邀明月  杯を挙げて明月を迎え
4:對影成三人  影に対して三人と成る
5:月既不解飮  月既に飲を解せず
6:影徒隨我身  影徒らに我が身に随う
7:暫伴月將影  暫く月と影とを伴い
8:行樂須及春  行楽須らく春に及ぶべし
9:我歌月徘徊  我歌えば月徘徊し
10:我舞影零亂  我舞えば影零乱す
11:醒時同交歡  醒むる時は同に交歡し
12:醉後各分散  酔うて後は各分散す
13:永結無情遊  永く無情の遊を結び
14:相期遥雲漢  相期す遥かなる雲漢に
  • ファイルに書き込む場合はopen()メソッドに"w"あるいは"a"のモードオプションをつける。"w"は既存の内容を消して上書き、"a"は追記。
  • with-asを使えばclose()しなくていいのは書き込みも同じ。
  • 以下の事例はpython code.py 文字列を実行すると、実行時間、書き込み文字列を区切り線とともにどんどんファイルに追記していく。
import sys
from datetime import datetime
file = "./log.txt"

#文字列の引数がない場合は処理を中断
if len(sys.argv)<2:
    sys.exit()

now  = str(datetime.now())
memo = sys.argv[1] #文字列の引数
line = "-"*10 #区切り線
with open(file, "a") as fileobj:
    fileobj.write(now+"\n")  #書き込み時間を入れる
    fileobj.write(memo+"\n") #引数を入れる
    fileobj.write(line+"\n") #区切り線を入れる
  • 書き込みの場合のダイアログを呼び出す場合はtkinterのasksaveasfilenameメソッドを呼び出す。
  • ファイルはなければ作成されるが、フォルダの場合はない場合は作成されずエラーになる。なので事前にフォルダの有無を確認する必要が出て来る。os.path.exists(パス)でファイル、フォルダが存在するかどうかが確認できる。Trueで存在、Falseで非存在。逆にファイルはある場合は上書きされることにもなりかねないので、os.path.exists(パス/ファイル名)でファイルの有無を調べ、注意をうながすこともありえる。
import os
from random import randint

#保存フォルダとパス
folder = "./data/"
file = folder+"sample.txt"

#ファイルを保存する
def filewrite():
    if not os.path.exists(folder): #フォルダの存在を確認
        os.makedirs(folder)        #なければフォルダを作成
    with open(file, "w") as fileobj: #ファイルはあれば既存のものが開き、なければ新規に作成される
        num = randint(0,100)
        fileobj.write(f"{num}が出ました。")
        print("ファイルを保存しました。")

#既存のファイルの有無をチェック。あれば注意をうながすメッセージを表示。
if os.path.exists(file):
    while True:
        answer = input("上書きしてもよいですか? (y/n)")
        if answer == "y":
            filewrite()
            break
        elif answer == "n":
            break
else:
    filewrite()