done is better than perfect

自分が学んだことや、作成したプログラムの記事を書きます。すべての記載は他に定める場合を除き個人的なものです。

Go言語でPythonのcollections.Counterを簡単に実装してみた

Pythoncollections.Counterは個人的にすごく好きなんですが,Go言語の練習を兼ねてGo言語で実装してみました.

完璧な実装ではなく,カウントできるのは文字列だけです.また,普通に実装間違えてる可能性もあります.自己責任でよろしくお願いします.

Pythoncollections.Counterについてはここでも紹介しています.細かい仕様については公式ページ参照のこと.

以下コードです.

package main

import (
    "fmt"
    "sort"
)

type Counter struct {
    innerMap map[string]int
    sum      int
}

type KeyCountPair struct {
    key   string
    count int
}

type KeyCountPairList []KeyCountPair

func (kcpl KeyCountPairList) Len() int {
    return len(kcpl)
}

func (kcpl KeyCountPairList) Swap(i, j int) {
    kcpl[i], kcpl[j] = kcpl[j], kcpl[i]
}

func (kcpl KeyCountPairList) Less(i, j int) bool {
    return kcpl[i].count > kcpl[j].count
}

func (c *Counter) MostCommon(n int) KeyCountPairList {
    length := len(c.innerMap)
    pairs := make(KeyCountPairList, length)
    idx := 0
    for k, v := range c.innerMap {
        pairs[idx] = KeyCountPair{
            key:   k,
            count: v,
        }
        idx++
    }
    sort.Sort(pairs)
    return pairs[:n]
}

func (c *Counter) Elements() []string {
    if c.sum == 0 {
        for _, v := range c.innerMap {
            if v > 0 {
                c.sum += v
            }
        }
    }
    elements := make([]string, c.sum)
    idx := 0
    for k, v := range c.innerMap {
        if v > 0 {
            for i := 0; i < v; i++ {
                elements[idx] = k
                idx++
            }
        }
    }
    return elements
}

func (c *Counter) Subtract(tc *Counter) *Counter {
    for k := range c.innerMap {
        c.AddN(k, -tc.Count(k))
    }
    return c
}

func (c *Counter) Add(s string) {
    c.innerMap[s] += 1
    c.sum += 1
}

func (c *Counter) AddN(s string, n int) {
    c.innerMap[s] += n
    c.sum += n
}

func (c *Counter) Count(s string) int {
    return c.innerMap[s]
}

func NewCounter() *Counter {
    m := make(map[string]int)
    return &Counter{
        innerMap: m,
        sum:      0,
    }
}

func main() {
    c := NewCounter()
    c.AddN("test", 3)
    c.AddN("apolo", 4)
    c.AddN("google", 8)
    c.AddN("microsoft", 3)
    tc := NewCounter()
    tc.Add("test")
    fmt.Println(c)               // ==> &{map[microsoft:3 test:3 apolo:4 google:8] 18}
    fmt.Println(c.Count("word")) // ==> 0
    fmt.Println(c.Subtract(tc))  // ==> &{map[test:2 apolo:4 google:8 microsoft:3] 17}
    fmt.Println(c.Elements())    // ==> [test test apolo apolo apolo apolo google google google google google google google google microsoft microsoft microsoft]
    fmt.Println(c.MostCommon(2)) // ==> [{google 8} {apolo 4}]
}

sort.Sortで降順にソートする場合ってLessの不等号逆にするしかないってまじですか?

Pythonの文字列連結に"+"演算子は必要ない

Pythonの入門サイトとかだと,文字列連結に+演算子を使うことがよく例示されているようです.

s = "this " + "is " + "test"
print(s) ==> "this is test"

ですが,Pythonにおいて文字列連結には+演算子は必要ないです.

s = "this " "is " "test"
print(s) ==> "this is test"

「だからどうした演算子書かないほうがわかりにくいじゃねーかバーカバーカ」って感じですが,例えばPythonの標準ライブラリだとエラー文出力に使われています.

# [ここ](https://github.com/python/cpython/blob/master/Lib/collections/__init__.py#L64-66)より引用
if not args:
    raise TypeError("descriptor '__init__' of 'OrderedDict' object "
                    "needs an argument")

要するに,長い文章を打ちたいけどトリプルクオートで書くとインデントが崩れて嫌で演算子も書きたくないみたいなときには使えそうですね(どんなときかは知りませんが)

git diffで日本語の文字化けを治す

このページのまんまです. ファイルはutf-8なのになぜか文字化ける(というか,みたいな表記になってしまう)ので,↑のように設定しました.

環境はMac OSΧ 10.10.1,git versionは2.2.1です.

$ git config --global core.pager "nkf -w | LESSCHARSET=utf-8 less"

前までは文字化けしてなかった気がするんだけどなぁ・・・

情報セキュリティスペシャリストに受かった

情報セキュリティスペシャリストに受かった

表題の通りです.大学院生の間に高度試験に一つは受かっておきたかったので目標が叶いました.

行った対策などを残しておきたいと思います.

情報セキュリティスペシャリストとは?

独立行政法人であるIPAが行っている情報処理技術者試験の一つです.
情報処理技術者試験試験には4つのレベルがあり,その内の最高レベルの一つが 情報セキュリティスペシャリストです.

情報システムや組織に対する脅威や脆弱性を評価し、技術面・管理面での有効な対策を遂行できる セキュリティエンジニアや情報システム管理者を目指す方に最適です。 情報セキュリティの重要性はますます高まっており、いま最も旬なエンジニアです。

上記ページより引用してみました.まあ大仰なことは書いてありますが,試験の中身を見てみると 高度に技術的というよりは如何に人間という最悪の脆弱性と戦うか,といった試験のように思います.
まあ実際の仕事でもそうなんでしょうね・・・

個別のセキュリティ問題に関してというよりは,どこに脆弱性が起こり得るか,ということを 考える知識はついたように思うので,セキュリティの初歩を学ぶ上ではとてもいい試験だと思います.
自分も,ここからさらに知識を広げていきたいですね.(更新制になるという噂もありますし・・・)

点数

点数は正直よくありませんでした・・・特に午後II.まさかの及第点ギリギリ.

得点
午前I得点 78.20点
午前II得点 76.20点
午後I得点 78点
午後II得点 60点

それぞれ満点は100点,合格基準点は60点以上です.午後IIほんとにやばかった. 自己採点などはしていないので何が原因かはわかりませんが,読解力のなさが如実に出た気がします.
高得点を狙うならば,文章力が必須かと.

行った対策

買った参考書は以下の二つです.

二つともとても勉強の役に立ちました.特に下の過去問題集では,IPAの公式解答に異議を唱えていたりとても面白かったです.:)

一つ目を試験の始まる三ヶ月ほど前に購入し,一ヶ月ほど前までだらだらと勉強していました.
一週間前に過去問を始めて解いて30点ほどしか取れなかったので慌てて過去問題集を買ってきて ひたすら過去問を解く→わからない問題・分野を復習するということを繰り返しました.

試験を受けてみて

上にも書きましたが,午後の試験は文章題です.問題文の文意を的確に汲み取る必要があります. また,記述の問題では高度な答えではなく,拍子抜けするくらい簡単な答えが正答である場合があるので, やはり慣れる意味を込めて過去問を解くのは必須でしょう.(「会社のラップトップを社外に持ち出すときに 気をつけるべきことはなんでしょう」→「肌身離さず持ち歩くこと」が答え!?)

次はデータベーススペシャリストを受けてみようと思っています.データベースの知識もわずかにしかないので また勉強が大変そう・・・

Pythonを始めた時から知っていたかったベターな書き方

かなり基本的な内容です.それと記事に一貫性があるわけではないです.

1. with statement

定番のwithです.ほとんどの場合で使えると思うので,積極的に使いましょう. Goでは同じようなことをdeferを使ったりしますね.

# not good...
f = open("./test.txt")
foo = f.read()とかその辺
f.close()
# better!
with open("./test.txt") as f:
    foo = f.read()

上のコードでは,例えばfoo = f.read()の部分のコードが長い場合,f.close()を忘れがちですよね. 下のコードではそのような心配が入りませんし,簡潔です.

2. collections

pythonはよくbattery includedな言語と言われますが,その名の通り外部のライブラリに あまり頼らず標準ライブラリだけで結構戦えます.

collectionsも便利な標準ライブラリの一つです.collectionsの中でも特にCounterdefaultdictは 使う機会が個人的に多いです. どちらもdict型によく似た構造体ですが,dict型にはない便利な機能が付いています.

  • Counter Counterはその名の通り引数として渡されたiterableなものの要素の数を数えてくれます.できたものは keyに要素,valueに要素の数が入ります. またそれ以外にも,most_common(n)というメソッドを使うと最頻出の要素から上位n個の要素を要素をとって来てくれます.
from collections import Counter
c = Counter([1,1,2,3,3,4])
print(c.most_common(2)) # ==> [(1, 2), (3, 2)]

同じことはdict型にもできますが,よくやりがちなミスが下のような感じです.

d = {}
for token in [1,1,2,3,3,4]:
    d[token] += 1 # ==> 当然エラー.初期値が割り当てられていない,

setdefaultなどで頑張ってもいいですが,あまり美しくないですよね,

  • defaultdict defaultdictdict型に似たようなクラスです.先ほどのダメなコードの例では初期値が割り当てられていなくて エラーとなっていましたが,dictを使っていて最初から初期値を割り当てておいて欲しいということはよくあります. そんなときにこういう風に書くと便利です.
from collections import defaultdict
d = defaultdict(list)
for i, token in enumerate([1,1,2,3,3,4]):
    d[token].append(i)
print(d) # ==> defaultdict(<class 'list'>, {1: [0, 1], 2: [2], 3: [3, 4], 4: [5]})

このように,定義するところで最初に空のlistを初期値にすると示すだけで,新たなkeyでアクセスされた時に 空のlistを最初から割り当てていてくれます.

3. sort

あるlistdictをソートした上でイテレーションを回したいことはよくあると思います.listはともかく, dictの場合はkeyでソートしたいときやvalueでソートしたいとき,色々な場合があると思います.

d = {2: 3, 1: 4, 5: 1}
for k, v in sorted(d.items()):
    print(k, v) # ==> 1 4, 2 3, 5 1の順番.keyで昇順の並び
for k, v in sorted(d.items(), key=lambda x:x[1]):
    print(k, v) # ==> 5 1, 2 3, 1 4の順番.valueで昇順の並び

もっと複雑な場合,例えばPythonの公式ドキュメントでは operatorモジュールを利用して以下のようなコードを紹介しています.

from operator import itemgetter, attrgetter
class Student:
    def __init__(self, name, grade, age):
        self.name = name
        self.grade = grade
        self.age = age
    def __repr__(self):
        return repr((self.name, self.grade, self.age))

student_objects = [
    Student('john', 'A', 15),
    Student('jane', 'B', 12),
    Student('dave', 'B', 10),
]

print(sorted(student_tuples, key=itemgetter(2))) # ==> [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
print(sorted(student_objects, key=attrgetter('age'))) # ==> [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
# gradeでソートしてさらにageでソートする場合
print(sorted(student_tuples, key=itemgetter(1,2))) # ==> [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
print(sorted(student_objects, key=attrgetter('grade', 'age'))) # ==> 上と一緒

4. all(), any()

以前に少しだけ書いたことがありますが,anyallはかなり便利です. 例えば,以下のようなコードを書いている場合,allを使うとスッキリかけます.(ちょっと例えが極端ですが...)

# not good...
cnt = 0
l = [1,1,2,3,4,4,5]
for i in l:
    if i < 10:
        cnt += 1
if cnt == len(l):
    print("All numbers are less than 10")
# better!
if all(x < 10 for x in l):
    print("All numbers are less than 10")
if any(x >= 10 for x in l):
    print("All numbers are less than 10")

5. ファイルオブジェクトに対するイテレーション

Pythonでは,ファイルオブジェクトに対してイテレーションをかけられます.巨大なファイルを行ごとに読みたい なんて時にすごく便利です.

with open("./test.txt") as f:
    for line in f:
        print(line)

公式ドキュメントのとおり細かい調整(シーケンス場所を変えるなど)はできませんが,多くの場合はこの書き方だけ覚えておけば十分でしょう.

5. sum

sum関数はジェネレータも取ることができます.例えば以下のようなコードだと,わざわざlistを生成するより早いです.

In [1]: %timeit sum(x for x in range(100))
100000 loops, best of 3: 6.21 µs per loop

In [2]: %timeit sum([x for x in range(100)])
100000 loops, best of 3: 6.94 µs per loop

sum以外にも結構こういう風に書けるのは多いので,遅いなーと思ったらジェネレータで書いてみるのもいいでしょう.


以上です.もっと紹介したいのですがめんどくなったので終わります. もし何か間違いやもっといい書き方などあれば教えてくれると嬉しいです.

指定ディレクトリ(デフォルト: カレントディレクトリ)の指定サイズ以下(デフォルト: 4096 bytes)のファイルを削除するスクリプト

を作りました。

大量にファイルをダウンロードした時にゴミがいっぱいある時、一時ファイルを削除したいときなどにお役立てください。

Python3依存です。

gist

Numpyで1-of-K表現を簡単に作る方法

愚直にやるとめんどくさそうでしたが、結構簡単に出来たのでメモ。 一応、1-of-K表現とは、ある要素だけが1で、それ以外が0なベクトルです。

[0, 0, 1, 0, 0]

みたいな奴。

ここでは、あるidxの要素が1、それ以外が0であることがわかっていると仮定する。

import numpy as np
K = 10
idx = 2
a = np.zeros(K)
a.put(idx, 1) # ==> array([ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])

簡単!