遙かなるマチョジニア

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

【書評】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)]

【LeetCode】Maximum Product Subarray 解法【Python】

leetcode.com

Maximum Subarrayの総乗バージョン。

しかし、解法が全然違う(自分が思いついた解法は)。

総和の場合と同じように、スプレッドシートで計算する。 f:id:shuheilocale:20200526063051p:plain

ここから見えてきたものをルール化する。

inputの配列の内、最大になる候補を「特定範囲」とすると。

  • 特定範囲に0がはいると総乗は0
  • 特定範囲に負が奇数個入ると、総乗は負
  • 特定範囲に負が偶数個入ると、総乗は正

これらを踏まえてロジックは下記のようにする。

  • 配列内に0が出てくるまでの範囲を特定配列とする
  • 特定範囲に負が偶数個の場合は、特定範囲の総乗を最大値候補にする
  • 特定範囲に負が奇数個の場合は、先頭の負を除く範囲での総乗と、最後尾の負を除く範囲での総乗を比較し、大きい方法を最大値候補にする
  • 最大値候補から、最も大きい値を返却する

ソースは汚いのでいつかのタイミングで修正したい。

def prod(nums):
    if len(nums) == 0:
        return None
    
    result = nums[0]
    for num in nums[1:]:
        result *= num
    return result
        

class Solution:
    
        
        
    def maxProduct(self, nums: List[int]) -> int:
        """
        負を偶数個含むなら、最大になる
        負を奇数個含むなら、最小になる
        0を含むとかならず0
        
        0がある場合は,0で区切る
        その範囲で、
        負が偶数個の範囲が、ローカルマックス
        左右のどっちを削れば、最大になるか。
        1つだけならそれ正だろうが負だろうが
        """
        if len(nums) == 1:
            return nums[0]
        
        local_nums=[]
        max_ = nums[0]
        count_minus = 0
        local_sumproduct = 1
        local_minus_idx_l = -1
        local_minus_idx_r = -1
        zero_idx_l = -1
        zero_idx_r = -1
        for idx, num in enumerate(nums):
            
            if num != 0:
                local_sumproduct *= num
                if num < 0:
                    count_minus += 1
                    
                    if local_minus_idx_l == -1:
                        local_minus_idx_l = idx
                    local_minus_idx_r = idx
                    
                local_nums.append(num)
            # 0がきたら一回計算
            else:
                zero_idx_r = idx
                if len(local_nums) != 0:
                    
                    if len(local_nums) == 1:
                        max_ = max(max_, local_sumproduct)
                    # 偶数のときは単純に全ての積
                    elif count_minus % 2 == 0:
                        max_ = max(max_, local_sumproduct)
                    # 奇数のときは、マイナスを外す
                    elif count_minus == 1:
                        a = prod(nums[zero_idx_l+1:local_minus_idx_l])
                        b = prod(nums[local_minus_idx_l+1:zero_idx_r])
                        n = []
                        if a is not None:
                            n.append(a)
                        if b is not None:
                            n.append(b)
                        max_ = max(n+[max_])
                    else:
                        a = prod(nums[local_minus_idx_l+1:zero_idx_r])
                        b = prod(nums[zero_idx_l+1:local_minus_idx_r])
                        max_ = max(a,b,max_)
                        n = []
                        if a is not None:
                            n.append(a)
                        if b is not None:
                            n.append(b)
                        max_ = max(n+[max_])
                        
                    max_ = max(0, max_)
                        
                # 初期化
                zero_idx_l = idx
                zero_idx_r = -1
                local_minus_idx_l = local_minus_idx_r = -1
                local_sumproduct = 1
                local_nums= []
                count_minus = 0
        
        # 最後の処理
        if len(local_nums) != 0:

            if len(local_nums) == 1:
                max_ = max(max_, local_sumproduct)

            # 偶数のときは単純に全ての積
            elif count_minus % 2 == 0:
                max_ = max(max_, local_sumproduct)
            # 奇数のときは、マイナスを外す
            elif count_minus == 1:
                a = prod(nums[zero_idx_l+1:local_minus_idx_l])
                b = prod(nums[local_minus_idx_r+1:])
                n = []
                if a is not None:
                    n.append(a)
                if b is not None:
                    n.append(b)
                max_ = max(n+[max_])
            else:
                a = prod(nums[local_minus_idx_l+1:])
                b = prod(nums[zero_idx_l+1:local_minus_idx_r])
                n = []
                if a is not None:
                    n.append(a)
                if b is not None:
                    n.append(b)
                max_ = max(n+[max_])
        
        
        return max_

f:id:shuheilocale:20200526064349p:plain

【LeetCode】Maximum Subarray 解法【Python】

Maximum Subarray leetcode.com

inputの配列の内、隣接する数字の合計が最も高くなる値を求めよ。 制限:O(n)

二重loop使えば簡単に解けるがもちろんそれはダメ。 サンプルのINPUTを可視化すると↓

f:id:shuheilocale:20200522073955p:plain

4, -1, 2, 1 の範囲が最大で6になる。

それぞれ開始位置をずらしてcumsumしていく。 と下記になる。

f:id:shuheilocale:20200522074105p:plain

太字の部分が各列で最大値となり、 太字の中で最大の値は6、つまり4,-1,2,1の数字の合計ということがわかる。

この太字をなぞるようにしてループを回し、最大値を保存すればOK。

さらに制約として、 全て正の場合は単純な合計、全て負の場合は、配列の中の最大値を返すようにして多少の高速化を行う。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        
        max_ = max(nums)

        if max_ <= 0:
            return max_
    
        if min(nums) > 0:
            return sum(nums)
        
        cumsum = nums[0]
        for num in nums[1:]:
            cumsum = max(num, cumsum + num)
            max_ = max(max_, cumsum)
            
        return max_

f:id:shuheilocale:20200522074410p:plain

テレワークのお供にAudibleはいかがですか。

自分は元々読書好きだが、隙間時間に本を読むだけでは満足できない。 移動中はpodcastやvoicyも聴いているが、できれば読書も楽しみたい。

てことで本を読み上げてくれるamazonが提供しているサービスを紹介したい。

本を読み上げてくれる

いや、まんまじゃん!と思うかもしれないが、そう、まんまである。

本を読み上げる、以外の何もない。でも求めていたのはそれだけなのだから、それで満足している。 本の種類は、そこそこある。著名な本はある程度抑えていると言った感じ。

f:id:shuheilocale:20200521062428p:plain

音声は気にならないし、むしろ感動した

音声については、人によって評価はまちまちなので好みによると思うが、個人的には気にならない。というか、ビジネス書が多いから、別にいいとも思わないし、悪いとも思わない。 ただ、三体を聴いたときは、すごいなあと思った。

三体

三体

  • 作者:劉 慈欣
  • 発売日: 2019/07/04
  • メディア: ハードカバー

これ、ナレーター1人でやってるのか!!という感動。読書だけだと決して体験できなかったのですごく嬉しかった。

倍速再生対応

結構細かい刻みでx3まで対応している。

f:id:shuheilocale:20200521062905p:plain

再生時間が書いているので、そこから倍数をかけてあげて、どのくらいで読み終えるのかが把握できるのも良い。

1コイン無料

登録すると初月は1コイン無料で配布される。それで試して、気に入らなければやめれば、料金は発生しない。 コインというのは、毎月配布されて、好きなオーディオと交換することができる。 もちろんコインがなくても、オーディオを購入することは可能だが、その際は、コインではなくてお金で直接購入する。 なのでコインは価格が高い作品と交換するのがよい。もちろん、興味のない作品と交換する必要はないが、 購入候補があれば、その中から一番高いものをコインで、一番安いものをお金で買ったほうがいい。

ながら聴きは移動時間だけではなくなった

普段は移動中にaudibleを使っていたが、最近はそれ以外でも使う時間が増えた。そう、テレワーク中である。シャローワーク(単純作業)の際は、 手が勝手に動くため、脳味噌をaudibleに集中することができる。流石に読書はできないが耳で聴く分には問題ない。 素晴らしい、テレワークと相性ぴったしかよ!

【LeetCode】Product of Array Except Self 解法【python】

leetcode.com

これはかなり考えたけどO(N)かつ除算なしという制限をクリアする解放が思い付かずにいた。 solutionをちらっと見たら図が載っていてそれでなるほど、となった(ソースと解説は読んでいない)。

ある1次配列について、自分以外の要素を乗算した配列を返す、という問題。図にするとこんな感じ↓

f:id:shuheilocale:20200520071148p:plain

ハイフン以外の要素をかける。

除算していいなら、全てを掛け合わせたあとでループして各要素を除算すればOKだがこれは禁止。 各要素について、各要素を除いた数を乗算というブルートフォースが考えられるがO(N)という制限があるので禁止。

で、よくみると、ハイフンを境に、左と右で数字が分かれている。 なので、左の乗算と右の乗算をそれぞれ計算して最後に各要素で掛け話せればO(N)で実現できる。

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        
        length = len(nums)
        l = [1] * (length-1)
        r = [1] * (length-1)
        
        l[0] = nums[0]
        r[0] = nums[-1] 
        
        rnums = nums[::-1]
        
        for i in range(1, len(nums)-1):
            l[i] = nums[i]*l[i-1]
            r[i] = rnums[i]*r[i-1]
            
        return [lv*rv for lv, rv in zip([1]+l, r[::-1]+[1])]
        

処理速度は全体的にみると少し遅いけど、solutionに書かれているものよりは速い。 f:id:shuheilocale:20200520071724p:plain

dicussに書かれていた解法(120ms)は↓

class Solution(object):
    def productExceptSelf(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        
        if not nums: return False
        
        arr = [1] * len(nums)
        pi = pj = 1

        for i in range(len(nums)):
            j = -1-i

            arr[i]*=pi; arr[j]*=pj
            pi *= nums[i]; pj *= nums[j]        
                    
        return arr