今川館

都内勤務の地味OLです

Pythonでサロゲートペア -- ほっけの逆襲

Pythonサロゲートペアを処理するとどうなるか

http://qiita.com/YusukeHirao/items/2f0fb8d5bbb981101be0

既に他の記事で上記記事に登場する「𩸽」(ほっけ)というサロゲートペアの文字をGoで扱う話しを書いたが、Pythonで処理するとどうなるのか試してみた。

Python2は寛容

Python2は3に比べて寛容である。printに渡しても取り敢えずエラーにならず表示される。

>>> print u'\uD867\uDE3D'
𩸽
>>> x = u'\uD867\uDE3D'
>>> y = u'𩸽'

但し、表示される文字は同じだが、文字列比較では同値ではないと評価される

>>> x == y
False

更に不思議なことに、こちらはordが結果を返すのに

>>> ord(y)
171581

こちらはエラーになってしまう
>>> ord(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: ord() expected a character, but string of length 2 found


http://docs.python.jp/2/library/functions.html#ord

ord(c)(原文)

長さ 1 の与えられた文字列に対し、その文字列が unicode オブジェクトならば Unicode コードポイントを表す整数を、 8 ビット文字列ならばそのバイトの値を返します。たとえば、 ord('a') は整数 97 を返し、 ord(u'\u2020') は 8224 を返します。この値は 8 ビット文字列に対する chr() の逆であり、 unicode オブジェクトに対する unichr() の逆です。引数が unicodePython が UCS2 Unicode 対応版ならば、その文字のコードポイントは両端を含めて [0..65535] の範囲に入っていなければなりません。この範囲から外れると文字列の長さが 2 になり、 TypeError が送出されることになります。

サロゲートペアはlenで長さを調べると、「2」が返却される。
unicode型をlenに渡しているので「文字数」を取得するはずが、「2」を返す点が問題。
 str型をlenに渡すとバイト数を返すのでここでの問題対象外。

>>> len(x)
2
>>> len(y)
1

参考:

>>> len(u'あ')  # unicode型: 文字数
1
>>> len('あ')  # str型: バイト数
3

Unicode番号から文字を生成する」

当然、chrに渡すと範囲外なのでエラー。

>>> chr(0x29E3D)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: chr() arg not in range(256

unichrは正しく値を返してくれる。

>>> unichr(0x29E3D)
u'\U00029e3d'
>>> print unichr(0x29E3D)
𩸽

しかも、他の文字を試すとUbuntu16のVMでは化けて表示されるが、Macだとエラーになった。

★Ubuntu16.04のVM

f:id:imagawa_yakata:20161216105738p:plain

★El CapitalのMac

f:id:imagawa_yakata:20161216105742p:plain

El CapitanのMacでダメなのは、Pythonのビルドの条件のせいらしい。(以下)

Pythonでサロゲートペアの範囲の数値実体参照を文字に戻す - 西尾泰和のはてなダイアリー

Python3だと扱いが難しくなる

Python3ではそもそもu'\uD867\uDE3D'をprintに渡すとエラーになる。

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'utf-8' codec can't encode character '\ud867' in position 0: surrogates not allowed

Python3は、最初からサロゲートペアを許さないポリシーのようだ。
そのため、'surrogatepass', 'suroogateescape'といったエラーオプションを指定して何とか解決する。

具体的には以下stackoverflowの回答の通り、UTF-16サロゲートペアを許可した状態で一度byteに変換し、strに戻すと大丈夫らしい。

How to work with surrogate pairs in Python? - Stack Overflow

>>> "\ud867\ude3d".encode('utf-16', 'surrogatepass').decode('utf-16')
'𩸽'
|<

ちなみに、Unicodeコードポイントでリテラル表記してあげれば大丈夫である。

>|text|
>>> print('\U00029e3d')
𩸽

サロゲートペアの入った文字列を処理する場合の注意点

サロゲートペア

「𩸽のひらきを居酒屋で注文して、1時間経つがまだ来ない。𠈻な客が店員を引き止めてなじるからだ。」

ほっけの ひらきを いざかやで ちゅうもんして、いちじかん たつが まだこない。
ぞくな きゃくが てんいんを ひきとめて なじる からだ。

年末の東京らしい風景を彷彿とさせるこの一文にはサロゲートペアの文字がふたつ含まれている。

ひとつは「𩸽」という文字
もうひとつは「𠈻」という文字である。

JavaScriptでのサロゲートペア文字列のメモ - Qiita
この記事に詳しく書かれているが、サロゲートペアは通常の文字列処理が通用しない厄介者である。

Goの文字列の扱い

Goでは文字をruneという型で処理する決まりらしい。
いつもの『プログラミング言語Go』の記述を引用すると、

Unicodeは世界のすべての書記体系のすべての文字、アクセントや他の発音区別記号、タブやキャリッジリターンなどの制御コード、多数の難解な文字を集めており、各文字にUnicodeコードポイント(Unicode code point)あるいはGo用語ではルーン(rune)と呼ばれる規格番号を割り当てています。

つまり、文字をruneとして取り出すと、番号で管理するそうだ。

実際、「ほっけ」と「ぞく」の文字をruneのリテラルで表すと、

package main

import "fmt"

func main() {
	var hokke, zok rune

	hokke, zok = '\U00029E3D', '\U0002023B'
	fmt.Printf("%c, %c", hokke, zok) // 𩸽, 𠈻
}

このように、16進数の番号を書く。

stringをrangeに渡すと[]runeに置き換えてくれる

Goでは文字列をrangeに渡すとちゃんとruneのスライスに置き換えてくれる。
このため、「一文字ずつ」ループを当たり前のようにできる。

exp := "𩸽のひらきを居酒屋で注文して、1時間経つがまだ来ない。𠈻な客が店員を引き止めてなじるからだ。"

for _, x := range exp {
	fmt.Printf("%c", x)
}
fmt.Println()

これで変数expに書いた通りの文章がコンソールに出力される。

もしこれをbyteのスライスにかえてしまうと都合が悪い。

exp := "𩸽のひらきを居酒屋で注文して、1時間経つがまだ来ない。𠈻な客が店員を引き止めてなじるからだ。" // 46文字

for _, x := range []byte(exp) {
	fmt.Printf("%c", x)
}

こっちを動かすと、画像のようにめちゃくちゃな出力になってしまう。

f:id:imagawa_yakata:20161216144357p:plain

utf8.DecodeRuneを使うとrange同様、安全に文字を取り出せる。

// import "unicode/utf8"

exp := "𩸽のひらきを居酒屋で注文して、1時間経つがまだ来ない。𠈻な客が店員を引き止めてなじるからだ。"
var xs []byte

xs = []byte(exp)
for len(xs) > 0 {
	r, size := utf8.DecodeRune(xs)
	fmt.Printf("%c", r)
	xs = xs[size:]
}
fmt.Println()

lenで文字数を数えたいか、バイト数を数えたいか

冒頭の文は全部で46文字あるが、単純に文字列をlenで調べてしまうとバイト数を返す。
これは、Pythonでlen(unicode)は文字数、len(str)はバイト数を返す話と似ている。

正しく文字数を取得したい場合は[]runeにキャストしてlenに渡すか、utf8.RuneCountInStringを使う。

exp := "𩸽のひらきを居酒屋で注文して、1時間経つがまだ来ない。𠈻な客が店員を引き止めてなじるからだ。" // 46文字

fmt.Println(exp)
fmt.Println(
	len(exp),                    // 138
	utf8.RuneCountInString(exp), // 46
	len([]rune(exp)))            // 46

サンプルコード

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	exp := "𩸽のひらきを居酒屋で注文して、1時間経つがまだ来ない。𠈻な客が店員を引き止めてなじるからだ。" // 46文字

	fmt.Println(exp)
	fmt.Println(
		len(exp),                    // 138
		utf8.RuneCountInString(exp), // 46
		len([]rune(exp)))            // 46

	for _, x := range exp {
		fmt.Printf("%c", x)
	}
	fmt.Println() // ここで改行を挟まないと何故か1回めのループの「𩸽」の文字が化けて表示される。

	var xs []byte

	xs = []byte(exp)
	for len(xs) > 0 {
		r, size := utf8.DecodeRune(xs)
		fmt.Printf("%c", r)
		xs = xs[size:]
	}
	fmt.Println()

	// DecodeLastRuneだと逆から反復
	xs = []byte(exp)
	for len(xs) > 0 {
		r, size := utf8.DecodeLastRune(xs)
		fmt.Printf("%c", r)
		xs = xs[:len(xs)-size]
	}
}

go installを使え -- QCon London 2016

2016年版Goのベストプラクティス

日本語訳(POSTD)
6年間におけるGoのベストプラクティス | プログラミング | POSTD

原著
Go best practices, six years in

こんな記事を見つけて読んでいた。

内容は15項目にもわたるプラクティスの紹介なのだが、今回、特に興味を持ったのが以下だった。

1. $GOPATH/binを$PATHに追加しましょう。インストールしたバイナリが扱いやすくなります。
2. リポジトリfooが主にバイナリ用なら、ライブラリコードはサブディレクトリlib/に置き、fooパッケージと名付けましょう。
3. リポジトリが主にライブラリ用なら、バイナリはcmd/配下の個別のサブディレクトリに置きましょう。
15. go buildよりもgo installを使いましょう。

GOPATH/binにPATHを通し、go installでビルドする

まず、15番の「$GOPATH/binを$PATHに追加 & go buildよりもgo installを使いましょう」という話に興味を持った。

今まで「go build」を実行するとバイナリが生成されることは知っていたが、「go install」というコマンドの存在を知らなかった。
「go install」を実行すると$GOPATH/binの下にバイナリが配置されるので、PATHを通しておけばいつでも実行できて便利ですよ、という意図らしい。

リポジトリ構成はgo getに対応を心がける

以下、バイナリとライブラリを作る場合でリポジトリ構成を変えた方が良いらしい。

[引用] バイナリを作る場合

github.com/peterbourgon/foo/
    main.go      // package main
    main_test.go // package main
    lib/
        foo.go      // package foo
        foo_test.go // package foo

[引用] ライブラリを作る場合

github.com/peterbourgon/foo
    foo.go      // package foo
    foo_test.go // package foo
    cmd/
        foo/
            main.go      // package main
            main_test.go // package main

この構成案は既に過去の記事で調べていたが、出典はこのカンファレンスだったのかという感想。

go getに対応したリポジトリ構成に、go installを加えることでシンプルに開発環境を構築することができそうだ。

go installを使えば、bin/の下にバイナリが配置されるので、go buildで生成したバイナリを誤ってコミットしてしまうミスも減ると思った。

ラベル付きbreak, continue, goto

Goのプログラムを見ていて、何年ぶりか分からないくらい久しぶりにラベル付きbreakを見かけた。

Goプログラミング言語仕様 - golang.jp

ラベル付きステートメント

ラベル付きステートメントは、goto、break、continueステートメントの宛先となります。

この通り、break, continueの他に、gotoにも使えるらしい。(つまりgoto文をGoは許している)

試してみた

ラベルは自分がいるスコープの外に定義されているものしか参照できない模様。
試しに以下のサンプルコードを書いて動かしてみた。

package main

import (
	"fmt"
	"time"
)

func main() {
L:
	for i := 1; true; i++ {

		for j := 1; true; j++ {

			fmt.Printf("i=%v> j=%v\n", i, j)

			if i == 2 && j == 2 {
				continue L
			}

			if i%2 != 0 && j == 3 {
				goto L2
			}
			if j == 3 {
				break L
			}

			time.Sleep(200 * time.Millisecond)
		}

	L2:
	}
}

自分でも挙動が読めない中、試行錯誤した結果、上記プログラムを動かすと以下の出力が得られる。

出力内容

i=1> j=1
i=1> j=2
i=1> j=3 // ここで goto L2 の条件にマッチしてjのループを抜ける
i=2> j=1
i=2> j=2 // ここで continue L の条件にマッチしてjのループを抜ける
i=3> j=1
i=3> j=2
i=3> j=3
i=4> j=1
i=4> j=2
i=4> j=3 // ここで break L の条件にマッチしてiのループを抜ける

当然これはやっておくでしょう

goto出てきたら当然コレはやっとくでしょう。

package main

import (
	"fmt"
	"strings"
	"time"
)

var ora = []string{
	"ハァ テレビもねぇ! ラジオもねぇ!",
	"クルマもそれほど走ってねぇ!",
	"ピアノもねぇ! バーもねぇ! おまわり毎日ぐーるぐる!",
}

const delay = time.Duration(750 * time.Millisecond)

func Sing(message string) {
	xs := strings.Split(message, " ")
	length := len(xs)
	for i, x := range xs {
		fmt.Print(x)
		if i+1 < length {
			fmt.Print(" ")
			time.Sleep(delay)
		}
	}
	time.Sleep(delay)
	fmt.Println()
}

func main() {

	for i := 0; true; i++ {

		Sing(ora[i])

		if i >= 2 {
			Sing("おらこんな村いやだぁ〜 おらこんな村いやだぁ〜")
			Sing("東京へ出るだぁ〜〜〜")
			goto Tokyo
		}
	}

Tokyo:

	for i := 0; true; i++ {
		Sing("東京へ出だな〜ら 銭コア貯めでぇ 東京でベコ飼うだぇ〜〜")
		break Tokyo
	}
}

最終的にここまで調整しました。

runtime.MemStatsでメモリ使用量を調べる&pprofモジュール

ポインタのことを理解するためにメモリの使用量を調べながら色々試そうと思ったが、どうやってやるのか分からないので調べた。

Understanding Go Lang Memory Usage · deferpanic eng blog

ここに詳しく書いてある。

  • 単にメモリの使用量を調べるだけならruntime.MemStatsで判る。
  • pprofモジュールを使うとtopコマンドみたいな詳細な出力を簡単に得られる。

runtime.MemStatsでメモリ使用量を調べる

サンプルコードとして「StringBuffer」というクラスを作って、Appendのメソッドチェーンでどんどん文字列を書き足していくプログラムを書いた。
そして処理前後でMemStatsから得られる情報を見比べて、使用量メモリが増えていることを確かめる。

package main

import (
	"bytes"
	"fmt"
	"runtime"
)

type StringBuffer struct {
	buf  bytes.Buffer
	size int
	err  error
}

func (s *StringBuffer) Append(text string) *StringBuffer {
	if s.err != nil {
		return s // Nothing to do.
	}
	size, err := s.buf.WriteString(text)
	s.size += size
	s.err = err
	return s
}

func (s *StringBuffer) String() string {
	return s.buf.String()
}

func NewBuffer() StringBuffer {
	return StringBuffer{}
}

const LF = "\n"

var mem runtime.MemStats

func PrintMemory() {
	runtime.ReadMemStats(&mem)
	fmt.Println(mem.Alloc, mem.TotalAlloc, mem.HeapAlloc, mem.HeapSys)
}

func main() {
	PrintMemory() // 47504 47504 47504 884736

	buf := NewBuffer()

	for i := 0; i < 25000; i++ {
		buf.Append("Hello").Append(", ").Append("world.")
		buf.Append(LF).Append("I have a ").Append("pen.")
	}
	fmt.Println(buf.size, buf.err) // 675000 <nil>

	PrintMemory() // 2348200 2348200 2348200 3112960
}

出力内容

48336 48336 48336 884736
675000 <nil>
2349032 2349032 2349032 3112960

確かに増えている。

MemStatsで得られる情報は、ドキュメントのMemStatsTypeの項に列挙されているが数が多いので今回使ったものだけ引用。

runtime パッケージ - golang.jp

// 一般統計
// 更新時にロックはされないため、近似値です。
Alloc uint64 // アロケートされ、使用中のバイト数
TotalAlloc uint64 // アロケート済みのバイト数(解放された数も含む)

// 主要なヒープ割り当て統計
HeapAlloc uint64 // ロケートされ、使用中のバイト数
HeapSys uint64 // システムから取得したバイト数

だそうです。

書き込み用のファイルライクオブジェクトはbytes.Buffer

読み取り専用ファイルライクオブジェクトはstrings.NewReaderとbytes.NewReaderの両方が用意されているが、書き込み用はなぜかbytes.Bufferしか無かったのでこれを使った。

文字列を書き込む「WriteString」メソッド、byteを書き込む「WriteByte」、byteのスライスを書き込む「Write」などのメソッドがあるので、今回はWriteStringを使った。

pprofモジュールを使うとより視覚的なビューを利用できる

更に、pprofモジュールを使うとLinuxのtopコマンドみたいな詳細な出力を得られるらしい。

golangで書かれたプログラムのメモリ使用状況を見る - hakobe-blog ♨
Understanding Go Lang Memory Usage · deferpanic eng blog

ふたつの記事を参考に試してみた。

[foo/main.go]

package main

import (
	"log"
	"net/http"
	_ "net/http/pprof"
	"sync"
)

func main() {
	var wg sync.WaitGroup

	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()

	wg.Add(1)
	wg.Wait()
}
$ cd foo
$ go build -o cmd
$ cmd  # 待機

ブラウザで所定のURLを問い合わせると詳細な情報が出力される。

f:id:imagawa_yakata:20161213140635p:plain

sync.WaitGroupとは?

これは、ひとつ以上のgoroutineを予約語goを使って動かして、すべてが終了するまで待ちたいときに使うものらしい。

sync - The Go Programming Language

type WaitGroup

A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. Then each of the goroutines runs and calls Done when finished.

Big Sky :: golang の sync パッケージの使い方

sync.WaitGroup は Wait() を呼び出すと Add() を呼び出した回数から Done() を呼び出した回数を引いて 0 になるまで待機する機能が簡単に実装出来ます。全ての goroutine の終了を待つ場合に使用します。

サンプルコードでは、Add(1)してDoneを一切呼ばないので、ずっと待つことになる。

ポインタが分からない③

やっと分かってきた感

まだポインタのことで悩んでいたのかと言われても仕方ないが、以下の記事を読んでいてやっと分かってきた感じがする。

C/C++は永久に不滅です! - Part4 誰もがつまずくポインタを完璧理解:ITpro

この中の「図3●int *p =&a;を実行したときのメモリーの状態」という図を見て、気づいたことが、

p := 10
q := &p
*q = 7

これがコンパイルエラーにならないんじゃないかということ。
実際ならなくて、3行めの文によってqの大元の変数pに7という別の数を代入したことになる。
間接参照演算子「*」を左辺に持ってきてはいけないと思いこんでいたことに気づく。

サンプルコード

package main

import "fmt"

type Point struct {
	x int
	y int
}

func main() {
	p := Point{0, 0}
	q := &p
	r := *q // rにはqの大元の変数p(の値)を保持しておく。rとpは別のメモリが割り当てられる。

	fmt.Printf("p: %p q: %p  r:%p\n", &p, q, &r) // p: 0xc82000a320 q: 0xc82000a320  r:0xc82000a330

	p.x = 8
	fmt.Println(p, q, r) // {3 10} &{3 10} {0 0}

	// ここで、qの大元の変数に新しいPointのオブジェクトを入れる。
	// -> pに代入しているのと同じで、なおかつ、rには影響しない。
	*q = Point{3, 3} // 左辺に間接参照演算子「*」を持ってこられる点に驚き。

	p.y += 7

	fmt.Println(p, q, r) // {3 10} &{3 10} {0 0}
}

わかったこと

  1. 変数には、(普通の)変数とポインタ変数の2種類ある。
  2. 変数に変数を代入した場合は別々のメモリが割り振られる。 -> r = p としたら、pとrは別物。但し値がコピーされる。
  3. ポインタ変数を作って変数のアドレスを代入すると、同じものを指す。 -> q = &p としたら、pとqは同じもの。
  4. 間接参照演算子「*」を使うと、ポインタ変数の大元の変数にアクセスできる。これは参照も代入も可能。

ポインタが分からない②

ポインタのことがやっぱり分からない。

どうしても納得いかないので、もうちょっと良く考えてみることにした。

以下のサンプルコードを動かして色々試してみると、どうも難しく考えるからいけないんだと思うようになった。

単なるルールとして、「ポインタを使うと同じものを指す」とだけ覚えておけば別に困らない気がしてきた。

package main

import (
	"fmt"
)

type Point struct {
	x int
	y int
}

func main() {
	var p = &Point{10, 20}
	var P = Point{3, 6}

	// これは、qがpそのものであることを直感的に示している。
	q := p

	// こちらも、(PとQは別物であることを前提に)、QがPそのものを指し示すよう指示しているように見える。
	Q := &P

	// これは、rとpが異なるものであることを直感的に示している。
	r := *p

	// ところが、これが、R !== P であることがわからない。
	R := P

	// つまり、この式でR === Pであるという前提を暗に抱いているといえる。
	// よって、「ポインタを代入しない限り、別物」から考え始めるのが良い。

	// sにはqのポインタを代入するので、s === q であることに疑いはない。
	s := &q

	// これは、p自体を操作しているようにしか見えない。
	(*p).x = 12

	S := *p; S.x = -100 // これをやってもpは一切影響を受けない。

	// これもまた、P自体を操作しているようにしか見えない。
	(&P).x = 45

	r.y = 17 // rにはポインタを取らなかったので、ここでpは影響を受けない。

	fmt.Println(p, q, r, *s) // &{12 20} &{12 20} {10 17} &{12 20}
	fmt.Println(P, Q, R)     // {45 6} &{45 6} {3 6}
}

どうもわたしが理解できていなかったことは、

  • &や*を付けると同じものや違うものを指すと誤解していた
  • そうではなくて、代入すると別々に分けられるタイミングが来るようだ
  • しかし、アドレス演算子を使ってポインタを代入すると、左辺と右辺が同一であることを示せる
  • (*p)や(&P)を使ってある変数そのものを操作できることから、やっぱり演算子自体にオブジェクトを分別する役割は無くて、代入に起因するとしか思えない。

ここまで考えるうちにつらくなってきて、要は「ポインタを使うと同じものを指す」とだけ覚えておけば良いんじゃないかと思うようになった。

元々、変数をポインタ型で宣言してしまうと、今度は「それ自身」を取り出す術がなくなるので、「*」という演算子が用意されている、と。

(まだよくわかってない・・)