JSゆるふわめも

がっこうでべんきょうしたことをめもがきしてます

ファイナライザを避ける

ファイナライザ

GCによって、インスタンスが回収されるタイミングで実行されるメソッド

ファイナライザは(原則)使用するべきでは無い

理由

  • ファイナライザは即時実行されるわけでは無い(意図しないタイミングで実行される可能性がある)
  • そもそも実行されることが保証されていない

ファイナライザ相当のことをしたい場合

  • 明示的にコールする後処理メソッドを書く
    • terminate, dipose, etc…
  • 上記メソッドがコールされたことを管理するフィールド値をオブジェクトに持たせる
    • isTerminated, isDisposed

参考: ExecutorService (Java Platform SE 6)

ファイナライザの使い所は?

上記メソッドを呼び出し忘れた時の保険

実装する場合の注意点

親クラスでファイナライザが実装されている場合、忘れずに呼び出すこと

  • かなり簡略化しているので、正確完全な情報はEffective Java原本を参照してください

text/scanner

Package scanner provides a scanner and tokenizer for UTF-8-encoded text. It takes an io.Reader providing the source, which then can be tokenized through repeated calls to the Scan function. For compatibility with existing tools, the NUL character is not allowed. If the first character in the source is a UTF-8 encoded byte order mark (BOM), it is discarded.

By default, a Scanner skips white space and Go comments and recognizes all literals as defined by the Go language specification. It may be customized to recognize only a subset of those literals and to recognize different identifier and white space characters.

スキャナパッケージはUTF-8エンコーディングされたテキストを対象にしたscannerとtokenizerを持っている。Scanメソッドを繰り返し呼び出すことで、ソースコードが記述されたio.Readerをtokenizedできる。既存のツールとの互換性を維持するために、ヌル文字は使用することができない。もしソースコードの先頭にBOMが付いていても無視される。

基本的にScannerは空白文字とコメントは読み飛ばし、Go言語仕様で定義された全てのリテラル値をパースする。しかし、scannerは異なるルールのソースコードをパースするためにカスタマイズされている場合もある。

サンプル

import (
    "fmt"
    "strings"
    "text/scanner"
)

func main() {
    const c = `
// This is scanned code.
var i int
var s string = "text"
if a > 10 {
    someParsable = text
}`
    var s scanner.Scanner
    s.Init(strings.NewReader(c))
    s.Filename = "test"
    for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
        fmt.Printf("%s:%v %v\n", s.Position, scanner.TokenString(tok), s.TokenText())
    }
}

実行結果

test:3:1:Ident var
test:3:5:Ident i
test:3:7:Ident int
test:4:1:Ident var
test:4:5:Ident s
test:4:7:Ident string
test:4:14:"=" =
test:4:16:String "text"
test:5:1:Ident if
test:5:4:Ident a
test:5:6:">" >
test:5:8:Int 10
test:5:11:"{" {
test:6:2:Ident someParsable
test:6:15:"=" =
test:6:17:Ident text
test:7:1:"}" }

Scanメソッドの返り値は

  • リテラル値は型
  • 変数・関数名(上記以外のもの)はIdent
  • { ( などはそのまま

scanner package

Goには良く使用するであろうbufio Scannerとは別に、構文解析を行うためのscannerパッケージに属するScanner型が存在する

Package scanner implements a scanner for Go source text. It takes a []byte as source which can then be tokenized through repeated calls to the Scan method.

scannerパッケージはGoのソースコードのscannerを実装しています。このパッケージはbyte配列をソースコードとみなし、Scanメソッドを繰り返し呼び出すことで、トークナイズします。

公式ドキュメントのScanメソッドのコード例を実行してみるとscannerパッケージの役割がよくわかる。 scanner - The Go Programming Language

 src := []byte("cos(x) + 1i*sin(x) // Euler")
1:1  IDENT   "cos"
1:4 (   ""
1:5 IDENT   "x"
1:6 )   ""
1:8 +   ""
1:10    IMAG    "1i"
1:12    *   ""
1:13    IDENT   "sin"
1:16    (   ""
1:17    IDENT   "x"
1:18    )   ""
1:20    ;   "\n"
1:20    COMMENT "// Euler"

scannerを使用して構文解析をしている例 d.hatena.ne.jp

おそらくかなり賢い32 << (^unint(0) >> 63)

プログラミング言語Go練習問題6.5で、

プラットフォームに対して最も効率的な符号無し整数を使用するために、 かなり賢い式32 << (^uint(0) >> 63) を使える。

と説明されている。 uintはプラットフォームが32bit環境なら32bitの符号無し整数・64bit環境なら64bitの符号無し整数として振る舞う。 なので、^uint(0)は32bit環境ならば0を反転しているので、32bit立った状態を表している。

したがって、(^uint(0) >> 63)は63bit文右シフトしているので、32bitならば0になり、63bitならば最下位のbitだけが立っている状態になる。

となると32 << (^uint(0) >> 63)は32bit環境ならば32 << 0で32となり、64bit環境ならば32 << 1で64となる。

なので、 おそらくかなり賢い32 << (^unint(0) >> 63) はプラットフォームのbitサイズを返す式となっている。

err != nilがめんどい。そんな人はPostfix Completionを使おう

元々VSCodeでgoを書いてたんだけど、最近はgoglandを使っててだいぶ満足している。 また、最近見つけた機能でPostfix Completionという機能がとても便利だった。

err.nilで保管をすると、下記のコードを自動で生成してくれる。

if err != nil {}

他にも気の利いた保管が用意されているので積極的に使っていきたい。

Go言語学習メモ

bit演算

y 0001

^単項で使用するとビット反転 ^y 1110

x 1011

符号ありの型をビット反転させると符号も反転する

 var ux uint = 0xff
    var uy uint = 0x1
    x := 0xff
    y := 0x1
    fmt.Printf("int x=%b int y=%b\n", x, y)
    fmt.Printf("^x=%b ^y=%b\n", ^x, ^y)
    fmt.Printf("uint x=%b uint y=%b\n", ux, uy)
    fmt.Printf("^ux=%b ^uy=%b\n", ux, uy)
    fmt.Printf("x&^y=%b \n", x&^y)
    fmt.Printf("y&^x=%b \n", y&^x)

&^二項演算子で使用するとビット差 x&^y 1010

bigの計算

var x,y big.Rat x.Add(&x,&y) //xを書き換える。

javaの場合bigIntergerに対するaddはGoで言うと下記のようになっている レシーバー(z)と第一引数(x)が異なるため、加算されるごとにメモリ消費量が増える したがって、Goはレシーバーと第一引数が一致している場合メモリ効率が良い x,y,z big.Rat z.Add(&x,&y)

UTF-8の自己同期化とは

そもそも同期とは? 例えばCPUアーキテクチャデータバスはバイト列の区切りをどのように判断しているのか CPUクロックを見てデータの区切りを判断している。つまり、データバスは自己では同期できていない

       0|0|1|0|0|
CPUクロック  ^ ^ ^ ^ ^

この意味で、UTF-8はそれ自身でデータの区切りがわかるフォーマットのため自己同期化されていると言える

Goのコンパイル環境

go env goに関する環境変数の表示

[GO_ROOT_BOOTSTRAP Go 1.7.1]を利用して、[~/tools/go/src]にある最新のコンパイラ[仮にNCompとする]が作成される。このNCompを利用して、残りの[~/tools/go/src]ソースコードコンパイルする。

初期の頃はこのBootStarpコンパイラC言語で書かれていた。

[~/tools/go/src]内のinternalは外からさわれないパッケージ vendarパッケージは3rdベンダーのソースコードを丸々取り込んでいるもの 丸々自分たちの領域に取り込むことで、goだけあれば動作することを保証している

Rune int32

Runeとint32は全く一緒の型

配列もしくはArrayへの配列言語仕様書に記載

func zero_array(ptr *[32]byte){
    for i := range ptr {
        ptr[i]=0
    }
}

//コンパイルエラー
func zero_array(ptr []byte) {
    for i := range ptr {
        ptr[i]=0
    }
}

func zero_array(ptr *[]byte) {
    for i := range ptr {
        (*ptr[i])=0
    }
}

constant xxx truncated to integerについて

Goにおける定数(constant)は定義がかなり緩い型になっており、 それを含んだ式や代入を行う場合明示的に型を指定する必要がある。

stackoverflow.com

上記質問で触れられているリンクを雑&かなり適当な意訳してみた

Conversions are required when different numeric types are mixed in an expression or assignment. For instance, int32 and int are not the same type even though they may have the same size on a particular architecture.

型の異なる数値型が一つの式や代入内に現れる場合、型変換が要求される。例えば、int32とint型はとあるアーキテクチャーでは同じサイズを持つかもしれないが、同じ型ではない。

Why does Go not provide implicit numeric conversions? The convenience of automatic conversion between numeric types in C is outweighed by the confusion it causes. When is an expression unsigned? How big is the value? Does it overflow? Is the result portable, independent of the machine on which it executes? It also complicates the compiler; “the usual arithmetic conversions” are not easy to implement and inconsistent across architectures. For reasons of portability, we decided to make things clear and straightforward at the cost of some explicit conversions in the code. The definition of constants in Go—arbitrary precision values free of signedness and size annotations—ameliorates matters considerably, though.

A related detail is that, unlike in C, int and int64 are distinct types even if int is a 64-bit type. The int type is generic; if you care about how many bits an integer holds, Go encourages you to be explicit.

A blog post titled Constants explores this topic in more detail.

何故Goは暗黙的に数値変換を行ってくれないのか C言語のように異なる数値型を意識することなく変換することは便利ですが、それ以上に混乱をもたらします。 符号なしの型を用いた場合はどうなるでしょう?値がものすごく大きな値だったら?その値はオーバーフローするの?結果は動作するアーキテクチャに依存するのでしょうか?これらの問題はコンパイラーにとっても問題です。「一般的」な数値型変換を実装するのは容易ではありませんし、アーキテクチャ依存です。我々は移植性の観点から問題を明確化することにし、素直にコード中で明示的な変換を行うことにしました。ですから、Go言語における定数の定義は任意の精度を持つ値で、それは符号あり、なし・サイズなどを定義しておらず、問題をかなりわかりやすくしています。

このことに関して詳しく言うと、Cと違って、intとint64はたとえそれらが同じビットサイズを持っていても全く違う型とみなされます。int型は汎用型であり、アーキテクチャ依存です。もしintegerのbitサイズが重要ならば、Go言語では明示的な型を使用するべきでしょう。

ブログ記事Constantsではこのトピックについてさらに詳細に説明しています。