Go と日本語
Twitter でちょろんと書いたが,なんか勝手に NFC 正規化されてしまうみたいだし,このブログではもう少し掘り下げて書いてみる。
Go では型名や関数名や変数名といった識別名(identifiers name)として Unicode 文字が使える。 ということは名前を日本語にすることも可能である。 まぁ,どんな文字でも使えるわけではないが。
たとえば以下のようなコードを考える。
// +build run
package main
type ペンギン struct{}
func (p *ペンギン) 鳴く() string {
return "ふるるー!"
}
func main() {
println((&ペンギン{}).鳴く())
}
型名や関数名に Unicode 文字が使えるなら上のコードはうまく動きそうだが,実際にはコンパイルエラーになる。
$ go run sample1.go
# command-line-arguments
./sample.go:12:15: undefined: ペンギン
./sample.go:12:18: invalid character U+309A '゚' in identifier
./sample.go:12:27: invalid character U+3099 '゙' in identifier
コンパイルエラーになる理由は2つ。
type
で宣言した型名とmain()
関数内で使われている型名が異なる「文字列」である- 半濁点および濁点を表す結合文字
U+309A
およびU+3099
は識別名として使えない
なんだか面倒くさそうである。
識別名として使える文字
Go の仕様書 を読むと識別名として使える文字が示されている。
このうち letter
は
と定義され Unicode 文字が使えることを示している。
unicode_digit
や unicode_letter
を含む文字の定義は以下の通り。
unicode_digit
は全角数字も含んでいる点に注意。
改行コードも LF なんだねぇ(Windows の CRLF は早く捨てたい)。
では Letter や Number の文字種が具体的にどの文字を指しているのか。
幸い unicode
標準パッケージを使って文字種をチェックできるので,簡単なチェッカーを書いてみよう。
// +build run
package main
import (
"flag"
"fmt"
"unicode"
)
var mapRangeTable = map[*unicode.RangeTable]string{
unicode.Variation_Selector: "Variation Selector",
unicode.Sc: "Symbol/currency",
unicode.Sk: "Symbol/modifier",
unicode.Sm: "Symbol/math",
unicode.Radical: "Radical",
unicode.So: "Symbol/other",
unicode.Lm: "Letter/modifier",
unicode.Ideographic: "Ideographic",
unicode.Lo: "Letter/other",
unicode.Nl: "Number/letter",
unicode.No: "Number/other",
unicode.Mc: "Mark/spacing combining",
unicode.Me: "Mark/enclosing",
unicode.Mn: "Mark/nonspacing",
unicode.Pc: "Punctuation/connector",
unicode.Pd: "Punctuation/dash",
unicode.Pe: "Punctuation/close",
unicode.Pf: "Punctuation/final quote",
unicode.Pi: "Punctuation/initial quote",
unicode.Ps: "Punctuation/open",
unicode.Po: "Punctuation/other",
unicode.Zl: "Separator/line",
unicode.Zp: "Separator/paragraph",
unicode.Zs: "Separator/space",
unicode.Join_Control: "Join Control",
unicode.Cc: "Control/control",
unicode.Cf: "Control/format",
unicode.Cs: "Control/surrogate",
unicode.Co: "Control/private use",
}
var arryRangeTable = []*unicode.RangeTable{
unicode.Variation_Selector,
unicode.Sc,
unicode.Sk,
unicode.Sm,
unicode.Radical,
unicode.So,
unicode.Lm,
unicode.Ideographic,
unicode.Lo,
unicode.Nl,
unicode.No,
unicode.Mc,
unicode.Me,
unicode.Mn,
unicode.Pc,
unicode.Pd,
unicode.Pe,
unicode.Pf,
unicode.Pi,
unicode.Ps,
unicode.Po,
unicode.Zl,
unicode.Zp,
unicode.Zs,
unicode.Join_Control,
unicode.Cc,
unicode.Cf,
unicode.Cs,
unicode.Co,
}
type unicodeChecker struct {
mapRangeTable map[*unicode.RangeTable]string
arryRangeTable []*unicode.RangeTable
}
func newChecker() *unicodeChecker {
return &unicodeChecker{mapRangeTable: mapRangeTable, arryRangeTable: arryRangeTable}
}
func (c *unicodeChecker) string(rt *unicode.RangeTable) string {
if c == nil || rt == nil {
return "Unknown"
}
if s, ok := c.mapRangeTable[rt]; ok {
return s
}
return "Unknown"
}
func (c *unicodeChecker) check(r rune) string {
if c == nil {
return c.string(nil)
}
for _, rt := range c.arryRangeTable {
if unicode.Is(rt, r) {
return c.string(rt)
}
}
return c.string(nil)
}
func main() {
flag.Parse()
args := flag.Args()
checker := newChecker()
for _, arg := range args {
fmt.Println(arg)
for i, r := range arg {
switch {
case unicode.IsLetter(r), r == '_':
fmt.Printf("\t%#U : OK : letter\n", r)
case unicode.IsNumber(r):
okng := "OK"
if i == 0 {
okng = "NG"
}
fmt.Printf("\t%#U : %v : unicode_digit\n", r, okng)
default:
fmt.Printf("\t%#U : NG : %v\n", r, checker.check(r))
}
}
}
}
ここで unicodeChecker.check()
メソッドは「Unicode 文字種の判別」で書いた check()
関数の改良版で,かなり細かくチェックできるようにしている。
実際に動かしてみよう。
$ go run sample2.go ペンギン ペンギン
ペンギン
U+30DA 'ペ' : OK : letter
U+30F3 'ン' : OK : letter
U+30AE 'ギ' : OK : letter
U+30F3 'ン' : OK : letter
ペンギン
U+30D8 'ヘ' : OK : letter
U+309A '゚' : NG : Mark/nonspacing
U+30F3 'ン' : OK : letter
U+30AD 'キ' : OK : letter
U+3099 '゙' : NG : Mark/nonspacing
U+30F3 'ン' : OK : letter
うんうん。
ちなみに絵文字は,複数のコードポイントで構成されている場合は当然として,単一のコードポイントでも letter
とは見なされないらしい。
$ go run sample2.go ⌛
⌛
U+231B '⌛' : NG : Symbol/other
あと康熙部首とかもアカン感じ?
$ go run sample2.go 埼⽟ 埼玉
埼⽟
U+57FC '埼' : OK : letter
U+2F5F '⽟' : NG : Radical
埼玉
U+57FC '埼' : OK : letter
U+7389 '玉' : OK : letter
やっぱ英語にしとこうや
どこで見たのか失念してしまったが,メジャーなプログラミング言語,特に手続き型の言語は英語の文法や語感が前提になっていて,日本人はそこから既にハンデがあると読んだことがある。
そういう言語体系の中に日本語を混ぜ込むと,日本語が刷り込まれている人ほど奇妙に見えてしまうんじゃないだろうか。 だから私のように英語不得手でも,プログラム・コードは「英語」で書くのが一番自然だろう。
参考図書
- プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- Alan A.A. Donovan (著), Brian W. Kernighan (著), 柴田 芳樹 (翻訳)
- 丸善出版 2016-06-20
- 単行本(ソフトカバー)
- 4621300253 (ASIN), 9784621300251 (EAN), 4621300253 (ISBN), 9784621300251 (ISBN)
- 評価
著者のひとりは(あの「バイブル」とも呼ばれる)通称 “K&R” の K のほうである。この本は Go 言語の教科書と言ってもいいだろう。
- with #前置詞といっしょ!: 電子書籍版
- 三戸 雅彦 (著), 木村 ヨーシロー (写真)
- 2017-05-07 (Release 2017-05-07)
- Kindle版
- B07143DX2F (ASIN)
- 評価
対象年齢は小学3年生から。“with” をはじめとしたよく使われる10個の前置詞についてふんわりとしたイメージを掴むことができる。中学生の時に読みたかった orz
- coffee #名詞のヒミツ!~英語を楽しく学ぼう~
- 三戸雅彦 (著)
- しおまち書房 2018-01-01
- 単行本(ソフトカバー)
- 4906985165 (ASIN), 9784906985166 (EAN), 4906985165 (ISBN)
- 評価
同じ著者による『with #前置詞といっしょ!』の続編と言えるもの。可算名詞や不可算名詞,単数形と複数形で意味が変わるもの,冠詞の使い方,といったことを言葉のイメージを中心に解説している。中学生の時に読みたかった orz
- 中学→高校の英文法が図解でわかるノート 基礎編 #動詞と踏み出そう!
- 三戸 雅彦 (著)
- しおまち書房 2020-01-01
- 単行本(ソフトカバー)
- 4906985262 (ASIN), 9784906985265 (EAN), 4906985262 (ISBN)
- 評価
同じ著者による『with #前置詞といっしょ!』『coffee #名詞のヒミツ!』の続編と言えるが対象年齢はもう少し「お兄さん・お姉さん」向けの内容。動詞を中心に授業のノートのような紙面構成で書かれていて分かりやすい。勉強中。