/home/by-natures/dev*

データ界隈で働くエンジニアとしての技術的なメモと、たまに普通の日記。

2018/11/14 正規表現でグループ化した文字列の取得

中国人の友達と日本語・中国語を教え合っているのですが、中国人にとっては中国語に近い熟語のほうが使いやすいようです。「それ錯誤です」と言われて気づきました。「間違う」は日本語としての変化があるので面倒だけれど、「錯誤」のような熟語なら音読みだけ覚えれば(中国語にその熟語が存在していれば)使えるので手っ取り早いみたいです。

もちろん違う点も多いので学ばないといけないですが、お互いの母国語にちょっとした共通点を見つけると面白くて、学ぶ意欲につながっています。

グループ化した文字列を取得する

Go言語でグループ化した文字列を取得したかったのですが、ちょっと探すのに手間取ったのでメモ代わりに。

nisenabe.hatenablog.com

FindSubmatch でグループ化した文字列が取得できます。ただ引数は []byte, 返り値は [][]byte なので少々面倒です。引数は []byte(string_var), 返り値は string() などで文字列に変換し直します。

stackoverflow.com

2018/11/08 SlackAPI, 三項演算子, goto文

飾り棚を少しずつ冬っぽくしようと、昨日夜飾り付けをしていました。マリボウルはクリアしか持っていないのですが、火をつけると雰囲気が華やかで良いですね。サルパネヴァプレートに写る様子もとてもきらびやか。

f:id:bynatures:20181108115653j:plain

Slack の API を呼ぶ

Slack の API を通じて、チャネルに通知を飛ばしたかったので調査。すごい簡単でした。昔 Twitter の API に投稿しようとして面倒だった記憶があったのですが、Slack は Webhook URL を取得したらほぼ完了です。あとは JSON を POST するだけでした。

crossbridge-lab.hatenablog.com

三項演算子がない

Go言語には三項演算子(tenary operator)がないので、プログラムが冗長になる場面が多いです。思想として、書きやすさよりも(他のプログラマに対して)読みやすさを重視しているから…ということなのですが。

stackoverflow.com

それでも三項演算子があれば

int index = val > 0 ? val : -val

と書けるはずが

var index int
if val > 0 {
    index = val
} else {
    index = -val
}

では冗長すぎないかということで上の StackOverflow の質問が投稿されていました。確かに上のプログラムだとかなり読みづらいですが、初期値を与えて else 節を消してしまえば読みやすくなると。

index := val
if val <= 0 {
    index = -val
}

確かにこれなら、どちらが主な値かを示すこともできるし読みやすいです。納得しつつ、でもプログラムを書いて、やっぱり三項演算子欲しいなぁと思うことを繰り返す昨今。。

goto 文を使ってみる

エラー値を受け取って if 文でリターンという処理が何度も続いたので、エラー処理をまとめるために goto 文を使ってみることにしました。ただ、ラベルを貼った移動先にスキップする行の中で、今まで出てきていない変数が新たに宣言されている(スコープに変数が新しく加わる)場合は構文エラーとなります。

cmd/compile: spurious "goto label jumps over declaration" - 2 · Issue #22101 · golang/go · GitHub

この X は goto とラベル宣言の間にいるのでエラーとなります:

package main
func main() {
    goto label
    X := 0
label:
}

変数スコープをややこくしないために、または実装上の都合かもしれませんが、エラー処理をまとめたいといった場合は少々面倒です。変数宣言を関数先頭にまとめてしまうか、新たにブロックを作って以下のようにすることで解決できました:

func main() {
    goto label
    {
        X := 0
        // X を使った処理...
        // もちろん中でも goto できる
    }
label:
}

2018/11/06[2] オブジェクト初期化の引数が多くなってきた時の対処法

オブジェクトのフィールドが増えると初期化する時の手間も増えてきてしまい、Java ならビルダパターンなどで必須パラメータと任意パラメータをうまく分けられたのですが、Go言語ではどうしているのか検索しました:

stackoverflow.com

ベストアンサーではないけれど UpVote が一番ついているものを見てみると、functional options というアイデアがあって、可変長引数でメソッドを渡して、その中で任意パラメータを処理する方法です。詳しくは以下のブログで紹介されています。

Functional options for friendly APIs | Dave Cheney

例えば id のみ必須パラメータであとは任意にしたい場合は以下のように書けます。最後の可変長引数はメソッドであれば何でも渡せてしまうので、そのオブジェクト(上なら Foo)のフィールドは外部パッケージからは非公開になるようにしておいた方がよさそうです。

func NewFoo(id string, options ...func(*Foo)) Foo {
        foo := Foo{id: id}
        for _, option := range options {
                option(&foo)
        }
        return foo
}

2018/11/06 日付処理

週末に TOEIC の S&W(Speaking and Writing)を受けてきました。2回目か3回目なのですが数年ぶりだったのと、対策する時間があまりなかったのでスピーキングがしどろもどろに。L&R は日々の勉強でよい点数が取れたのですが、スピーキングはやはり試験対策しないとダメそうです。ちょっと試験料が高いのでまたしばらく時間を空けて受けようと思います。

スピーキングは DMM 英会話で少しずつ勉強しています。この前やっと8000分を超えました。130時間ちょっとですかね。仕事中にあまり口を動かす職業ではないので、30分自分だけが主に喋る時間って楽しいですし精神的にも良いです。レッスンが終わるといつもすっきりした気分です。外国の先生なのでプライベートな相談をしても安心だし、世界各国の色々な話も聞けるのもよいです。語彙を増やすためにあえて普段興味を持たないトピックを選んだりして、知識の幅も広がっていると思います。

昔、言語を学ぶのって明確な目標がないといけないと思っていて、大学で3年フランス語を勉強したのですが途中でやめてしまいました。理系大学で3年も第二外国語をやる学生なんてほとんどおらず、周りはフランス語圏に留学したい人しか残っていなかったので、最初は楽しかったのにだんだんと肩身が狭くなってしまいました。でも就職してからは自分なりの方法を考えながら勉強できるので楽しいです。

話は変わりますが、TOEIC 900点並の翻訳機なんて話題が今日出てましたね:

www.asahi.com

翻訳機が便利なのは間違いないんですが、スマートフォンで Google が翻訳してくれるものでも十分便利だし、教師データを考えても Google には敵わない気もするので政府が予算を使ってまですることかというと疑問です。オリンピックについて言及されているので、日本国内での特定用途特化したデバイスを目指しているんでしょうか。

Go言語での時刻処理

stackoverflow.com

time パッケージがとても便利なのですが、オブジェクトに「分」を表す int フィールドがあって、その Duration オブジェクトを作りたいときに少し手間取りました。 intValue * time.Minute だと int Duration の間に乗算が定義されていないのでエラーになってしまうので、 time.Duration(intValue) * time.Minute として DurationDuration の乗算にすることで解決できました。

3 * time.Minute のような数字がリテラルの場合は変数型が決まっていない状態なのでコンパイルできるのですが、一度変数に入れてしまうと型が定まってしまうので、暗黙的な型変換になれていると戸惑います。

2018/11/01 クロスコンパイルとリリース

ハロウィンについてフィリピン人の英会話の先生と話をしていたら、フィリピンは世界でもっともクリスマスを長く祝う国?らしく、9月1日からクリスマスのデコレーションをするのだとか。ハロウィンもハロウィンで仮装して騒いで楽しむらしく、10月末はハロウィンとクリスマスが混在するようです。

11月からクリスマスなんて早いなぁと思っていましたが、それよりはるかに長い期間クリスマスを祝う国があると知っておどろきました。日本のお正月準備はクリスマスの後ばたばたと始まるので、フィリピンのようにイベントを重ねてやったらどうかなと一瞬考えましたが、和と洋で頭がおかしくなりそうなのでハロウィン・クリスマスほどは上手く行かなそうです。。

リリースの仕方

リリースの仕方について調べていたところ、以下のブログ記事がとても役に立ちました。

golang勉強会でGo製ツールの配布方法について話してきた | SOTA

Go言語ではクロスコンパイルしてさまざまなプラットフォーム向けのバイナリファイルを生成できるのですが、それをまとめて行える gox コマンド、そしてそれを GitHub へリリースする ghr コマンドなどが紹介されています。

speakerdeck.com

gox コマンドは -output オプションで出力ファイル名を指定できます。出力ディレクトリを指定するわけではないので、例えば以下のようにすると dist/ ディレクトリ以下に成果物が出力されます:

$ gox -os "linux" -output="dist/{{.Dir}}_{{.OS}}_{{.Arch}}" ./...

ghr コマンドはパブリックな GitHub を想定しており、GitHub EnterPrise(GHE) で利用する場合には環境変数を利用する必要がありました:

$ GITHUB_API=https://<GHEのドメイン>/api/v3/ GITHUB_TOKEN=<トークン> ghr <バージョン> <リリース対象ディレクトリ>

トークンは以下の公式ページで紹介されているやり方で、GUI からトークン生成可能です:

Creating a personal access token for the command line - User Documentation

2018/10/31 ステージング・本番など実行環境によって変数設定する

昨日最寄駅で、地図の前で荷物を持ってうずくまっている、来日したばかりと見える外国人家族がいたので道案内をしました。そこの位置からだとホテルまでいくのが難しく、歩くと40分以上かかるし、ホテルの最寄り駅もそこからはうまくアクセスできないので、4人家族だったのでタクシーに乗ってもらったほうが良いと思ってタクシーを案内しました。ホテルへの辿り着き方を調べてこなかったのかもしれませんが、なんにせよ送り届けられて良かったです。

夫婦間でフランス語を話していたのでフランス人だったのかもしれません。大学でフランス語を勉強していた以来に生のフランス語が近くで聞けて少し嬉しかったです。

flags ライブラリに強制オプションはない

強制にしたいオプションがあったのですが、そういうオプションは flags にはない様子。どうせ引数チェックはするのだからそこで弾けば良いという回答ですが、その通りですね。

stackoverflow.com

ステージング・本番など実行環境によって定数を変えたい

環境変数にどの環境かを設定するか、Build Constraints という機能が使えるようです。

stackoverflow.com

ただ BuildConstraints はちょっと動作がわかりづらい気がしました。環境変数を読み込み、環境によって変数設定するのをパッケージ初期化時の init() メソッドで呼ぶ方が分かりやすいので今回はそのようにしました。上の質問者も Build Constraints ではなく、init() メソッドで環境変数を読み込む方法を取ったようです。

2018/10/29 異なる型のスライスの結合

先日、上野アメ横のセンタービル 地下食品街に行って仙草ゼリーを買ったのですが、店員さんが中国人だったので「你好」と言うと、「你好」を返してくれました。そのあとお会計の数字も聞き取れたし(920, 九十二)、一見無愛想なおじさんでしたが、最後に「谢谢你」と言ってもらえました。「谢谢」より少し丁寧な言い方のようです。

英語も中国語も、大学の時に勉強していたフランス語も、学ぶにつれて街中で役立つことが増えて生活が少し豊かになると実感します。

headlines.yahoo.co.jp

2つのスライスの結合

ある構造体と、それに移譲している別の構造体があって、それらのスライスを結合したかったのですが、うまくいかずに調べていました。簡略化すると以下の通りなのですが:

...
 5 type A interface{}
  6
  7 type X string
  8
  9 func main() {
 10         sliceA := make([]A, 0, 0)
 11         sliceX := []X{"x1", "x2"}
 12         var appendedSlice []A
 13         appendedSlice = append(sliceA, sliceX[0], sliceX[1])   // 動く
 14         appendedSlice = append(sliceA, sliceX...)  // 動かない。エラー: cannot use sliceX (type []X) as type []A in append
 15         fmt.Println(appendedSlice)
 16 }

14行目は13行目の構文糖だと思っていたのですが、型が異なる場合は挙動が異なるようです。

StackOverflow に質問したところ、回答が返ってきました:

stackoverflow.com

スライス同士が直接型変換されるようなケースは Go言語では許容していないようで、13行目のように個別に指定することを強制しているようです。例えば XA を実装しているので []X[]A に変換しようとしても、これも直接行うことはできずに、1つ1つ値を入れていくしかないようです。

回答の中で、以下の Go言語でよくある質問集をもらいましたので、あとで読んでみます。ありがたい。。

Frequently Asked Questions (FAQ) - The Go Programming Language

以下、参考に読んでいたページです。

stackoverflow.com

qiita.com

コマンドライン引数の処理

flag パッケージがありますが、他のプログラミング言語と大きく違いはなさそうです。

flag - The Go Programming Language

今回、変数をコマンドラインフラグで渡したかったので、同じフラグを複数回設定できる必要がありました。そのためにはフラグを受け取った時の処理を自分で書く必要があります。といってもインタフェースを実装するだけで、以下の StackOverflow の回答にも紹介されていました:

stackoverflow.com