遙かなるマチョジニア

マッチョXエンジニアを目指すブログ

家族持ちと自己研鑽についての日記

家族がいるということは家族との時間を過ごす必要がある。 それは当たり前だが、そうなると必然的に自分だけの時間は減る。

家族を持つことで自分の時間が増えるという人はまずいないだろう。

だから、自分の時間を大事にしたい人は、「結婚をしない」か、「結婚するまでに使い果たす」かだと思う。 特に自己研鑽の場合、多くの人の目的は市場価値の向上だろうけど、それが仕事内容と同じベクトルであれば 自己研鑽に割く時間は少なくても良いと思う。仕事は毎日7.5時間以上あるので、 それが1ヶ月だと約150時間、年間約2000時間ほどである。

もしも仕事が自己研鑽へ直結していなかった場合、これはきつい。 一歩踏み出す勇気があるならば即転職したほうが良い、それは家族を持たないうちにしたほうが良い。 転職する勇気が無いとしても、やはり家族を持たないうちにプライベートの時間を割いて自己研鑽すべきだろう。

しかし問題なのは、このことに、家族を持った後で気づく人が多いということだ(しらんけど、自分はそうだから)。 「子供の頃にもっと勉強していたほうがよかったなあ」と思う大人は多い。それと同じだ。

必要と感じたときには、すでに手遅れなパターンが多い。

  • 家族を持っている
  • 仕事内容が市場価値向上と一致しない
  • 筋トレが趣味
  • 睡眠は7時間以上取らないと死ぬ

という自分は、自己研鑽する時間が足りない。これは自分が招いた結果だから自分にしか責任が無い。 睡眠以外はすべて自分の選択による結果なのだ。

それにも懲りず、今はフォートナイトにハマってしまった。 これから先、自己研鑽に当てる時間はほとんど無くなる。

そんな暇があったらフォートナイトのスキルを磨いたほうがいいと感じているからだ。

筋トレも週5だったのを週4に変えて、プログラムも見直している。 具体的には今古賀翔さんのプログラムを採用している。

note.com

しかし昨日今日の大雨で、筋トレする時間もまた減ってしまっている(理由は割愛)。 コロナにより1ヶ月自粛筋トレを自粛していたのもあって、以前より筋トレに対する熱が冷めたのだ。 これは間違いない。というか、週5も週4も変わらないんじゃないか、という考えになった。 1回のトレーニングを充実させれば、むりそ週4のほうが効果があるのでは?と。 そして週4に減らして1回のトレーニングを妥協し始めている。

すべてが惰性へと傾いているのだ。

仕事なんてしたくない。
自分の時間がほしい。
フォートナイトうまくなりたい。
Netfixでイコライザー2が観たい。
FPS Freek Vortex買った。
機械学習もっと勉強したい。
でもフォートナイトがうまくなりたい。
イムリング買った。
背面パッドほしい。

最近はもっぱらこんな感じで葛藤しているのである。

【LeetCode】Climbing Stairs 解法【Python】

Climbing Stairs

leetcode.com

動的計画法のジャンルとして紹介されていたので解いてみたが、 なんとなく動的計画法ぽいなあと思いつつ具体的な解放が思いつかず。 ツリー構造で表現してみる。

f:id:shuheilocale:20200624080249p:plain

これはn=3のとき。ノードの部分の残りの数が同じ場合は、その子は同じ構造になる。 最終的にエッジの数を数えればいい。 で、n=4のときは?最初の分岐で、1と2に分かれて、1の方は残り3になるので、n=3の構造と一緒。 そして2の方は残り2になるのでn=2と一緒。 これ、フィボナッチで表せそう。ということで解法。

コード

class Solution:
    def climbStairs(self, n: int) -> int:
        # 合計nになる1,2の組み合わせ
        # 0 = 1
        # 1 = 1
        # 2 = 2
        # 3 = 3
        # 4 = 5
        # 5 = 8
        # ※便宜上0 = 1とする
        ret = [1]*(n+1)
        for i in range(1,n):
            ret[i+1] = ret[i] + ret[i-1]

        return ret[n]    

f:id:shuheilocale:20200624080635p:plain

実際にsolutionでフィボナッチの解き方が紹介されており、 自分の解法のような愚直なコードではなく、公式一発だった。

leetcode.com

また、動的計画法についてはこちら。

leetcode.com

i番目に到達できる経路を順次計算していく、という。自分のコードは結果的に動的計画法のやり方になっていた。

【LeetCode】Reverse a Linked List 解法【Python】

Reverse a Linked List

leetcode.com

独自のリンクリストクラスを逆順にする問題。こういうのは現実問題として結構多いと思う。 解放としては、愚直にやるしかないって感じ。再帰使ったやりかたもあるが、可読性が下がるのであまり好きじゃない。

leetcodeの記述を前提としているのでヘルパークラスみたいな実装になっているが、 実際はリンクリストクラス自身にreverseをつけてもいいと思うんだが、どうなんだろ。

コード

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        
        if head is None:
            return None
 
        next_ = head.next
        pre_node = ListNode(val= head.val, next=None)
        if next_ is None:
            return pre_node
        
        while next_ is not None:

            val = next_.val
            nxt = pre_node
            rev_node = ListNode(val=val, next=nxt)
            
            pre_node = rev_node
            next_ = next_.next
            

        return rev_node

ちょっとコードが汚い。 headがNoneのときはいいとして、 headだけで終わっている場合(連結がない場合)のifは不要だと思うのできれいにできそう。

f:id:shuheilocale:20200612080951p:plain

solutionに書いてあったきれいなコード↓

class Solution:
# @param {ListNode} head
# @return {ListNode}
def reverseList(self, head):
    prev = None
    while head:
        curr = head
        head = head.next
        curr.next = prev
        prev = curr
    return prev

【LeetCode】Sum of Two Integers 解法【Python】

Sum of Two Integers

leetcode.com

加算と減算を計算する関数を作る、ただし+と-を使わずに。という問題。 これは流石にわからなかった。バイナリ演算というのは想像できるが、 実際のアルゴリズムを考える余力もなく、答えをみた。

コード

class Solution:
    def getSum(self, a: int, b: int) -> int:
        MAX_INT = 0x7FFFFFFF
        MIN_INT = 0x80000000
        MASK = 0x100000000
        
        while b:
            carry = (a & b)
            a = (a ^ b) % MASK
            b = (carry << 1) % MASK
            
        if(a <= MAX_INT):
            return a
        else:
            return ~((a % MIN_INT) ^ MAX_INT) 

しかし、普通に+と-使って計算したときと処理速度変わらないのだが、 これを使う場面があるのだろうか。 f:id:shuheilocale:20200612073349p:plain

【書評】OpenSSH[実践]入門:テレワーク時の仕事ハックのために

勤めている会社ネット環境がクソすぎてテレワークが許可されても全然仕事ができない状態だった。 sshのポートを許可してくれと懇願してそれだけはOKをもらった。 (それにしても、あれだけ敏感になっておいて22番ポートを開けるリテラシーのなさよ。)

んでsshが使える環境になったので、その範疇でやれることをやりたいと思って購入したのがsshの本である。

OpenSSH[実践]入門 (Software Design plus)

OpenSSH[実践]入門 (Software Design plus)

  • 作者:川本 安武
  • 発売日: 2014/11/01
  • メディア: 単行本(ソフトカバー)

sshだけでこの分厚さは想像以上ではあったものの、sshに関する本はあまり出版されていなかったので選択肢はほぼなかった。 この本は評価もそこそこ高いし、「実践」「入門」というワードに釣られてしまった。

ネットである程度調べて実践していたが、それでも知らない点がたくさんあった。 そもそも、なんのために使うんだ?というような章もある。これは、結構な人数を管理するための機能なんだろうけど、自分の場合は数人レベルなので、今は関係ないとおもって斜め読みした。 単純に、sshでリモートログインして開発してますよ、という人は一度読んでおいて欲しい。 まじか、こういう使い方があるのか、など新たな発見は間違いなくある。 個人的にはGoogle Authenticatorをサポートしていることにびっくりした。

個人的付箋

ポートフォワードの追加と削除

エスケープキャラクタ(デフォルトはチルダ~)のあとにCを入力するとコマンドラインが開き、プロンプトがsshに変わる。 例えば、ヘルプを表示する。

$ ~C ←実際には~Cは表示されない
ssh> help
Commands:
    -L[bind_address:]port:host:hostport request local forward
    -R ・・・
 ・・・

インターネットに接続できない環境のホストをアップデートする

ポートフォワードとtsockを組合せえれば、ネットワークから切り離されたOSをアップデートさせる経路を作れる。まず、リモートポートフォワードを有効にして接続し、隔離されたホストがクライアントのsshdに接続できるようにする。 10022はクライアントのsshdに繋がるポート。

[client]$ ssh -R 10022:localshot:22 remotehost

つぎにダイナミックポートフォワードを有効にし、クライアントをSOCKSのプロキシにする。

[remotehost]$ ssh -D 1080 -p 10022 localhost

tsocksはプロキシエンドで名前解決ができる。remotehostはクライアントをプロキシにしてインターネットを接続することが可能になる。

[remotehost]$ tsocks yum update

Match(条件分岐)

OpenSSH-6.5から、SSHクライアントのコンフィグファイル内でキーワードによる条件分岐ができるようになった。

Matchで使用できるキーワード

execで展開されるトーク

  • %L
    • ローカルマシンのホスト名の最初のドットまで
  • %l
    • ローカルマシンのホスト名
  • %h
    • 接続するリモートマシンのホスト名
  • %n
  • %p
  • %r
  • %u
    • ローカルマシンでsshを実行したユーザ名

設定例

Host web01
  HostName 192.168.100.11

Host proxy-server
  ProxyCommand none

Match exec "nmcli connection status id <ap-name> 2> /dev/null"
  proxyCommand ssh -W %h:%p proxy-server

%hを使用した設定例

Host web01 web02 mail01 mail02 db01 db02
  HostName %h.localhost.localdmain

ローカルポートフォワードを複数設定する

複数行で記述可能

LocalForward localhost:8080 192.198.100.100:80
LocalForward localhost:9090 192.198.100.200:80

ProxyCommand

リモートホストへ接続するために使用するコマンドを指定する。 ProxyCommandで使えるトークンは下記。

  • %h
    • 接続するホスト名
  • %p
    • 接続するポート番号 *%r
    • リモートのユーザ名

暗号方式による転送速度とCPU負荷の違い

書籍の一部を掲載。 f:id:shuheilocale:20200529082756p:plain

【LeetCode】Longest Substring Without Repeating Characters 解法【Python】

Longest Substring Without Repeating Characters

leetcode.com

与えられた文字列内で、文字が重複しない範囲の最大の文字数を返却する。

解法

1文字見ていって、重複した時点の1文字前までの文字数を最大候補として、 重複した文字の最初に出てきた場所から再度カウントし直す。 それを繰り返して最大候補の中から最大文字数を返却する。

f:id:shuheilocale:20200529072946p:plain

毎回、重複した文字の最初に出てきた文字まで戻るのはコストなので、 その場所を覚えておき、現在の場所までの文字列をスライス、みたいなふうにして高速化。

コード

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        
        if s == "":
            return 0
        if len(set(s)) == len(s):
            return len(s)
        
        max_len = str_len = 1
        local_s = [s[0]]
        s_idx = 0
        for i in range(1, len(s)):

            if s[i] in local_s:   
                str_len = i - s_idx
                max_len = max(max_len, str_len)
                
                find_idx = local_s.index(s[i])
                s_idx = find_idx+s_idx+1
                local_s = local_s[find_idx+1:]
            
            local_s.append(s[i])
        else:
            str_len = len(s) - s_idx
            max_len = max(max_len, str_len)
                
        return max_len
            

f:id:shuheilocale:20200529071442p:plain

f:id:shuheilocale:20200529071621p:plain

ハッシュ使ったらもっと速そう。

solutionに書いていたシンプルなコード(にちょっとだけ手を加えて高速化したもの)

class Solution:
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """

        str_list = []
        max_length = 0

        for x in s:
            if x in str_list:
                max_length = max(max_length, len(str_list))
                str_list = str_list[str_list.index(x)+1:]
            str_list.append(x)    
        else:
            max_length = max(max_length, len(str_list))

        return max_length

さらに、solutionに書いていた、めちゃ速い解法。 やはりdict使っている。

class Solution:
    def lengthOfLongestSubstring(self, s):
        dicts = {}
        maxlength = start = 0
        for i,value in enumerate(s):
            if value in dicts:
                sums = dicts[value] + 1
                if sums > start:
                    start = sums
            num = i - start + 1
            if num > maxlength:
                maxlength = num
            dicts[value] = i
        return maxlength

【書評】入門Python3 :とりあえず読んでおくべき

ある程度pythonを触った身からすると、読んでおいて損はない本。

入門 Python 3

入門 Python 3

  • 作者:Bill Lubanovic
  • 発売日: 2015/12/01
  • メディア: 単行本(ソフトカバー)

これが全く触ったことがな人だともしかしたら読みづらいかもしれない。 そもそもオラ本が全般的に初心者に易しくない気はする(head firstシリーズは易しい?)。

  • pythonを触ったことがある人
  • python以外の言語を触ったことがある人

上記に当てはまる場合(自分はそう)は、間違いなく優良な本だった。 というのは、やはり実践主義でコードを書いていると、変な癖がつく気がしていて、 それはpythonのポリシーと合っているかわからないし、そもそも無駄な実装をしている可能性もあり、 標準ライブラリを使えば簡単に解決できるのではないか、という不安を抱いて、 この本を読んだらその不安が的中していたからである。

例えば、setクラスはずっと重複なしリストだと思い込んでいた。 「リストから重複を除外したい」と調べたら大体setを使った解法が出てくるため、 そのために作られたクラスなのだと思い込んでしまっていた。実際は集合クラスなので集合演算ができる。 他にもcsvにあるDictReaderやDictWriterも便利だなと思った(pandas使う場面が多いけど)。

とはいえ、イディオムやデザインパターンなどの内容は書いていないため、 この本を読めばシステム開発ができるとまではいかない。 にしても、どのような道具があるのかを知ることできるし、どの場面で使えばいいのかもわかるため、 今後pythonを触るエンジニアは早めに読むことをおすすめする。

個人的付箋

集合クラス

setは集合クラス。重複なしリストかと思ってたが違った。 なので集合演算ができる。

a  = {1, 2}
b = {2, 3}

a & b
a.intersection(b)
a + b
a.union(b)

elseによるbreakチェック

whileやforは、正常終了したかどうかをチェックするオプションのelseを持っている。 ループがbreak呼び出して途中で終了しておらず、最後まで実行されたときにelse内が実行される。

for c in cs:
    print('hoge')
    break
else: # breakしていない
    print('fuga')

例外

exrcept exceptiontype as name

short_list = [1, 2, 3]
while True:
    value = input('Position [ q to quit ] ? ')
    if value = 'q':
        break
    try:
        position = int(value)
        print(short_list[position])
    except IndexError as err:
        print('Bad index:', position)
    except Exception as other:
        print('Something else broke:' other)

存在しないキーの処理

存在しないキーで辞書にアクセスする際の振る舞いについて、 get()関数を使ってデフォルト値を返すようにすれば例外を避けられる。 setdefault()関数はキーがなければさらに辞書を要素に追加することができる。

periodic_table = {'Hydrogen' : 1, 'Helium' : 2}
carbon = periodic_table.setdefault('Carbon', 12)

# carbon => 12
# periodic_table =>  {'Hydrogen' : 1, 'Helium' : 2, 'Carbon', 12}

# すでにキーがある場合は更新されない
helium = periodic_table.setdefault('Helium',  923)
# helium => 2
# periodic_table =>  {'Hydrogen' : 1, 'Helium' : 2, 'Carbon', 12}

defaultdictは辞書作成時にあらゆる新キーのためにあらかじめデフォルト値を設定する。

from collections import defaultdict
periodic_table = defaultdict(int)
periodic_table['Hydrogen'] = 1
periodic_table['Lead']

periodic_table
# defaultdict(<class 'int'>, {'Lead': 0, 'Hydrogen': 1})


特殊メソッド

比較のための特殊メソッド

メソッド 意味
__eq__( self, other ) self == other
__ne__( self, other ) self != other
__lt__( self, other ) self < other
__gt__( self, other ) self > other
__le__( self, other ) self <= other
__ge__( self, other ) self >= other

算術演算のための特殊メソッド

メソッド 意味
__add__( self, other ) self + other
__sub__( self, other ) self - other
__mul__( self, other ) self * other
__floordiv__( self, other ) self // other
__truediv__( self, other ) self / other
__mod__( self, other ) self % other
__pow__( self, other ) self ** other

その他の特殊メソッド

メソッド 意味
__str__( self, other ) str(self)
__repr__( self, other ) repr(self)
__len__( self, other ) len(self)

下記でドキュメント化されている。

https://docs.python.org/3/reference/datamodel.html#special-method-names

Guidoのアドバイス

Guido van Rossum

データ構造を作り込みすぎないようにしよう。オブジェクトよりもタプルの方がいい。ゲッター/セッター関数よりも単純なフィールドを選ぶようにしよう。組み込みデータ型はプログラマーの友達だ。数値、文字列、タプル、リスト、集合、辞書をもっと使おう。そして、コレクションライブラリ、特にデックをチェックしよう。

collections --- コンテナデータ型 — Python 3.8.3 ドキュメント

名前つきタプル

from collections import namedtuple
Duck = namedtuple( 'Duck', 'bill tail' )
duck = Duck('wide orange', 'long')
# duck => Duck(bill='wide orange', tail='long)
# duck.bill => wide orange
# duck.tail => 'long'

csv

DictReaderを使って辞書リストにすることができる。 各行のキーが、各列のヘッダーとなる。

import csv
with open('villains', 'rt') as fin:
    cin = csv.DictReader(fin, fieldname=['first', 'last'])
    villanins = [row for row in cin]

DictWriterを使って辞書リストからヘッダー付きのcsvファイルを作成することができる。

with open('dst_villains', 'wt') as fout:
    cout = csv.DictWriter(fout, ['first', 'last']
    cout.writeheader()
    cout.writerows(villains)

yaml

PyYAMLは文字列からPythonオブジェクトをロードできるが、これは危険なことだ。 load()ではなくsafe_load()を使うべき。

Ned Batchelder: War is peace

timedelta

from datetime import date, timedelta
now = date.today()
one_day = timedelta(day=1)
tomorrow = now + one_day
yesterday = now - one_day

# 17日後
now + 17 * one_day

リスト内表記はappend()を使ったリストへの要素追加より速い

result =[]
for value in range(1000):
    result.append(value)

# こちらのほうが2倍以上速い
result = [value for value in range(1000)]