読者です 読者をやめる 読者になる 読者になる

nanahara.log

プログラミング関係のノート

GoでLuhnアルゴリズム

Go

Luhnアルゴリズムについて

用途

  • 記入ミスやタイプミスを検出するためのもの
  • 暗号学的ハッシュ関数としては使えない。悪意ある攻撃を防ぐものではない
  • クレジットカード等の番号で使われている

利点と欠点

  • 任意の1桁の間違いや隣接する桁の数字の順序間違いを検出できる
  • 09 から 90 (または逆)という間違いは検出できない
  • 同じ数字が2つ連続する場合の間違いも10種類のうち4種類までは検出できる(22 ⇔ 55、33 ⇔ 66、44 ⇔ 77 は検出できない)

方法

「49927398716」 という番号を検証する場合。

操作
右端から偶数番目の桁をそれぞれ2倍 4,18,9,4,7,6,9,16,7,2,6
それぞれの数字の総和を計算する 4+1+8+9+4+7+6+9+1+6+7+2+6 = 70
10で割りきれるか調べる 70 mod 10 = 0

参考

Goで実装してみる

gist.github.com

GoでUnitTestしてみる

Goでテスト書いたことなかったので、せっかくなのでテストも書いてみる

testingパッケージを使うらしい。

気になった点として、GoにはAssertがないが、これはテスト用のミニ言語の存在を嫌う思想のようだ。

詳しくは Go の Test に対する考え方 - Qiita

理想のテスティングとは…。

最初の所感だと、Assertないと不便に感じたが、とりあえず標準に従うことにする

実行例

➜  src git:(master) ✗ go test -v luhn
=== RUN   TestLuhn
--- PASS: TestLuhn (0.00s)
PASS
ok      luhn    0.007s

# わざとFail
➜  src git:(master) ✗ go test -v luhn
=== RUN   TestLuhn
--- FAIL: TestLuhn (0.00s)
    luhn_test.go:49: Oops! [6011000990139425] is invalid
FAIL
ok      luhn    0.007s

実装

gist.github.com

テスト用データ

テスト用のクレジットカードの番号は、以下のサイトから借りさせていただいた

ECサイトの動作テストに使える、クレジットカードのテスト番号一覧 | Webクリエイターボックス

Gitのブランチ戦略まとめ

Git

今更話題として盛り上がることは少ない気がするが、自分の中で曖昧になっているので有名そうなものをまとめてみた。

数年前のエントリになるけど、雛形として有名なものだとこの辺りだと思われる

1. A successful Git branching model (git-flow)

A successful Git branching model を翻訳しました

リリースを中心に考えられており、以下の5つのブランチを使用する

  • メインブランチ
    • master : 常にリリース可能な状態。
    • develop : 次のリリースのための最新の開発作業の変更を常に反映する。
  • サポートブランチ
    • Feature branches : 新機能開発用。developから分岐して、developにマージ。(–no-ff フラグ付けて、Fast-forwardしないようにする)
    • Release branches : リリース作業用。developから分岐して、developとmasterにマージ。
    • Hotfix branches : 緊急対応用。masterから分岐して、developとmasterにマージ。

git-flowはこのモデルを補助するためのプラグイン

2. GitHub Flow

GitHub Flow (Japanese translation)

ざっくり言うとmasterブランチとfeatureブランチだけで進めるモデル。

プロダクション環境へのデプロイを毎日行うため、リリース作業というもの自体がないケース。

GitHub Flowとは何だろうか?

  • masterブランチのものは何であれデプロイ可能である
  • 新しい何かに取り組む際は、説明的な名前のブランチをmasterから作成する(例: new-oauth2-scopes)
  • 作成したブランチにローカルでコミットし、サーバー上の同じ名前のブランチにも定期的に作業内容をpushする
  • フィードバックや助言が欲しい時、ブランチをマージしてもよいと思ったときは、 プルリクエスト を作成する
  • 他の誰かがレビューをして機能にOKを出してくれたら、あなたはコードをmasterへマージすることができる
  • マージをしてmasterへpushしたら、直ちにデプロイをする

又、中の人に聞いたGitHub flowの本当の使い方 - Qiitaによると、実際はマージ前にデプロイしているらしい。

マージ前にデプロイすることでさらにユーザーに近いところでフィードバックを受けることができる

3.GitLab flow

GitLab flowから学ぶワークフローの実践

master(アップストリーム)からproduction(ダウンストリーム)に流していくモデル。

Git-flowが複雑さや、GitHub flowがリリース周りの問題点を指摘して、それらを解決しようとしたもののようだ。

  • 環境ブランチ
    • コミットがダウンストリームに流れるワークフロー。
    • ブランチ名と環境名が一致している例だと、masterブランチからpre-productionブランチへマージ(MergeRequest)、pre-productionブランチから、productionブランチへマージといったフロー。
    • Hotfixをcherry-pickしたい場合はfeatureブランチで開発したものをmasterブランチにマージ(MergeRequest)する。この際featureブランチを消さずにmasterがokであれば他のブランチにもマージする
  • releaseブランチ
    • リリース作業時、できるだけ最新のmasterからrelease(stable)ブランチを分岐。
    • releaseブランチ作成後は最小限のbugfixだけをreleaseブランチにマージ。
    • 「アップストリームファースト」。可能であればbugfixはmasterにマージしてから、releaseブランチへcherry-pickする(masterへのマージ漏れを防ぐ)

若干、話が逸れるが、「Googleがブランチを切らない開発をしている」ってホントなの?|CodeIQ MAGAZINEによると、Googleの一部のプロジェクトではブランチを切らないケースがあるらしい。

Googleは様々なプロジェクトを一つのレポジトリで管理しており、ブランチを切らない運用をしていると話しています。 この理由としてAri Shamashは、「各開発で使用する多くのライブラリに関して、管理する必要がないというメリットがある」 そして、フラグを管理しながら進めるとのこと。 フラグを使って管理することが多いようです。つまり、機能に対して条件による制限をかけながら、直接masterに書いていく

所感

最近関わっている自分のプロジェクトではリリース関連の作業がないものがなかなか想像しづらいため、GitHub FlowやGoogleの手法は適用しづらく、本当にケースバイケースだなという印象。

これらのモデルをを参考にしつつチームで要件にあったスタイルを開発が本格化する前に決めていくべきなのかと。

SQLアンチパターン チートシート

Database

SQLアンチパターンを再読した。今更だけど、後から見直すための簡易まとめ(ほぼ目次の整形)

SQLアンチパターン

SQLアンチパターン

Ⅰ.データベース論理設計のアンチパターン

タイトル 目的 アンチパターン 解決策
1 ジェイウォーク(信号無視) 複数の値を持つ属性を格納する カンマ区切りフォーマットのリストを格納する 交差テーブルを作成する
2 ナイーブツリー(素朴な木) 階層構造を格納し、クエリを実行する 常に親のみに依存する 代替ツリーモデルを使用する
3 IDリクワイアド(とりあえずID) 主キーの規約を確立する すべてのテーブルに「id」列を用いる 状況に応じて適切に調整する
4 キーレスエントリ(外部キー嫌い) データベースのアーキテクチャを単純化する 外部キー制約を使用しない 外部キー制約を宣言する
5 EAV(エンティティ・アトリビュート・バリュー) 可変属性をサポートする 汎用的な属性テーブルを使用する サブタイプのモデリングを行う
6 ポリモーフィック関連 複数の親テーブルを参照する 二重目的の外部キーを使用する 関連(リレーションシップ)を単純化する
7 マルチカラムアトリビュート(複数列属性) 複数の値を持つ属性を格納する 複数の列を定義する 従属テーブルを作成する
8 メタデータトリブル(メタデータ大増殖) スケーラビリティを高める テーブルや列をコピーする パーティショニングと正規化を行なう

Ⅱ.データベース物理設計のアンチパターン

タイトル 目的 アンチパターン 解決策
9 ラウンディングエラー(丸め誤差) 整数の代わりに小数値を使用する FLOATデータ型を使用する NUMERICデータ型を使用する
10 サーティワンフレーバー(31のフレーバー) 列を特定の値に限定する 限定する値を列定義で指定する 限定する値をデータで指定する
11 ファントムファイル(幻のファイル) 画像をはじめとする大容量メディアファイルを格納する 物理ファイルの使用を必須と思い込む 必要に応じてBLOB型を採用する
12 インデックスショットガン(闇雲インデックス) パフォーマンスを最適化する 闇雲にインデックスを使用する 「MENTOR」の原則に基いて効果的なインデックス管理を行う

Ⅲ.クエリのアンチパターン

タイトル 目的 アンチパターン 解決策
13 フィア・オブ・ジ・アンノウン(恐怖のunknown) 欠けている値を区別する NULLを一般値として使う、または一般値をNULLとして使う NULLを一意な値として扱う
14 アンビギュアスグループ(曖昧なグループ) グループ内で最大値を持つ行を取得する 非グループ化列を参照する 曖昧でない列を使用する
15 ランダムセレクション サンプル行をフェッチする データをランダムにソートする 特定の順番に依存しない
16 プアマンズ・サーチエンジン(貧者のサーチエンジン) 全文検索を行う パターンマッチ述語を使用する 適切なツールを使用する
17 スパゲッティクエリ SQLクエリの数を減らす 複雑な問題をワンステップで解決しようとする 分割統治する
18 インプリシットカラム(暗黙の列) タイプ数を減らす ショートカットの罠に陥る 列名を明示的に指定する

Ⅳ.アプリケーション開発のアンチパターン

タイトル 目的 アンチパターン 解決策
19 リーダブルパスワード(読み取り可能パスワード) パスワードのリカバリーとリセットを行なう パスワードを平文で格納する ソルトを付けてパスワードハッシュを格納する
20 SQLインジェクション 動的SQLを記述する 未検証の入力をコードとして実行する 誰も信用してはならない
21 シュードキー・ニートフリーク(疑似キー潔癖症) 欠番を詰める 隙間を埋める 疑似キーの欠番は埋めない
22 シー・ノー・エビル(臭いものに蓋) 簡潔なコードを書く 肝心な部分を見逃す エラーから優雅に回復する
23 ディプロマティック・イミュニティ(外交特権) ベストプラクティスを採用する SQLを特別扱いする 包括的に品質問題に取り組む
24 マジックビーンズ(魔法の豆) MVCのM(モデル)を単純化する モデルがアクティブレコードそのもの モデルがアクティブレコードを「持つ」ようにする
25 砂の城 サービスの安定稼働 想定不足 可能な限りの想定(ベンチマーク、テスト環境の構築、例外処理、バックアップ、高可用性、ディザスタリカバリ、運用ポリシーの策定)

ex.他(スライド)

タイトル 目的 アンチパターン 解決策
26 とりあえず削除フラグ データを消さずに、無いことにしたい 削除フラグの導入 「フラグ」以外の道も検討する



読んでいて過去の嫌な思い出も蘇る本だが、ある問題を解決しようとしてアンチパターンが発生する経緯が具体的に書かれており非常に読みやすい。

それぞれの具体例も少し書こうかと思ったが、監訳者による↓のコンパクトな神資料を発見したのでこちらを見返した方が断然分かりやすい。。

SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)

迷った際はこれらを横目に本に立ち返ろう。

しかし、テーブル記法だと読みづらかったか。。

MacBook Pro 15-inch(Late 2016) 所感

MacBook Pro 15-inch(Late 2016)が届いたので感想とレビュー
※使い始めて2日目です

www.apple.com

乗り換えた理由は5年付き合ったMacBook Air 11-inch(Mid 2011)からいい加減乗り換えたかったから。Skylake待ちたかった気持ちあったけど、待ちきれず今回に至る。

ちなみに会社ではMacBook Pro 13-inch(Mid 2015)使わさせてもらってるので、 5年分の感動が待ち受けていたわけではないし、型落ちも考えたけど同じ世代だとつまらないので、今回はTouch BarのLate 2016モデルを選択してみた。

スペック

15-inch MacBook Pro - スペースグレイ
2.6GHzクアッドコアIntel Core i7
16GB 2,133MHzメモリ
512GB PCIeベースSSD
Radeon Pro 450

15-inchの下位モデルで、ストレージだけ1段上げた。最後まで13-inchの上位モデルと迷ったが、こちらに。

使用感

感想として、新機能については所々思うところを書いているけど、個人的には旧モデルから大幅にスペック上がっていることもあり不満を上回っている。取り敢えず買って良かったとは思っている。

以下、各機能や項目についての現時点でのコメント

ディスプレイ

綺麗で広い。

Retinaで15インチ。旧モデルと比べるとふつくしい...。

15インチもあったら、多くの作業で外部ディスプレイなくてもストレス感じなさそう

キーボード

凄まじく浅い。

普段はHHKBも使ってるのでストロークの落差がすごい。

ペタペタしていて疲れやすい気がするが、これ書いているうちに心なしか慣れてきた。

HHKB繋げればいいのだが、USB Type-C環境まだ揃えてないし、Touch Barの存在がさらに薄くなる(距離が遠くなる)のはなんだか悲しい。

トラックパッド

広い。

広さは邪魔にはなっていないけど、これ狭くしてTouch Barの下にFunctionキーも置いて良かったのではと思ってしまう。

広さよりも深く押した時のリアクションがベコベコしていて、そっちの方が気になる。

Touch Bar

可もなく不可もなく。

Touch Bar自体での操作はちょっと楽しいが、とりわけ快適になるというわけでもない。

まだ使い込んでいないせいか不便は感じていないが、旧モデルで無意識下でやっていた操作でストレスを感じるのではないかと危惧している。

Vimmerだけど、Escキーは使っていない認識なのでそこは大丈夫そうだった(たぶん)(嘘でした。モード切替以外で使ってた)

なんだかモロそうだから、とにかく壊れないで欲しいという想い

Touch ID

地味に便利。

今更発表会でドヤる機能なのかというと微妙ではあるが、便利は便利。

ちなみにTouch IDの箇所が物理ボタンになっていて、電源ボタンに近い機能も担当している。長押しでの強制終了とかもここで操作できる。

USB Type-C

乗り換えコスト高。

各所で言われている通りUSB Type-C自体がどうこというより、半ば強制的な乗り換えに不満を覚える。

これから揃えていくけど、従来のポートも1つ2つ残して欲しかった。iPhone7にもデフォルトで繋げないのはどうなのか。

乗り換えた先に明るい未来がきっと待っていることを信じるしか。

消えたMag Safe(充電)

一抹の不安。

引き続きUSB Type-Cの話だが、割としっかり刺さるので、コードに足引っ掛けたら死にそう。

そのうち↓のようなものを買ってしまうかもしれない。漂う本末転倒臭。 japanese.engadget.com

あとは接続する時にマグネットに吸い寄せられる形にならないので、慣れるまでは刺すときに目視が必要。

サイズ・重さ

高さ:1.55 cm
幅:34.93 cm
奥行き:24.07 cm
重量:1.83 kg3

らしい。サイズは良いけど、思ったより重さを感じる。

ケースは店頭で確認させてもらって、この旧13-inchモデル用と思われるケースにすっぽり収まったのでこれを購入した(このケースがたまたま入っただけかもしれない)。

15-inchモデル用として売られているものは現在使用しているリュックに入らなかったこともあり。

まとめ

こいつとやっていく所存です。

App Storeのランキング取得

Tips

Appleから提供されているRSSフィードがあって、JSONでも取得可能

以下のサイトでurlを生成できて、トップセールス以外も色々ある

https://rss.itunes.apple.com/jp/?urlDesc=%2Fgenerator

Pythonトップセールスの上位10を取得するサンプル。それだけ gist.github.com

$ python apple_ranking.py
1 モンスターストライク
2 ドラゴンボールZ ドッカンバトル
3 星のドラゴンクエスト
4 パズル&ドラゴンズ
5 白猫テニス
6 アイドルマスター シンデレラガールズ スターライトステージ
7 Fate/Grand Order
8 LINE
9 LINE:ディズニー ツムツム
10 戦国炎舞 -KIZNA- 【人気の本格戦国RPG】

Pythonのクロージャについてのメモ

Python

他の言語でクロージャ書いていて、Pythonはどうだったっけと思ったのでメモ。

Effective Pythonの項目15に詳しかった。

クロージャの説明については割愛。

引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とする。 (https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%83%BC%E3%82%B8%E3%83%A3)

Pythonのスコープ

まずPythonのスコープについて。

Pythonだと基本として内側の関数から外側の変数を参照できるが、更新はできない。

# 1.これは動くが
... def outer():
...     x = 1
...     def inner():
...         print(x)
...         return x
...     return inner

>>> o = outer()
>>> o()
1    
1    

# 2.これはエラー  
>>> def outer():
...     x = 1
...     def inner():
...         print(x)
...         x = 2
...         return x
...     return inner
...
>>> o = outer()
>>> o()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in inner
  UnboundLocalError: local variable 'x' referenced before assignment

# 3.これもエラー   
>>> def counter():
...     x = 1
...     def inner():
...         x += 1
...         return x
...     return inner

>>> c = counter()
>>> c()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in inner
  UnboundLocalError: local variable 'x' referenced before assignment

1はinnerから見て外側の変数であるouterのxを参照できている。

2と3はinnerがxをローカル変数として宣言している(代入によって別の変数として宣言している)。

そして、それぞれ初期化前に評価しようとしてUnboundLocalErrorが発生している。

3の累算代入文については若干腑に落ちなかったが、

  • xへ代入されているのでaddのローカル変数として宣言されている
  • 加算前に評価を行うが、その際には初期化されていないので、UnboundLocalErrorとなる

...ということだと思われる(理解が間違っていたらご教示ください!)。

(参考)

通常の代入とは違い、累算代入文は右辺を評価する前に左辺を評価します。たとえば、a[i] += f(x) はまず a[i] を調べ、f(x) を評価して加算を行い、最後に結果を a[i] に割り当てます。 (http://docs.python.jp/3/reference/simple_stmts.html#augmented-assignment-statements)

Python3.xでのクロージャ

では実際にPythonクロージャをどう書くかというところで、Python3.xではnonlocalを使ってこう書く

# Python3.x
def counter():
    x = 0
    def inc():
        nonlocal x
        x += 1
        return x
    return inc

c = counter()
c()
# >>> 1    
c()
# >>> 2    
c()
# >>> 3    

nonlocal 文は、列挙された識別子がグローバルを除く一つ外側のスコープで先に束縛された変数を参照するようにします。これは、束縛のデフォルトの動作がまずローカル名前空間を探索するので重要です。この文は、中にあるコードが、グローバル (モジュール) スコープ以外のローカルスコープの外側の変数を再束縛できるようにします。 (http://docs.python.jp/3/reference/simple_stmts.html#nonlocal)

Python2.xでのクロージャ

ちなみにPython2.xでは、リストの要素を使って以下のように書いていたらしい。

# Python2.x
def counter():
    x = [0]
    def inc():
        x[0] += 1
        return x[0]
    return inc 

c = counter()
c()
# >>> 1    
c()
# >>> 2    
c()
# >>> 3    

参考

Effective Python ―Pythonプログラムを改良する59項目

Effective Python ―Pythonプログラムを改良する59項目

PythonでCSVをDBにインポート(with Flask-Script and SQLAlchemy)

Python Flask

Flask + SQLAlchemyを使っていて、フィクスチャ、サンプルデータ、マスタデータ等をDBに入れたいが、何かしらのファイル形式(今回はCSV)で書いたものをDBへとインポートする仕組みを作りたい

LOAD DATA INFILE 使えばええやんって話もあるけど、インポートの時点である程度のバリデートもできる(実際はデータを入れてから流すケースが多そうではあるが)ので、一考の価値はありかと

例えば、下記のようなテーブル(Message)に対して

mysql> desc message;
+------------+-----------+------+-----+---------+----------------+
| Field      | Type      | Null | Key | Default | Extra          |
+------------+-----------+------+-----+---------+----------------+
| message_id | int(11)   | NO   | PRI | NULL    | auto_increment |
| author     | text      | YES  |     | NULL    |                |
| text       | text      | YES  |     | NULL    |                |
+------------+-----------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

以下のようなCSV(message.csv)を用意して、データを取り込むようにする

message_id,author,text
1,Tom,This is a sample message
2,Ken,Oops!

Flask-Scriptとは

Flaskで外部スクリプトを簡単に書くためのextention。

Djangoでいうmanage.pyのようなものが実現できる

Flask-Script — Flask-Script 0.4.0 documentation

@manager.command
def hello():
    "Just say hello"
    print "hello"
python manage.py hello
> hello

↑は公式の引用だが、こういった形で使用できる

CSVをDBにインポートする

CSVに対してmodel(SQLAlchemy)を動的にロードしDBに突っ込む

将来のmodelの追加・変更を考えるとインポートのロジック自体にmodelの情報を持たせたくない

ここではCSVのファイル名をクラス名、CSVの1行目をクラスの各属性の情報として役立てるようにしている

from flask_script import Manager
from skeleton import app, db
import csv
import importlib
import os


manager = Manager(app)


@manager.command
def import_csv():
    fixtures_dir = app.config['FIXTURES_DIR']
    models = importlib.import_module('skeleton.models')

    for file_name in os.listdir(fixtures_dir):
        class_name = file_name.replace(".csv", "").capitalize()
        Klass = getattr(models, class_name)
        with open(fixtures_dir + '/' + file_name, encoding='utf-8') as csv_file:
            reader = csv.reader(csv_file, delimiter=',')
            header = next(reader)
            for row in reader:
                klass = Klass()
                for i in range(len(header)):
                    setattr(klass, header[i], row[i])
                db.session.add(klass)
            db.session.commit()


if __name__ == "__main__":
    manager.run()

件数多ければbulk insertにする必要がある

Pythonメモ

  • importlib : モジュールの動的ロードしてくれるライブラリ
  • getattr : object の指名された属性の値を返す組み込み関数。getattr(x, ‘foobar’) は x.foobar と等価
  • setattr : 値を属性に関連付ける組み込み関数。setattr(x, ‘foobar’, 123) は x.foobar = 123 と等価

31.5. importlib — import の実装 — Python 3.6.0 ドキュメント
2. 組み込み関数 — Python 3.6.0 ドキュメント

modelやdb周りの簡素な設定も含んだコードはこれ