2013-04-13

Go Conference 2013 Spring

Go Conference 2013 Spring 略して GoCon に行ってきました。

A Tour of Go

公式チュートリアルであるところの A Tour of Go というのがあります。1ページにコードと文書があって、Run ボタンをクリックすると実行結果が見られます。以前にこれを読んだことがあったのですが、よく分からないまま Run ボタンを押して、どんどんページを送っていって、結局何も分かっていない、という状態になりました。そこで今回は写経することにしました。

ダウンロードサイト から、OS X のバイナリをダウンロード、インストールして写経をしていたら、主催者の @ymotongpooGo Playground を教えてくれました。ブラウザで Go のコードを書いて、その場で実行できます。実行時間が短く、サードパーティのライブラリを必要としないコードであれば、これで簡単に実行できます。そういうわけで、Go Playground でチュートリアルをやっていきました。

ついでに書いたコードを簡単に共有するための URL も生成できます。

変数とポインタ

Go では変数とポインタが明確に区別されます。普段つかっている Python では名前にオブジェクトをバインドするモデルで、事実上すべてがポインタみたいなものです。ここはちょっと注意が必要でした。Go では x = y と書くと x に y のコピーを代入します。ポインタ(というか参照というか)は明示的に & を使います。

package main
import "fmt"

type Foo struct {val int}

func main() {
     f := Foo{0}
     g := f   // 変数なので f のコピー
     h := &f  // ポインタ
   
     f.val++

     fmt.Println(f.val)  // => 1
     fmt.Println(g.val)  // => 0
     fmt.Println(h.val)  // => 1
}

一方 Python では x = y と書くと、x に y のポインタ(というか参照というか)を代入します。コピーを代入するには明示的に copy モジュールを使うという考え方です。

from copy import copy

class Foo(object):
    def __init__(self, val):
        self.val = val

f = Foo(0)
g = copy(f)  # copy
h = f        # pointer

f.val += 1

print(f.val) # => 1
print(g.val) # => 0
print(h.val) # => 1

goroutine

並列処理/並行処理を Go は言語レベルでサポートしていて、go foo(..) と書くと、foo() 関数の呼び出しが非同期で実行できます。

トイレで「goroutine ってコルーチンなんだなぁ」と思っていたのですが、席に戻った瞬間 @Jxck_ さんの発表 で「goroutine はコルーチンではありません」と電撃発言です。どうやら OS のスレッド上で動作する並行ルーチンとして実装されているようです。

実装はおいといて、コルーチンを使ったトランポリンみたいなことが簡単に書けそうです。ちなみにトランポリンの理解は非常に曖昧です。

goroutine として呼び出す時でも、関数は通常の引数をとるので、ポインタを渡すことができます。ということは、複数の goroutine で同じオブジェクトを参照するという事態が起こるんだろうなぁと考えて、簡単なコードを書きました。

package main

import (
  "fmt"
  "time"
)

type Foo struct {
  value int
}

func main() {
  f := Foo{0}  // 初期値は 0
  fmt.Println(f.value)

  go Incr(&f, 1, 1)  // +1
  go Incr(&f, 2, 200) // +2

  time.Sleep(1 * time.Second)
  fmt.Println(f.value)  // => 2
}

func Incr(f *Foo, delta int, wait time.Duration) {
  oldValue := f.value  // 古い値を取得して
  time.Sleep(wait * time.Millisecond)  // しばらく休んで
  f.value = oldValue + delta  // 上書き
}

プロセスをまたいでいる時にどうなるかは不明です。こんなときにスレッドセーフじゃないようなコードを書くなという話なわけです。

並列処理といえば…

これは明らかに誤解なのですが、気分がよかったので、Twitter 上では訂正せずにいました。

以前の職場で開発環境/ツールのマーケティングをしていて、そのラインナップに FPGA 用にコードを書くツールが含まれていました。もともとグラフィカルに記述すると、PC 用に実行形式のバイナリを吐き出すというツールで、簡単に並列処理を記述できたのです。そこ発展して FPGA を搭載した I/O ボードの処理をデプロイできる、というツールです。

そんなわけでハードの設計は、まったくできません。ただFPGAを使うときにはプログラムの書き方が、大きく変わる、というのは感じています。Go も並列処理を念頭においた言語らしいので、あらためて並列処理を考える時期にきているようです。

2013-04-07

mock.patch() が置き換えする対象

mock.patch() の挙動について質問されて、即答できなかったので、簡単に調べ直してみました。

簡単なサンプル

まず以下のようなコードを想定します。

# foo.py その1
import random

def pickup(seq):
    return random.choice(seq)

random.choice() 関数は呼び出すごとに挙動が変わり、テストしにくいので、mock の出番です。

# test.py
from unittest.mock import patch
import foo

with patch('random.choice') as m:
    m.return_value = 0
    assert foo.pickup([1, 2, 3]) == 0

import の仕方を変えるとモックに失敗する

ここで foo.py の書き方を以下のように変えます。

# foo.py その2
from random import choice

def pickup(seq):
    return choice(seq)

pickup() 関数を外側から見た挙動は同じです。しかし、なんということでしょう、test.py を実行すると assert が失敗します。pdb や print を使って調べるとわかりますが、choice() 関数が Mock オブジェクトになっていません。

名前に対してパッチしている

with patch('random.choice') のコンテクストに入るとき、大雑把に以下のようなことが起こります。

import random
random.choice = Mock()

※ 実際には Mock ではなく、そのサブクラス MagicMock ですが、この議論の本質ではないので、Mock で話を進めます。

Python のプロセスでは、モジュールはシングルトンとしてふるまうため、プロセス内で random.choice という名前は、Mock オブジェクトを参照することになります。foo.py その1の中では random.choice という名前を使っているため、Mock オブジェクトを参照します。

ところが foo.py その2 は random.choice という名前を参照しているわけではありません。from random import choice すると、大雑把に以下のようなことが起こります。

import random
choice = random.choice
del random

patch() 適用後、名前の参照先は

  • グローバルな random.choice => Mock オブジェクト
  • foo.py の choice => 元の choice 関数

のようになっています。このため、foo.py その2では choice がモックにならないのです。

名前空間 foo 以下の名前に対して patch を適用する

じゃあ、どうするのかというと、普段は patch('foo.patch') としています。上記の問題が発生するような状況だというのが分かっていれば、これで解決です。しかしながら、foo.py を、その1からその2に実装を変えたとき、テストが fail するけれど、どこがどうなっているのか見つけにくいな、と思いました。

その1のときに、test.py に patch('foo.random.choice') と書くとよいのでしょうか。こうすると foo.py その2に書き換えてテストすると、fail ではなくて patch() 実行時にエラーが出るので、名前がおかしいことに気付くような気がします。

2013-04-06

Robin Williams / ノンデザイナーズ・デザインブック

文書デザインの考え方を紹介する本

今年、付きまとっている @ymotongpoo が読んでると聞いて、ノンデザイナーズ・デザインブックを再読しました。プログラミングでいうところの、デザインパターンとも言うべきプラクティスが解説されています。

この本自体は以前から持っていましたが、流し読みしただけで、実践したことはありません。使う機会がなかったのと、この本がパターンのカタログであると捉えていなかったからです。

再読したので、忘れないうちの実践として、このブログのデザインを変更しました。Blogger.com に最初から入っている「シンプル」というテーマをベースに、CSS だけを変えました。HTML は触っていません。

整列、近接、反復

あなたは文字揃えの扱いに慣れてきました。しかしいっそうの訓練を積むまでは、 1ページに1種類の文字揃えだけを使うようにしてください。つまりすべての文字列を、左揃えか、右揃えか、中央揃えかのどれか一つにするのです。

もとのデザインではタイトル、エントリのタイトル、本文が揃っていませんでした。そこで、これらの左端を揃えました。

右のカラムは元々揃っていたので、そのままです。

もし関連する項目がいくつかあるなら、それを近接させてグループにしましょう。直接的な関連をもたない項目は離しましょう。

ブログの論理的な構成は、以下のようになっています。


  • ヘッダ
  • 記事群
    • 記事
      • 日付
      • 題名
      • 本文
        • セクション
        • [セクションタイトル]
        • 文章


これまではセクションタイトルのフォントが小さくて、セクションを認識しづらい見栄えでした。大きくすると、まとまりが見えるようになりました。

反復は、一貫性をさらに強化したものです。約物、書体、線、色など、あなたがす でに繰り返し使っている要素を見つけて、そのうちの一つを強調して反復の要素と して使えないか考えてみましょう 

ブログの場合、似た構成要素が反復するつくりになっています。絵や文章を配置するのではなく、文書構造にスタイルを適用しているので、自動的に反復が適用できます。

コントラストが課題

コントラストは、染みを隠すために壁を塗るのに似たところかあります。染みの上に塗る色が周りの色とだいたい同じではだめで、正確に同じ色にするか、それが不可能なら壁全体を塗り直すしかないのです。

今回の作業ではコントラストを活用できていません。タイトルを目立たせていないため、全体的にメリハリがないのです。

広告なんかだと、読む気がなかった人に強制的に提示するので、タイトルが目立つことは重要かも知れません。このブログへの流入は、リンクか検索エンジンからのほとんどです。ブログのタイトルが目立ったから読む、という因果関係はまず成立しないでしょう。かといって本文との差がないと構造が分かりにくいものです。そんなわけで、ヘッダ部分はフォントを大きく、色を薄くしました。

そもそもブログ名が悪いのかも知れません。見栄えではなくて、コンテンツの問題なので、今回は保留。

また、色のセンスがないのでモノトーンにしていますが、本当は色を使わないほうが難しいはずです。marippe の「色彩センスのいらない配色講座」や、フルカラー新装増補版が助けになるのだと思います。

問題解決のフレームワーク

実践してみたところ、かなりマシになりました。特に整列の効果は絶大です。インデントが好きで Python を使っているのに、もっと早く気づかなかったのが不思議なくらいです。

これまで、漠然と気に入らないなあ、と感じていたことに対して、体系的に問題と解決を捉えられるようになったのが最大の収穫です。