- 投稿日:2020-09-26T23:45:24+09:00
【Golang】Go言語の基礎 値レシーバーとポインタレシーバーについて
今回はGo言語の値レシーバーとポインタレシーバーについてやっていきます!
この記事の後に、こちらの記事まで読んでいただくとよりGo言語に関する理解が深まると思います!値レシーバーとは
package main import "fmt" type Vertex struct { X, Y int } func (v Vertex) Area() int { return v.X * v.Y } //値レシーバー func (v Vertex) Scale(i int) { v.X = v.X * i v.Y = v.Y * i } func main() { v := Vertex{3, 4} v.Scale(10) fmt.Println(v.Area()) // => 12 }こちらは値レシーバになります。
めっちゃ乱暴にいうと*がなければ値レシーバーになります。値渡しとか言われるようです。v.Scale(10)で引数を渡しており、Scale関数の中では v.X = 3 * 10 v.Y = 4 * 10 となります。
しかし、値渡しでは直接Vertexの中身を書き換えることができません。あくまでもScale関数のスコープ内で値が書き換わっただけです。
よってArea関数では初期値のX,Yが使われるので返り値として 3 * 4 = 12が出力されます。【補足】
func (v Vertex) Area()と書くことによってVertexとArea()関数を紐づけることができます。
こちらのメソッドを呼び出すときは、v.Area()と書くと呼び出すことができます。
Goにはクラスという概念がありませんが、このようにすることによってオブジェクト指向のような書き方ができます。
(rubyでいうとUser.hogeみたいな感じですね)値レシーバー
package main import "fmt" type Vertex struct { X, Y int } func (v Vertex) Area() int { return v.X * v.Y } //ポインタレシーバー(*がついただけ) func (v *Vertex) Scale(i int) { v.X = v.X * i v.Y = v.Y * i } func main() { v := Vertex{3, 4} v.Scale(10) fmt.Println(v.Area()) // => 1200 }変数名のとなりに*をつけただけです!
これをすることによって構造体の中身を書き換えることができます。よってScale関数の引数で渡されたときに v.X = 30, v.Y = 40 となりそれがそのままArea関数に渡ります。
よってv.Area()の出力結果は1200となります!
最後まで読んでいただきありがとうございます!
なにか間違いや、ご質問などありましたら気軽にコメントもらえると嬉しいです!!
- 投稿日:2020-09-26T23:38:46+09:00
ACL Beginner Contest のメモ
前置き
Atcoderをやってみたので、自分用のメモです。
あとから加筆・修正する予定です。問題
https://atcoder.jp/contests/abl
A
Q_A.gopackage main import ( "fmt" ) func main() { var k int fmt.Scanf("%d", &k) var s string = "" for i := 0; i<k; i++{ s += "ACL" } fmt.Printf("%s\n", s) }B
Q_B.gopackage main import ( "fmt" ) func main() { var a, b, c, d uint64 fmt.Scanf("%d %d %d %d", &a, &b, &c, &d) var s string if (a<=d && c <= b) || (c <= b && a<=d) { s = "Yes" } else { s = "No" } fmt.Printf("%s\n", s) }C
覚えてたら後で書きます。
D
覚えてたら後で書きます。
E
覚えてたら後で書きます。
F
覚えてたら後で書きます。
- 投稿日:2020-09-26T22:50:17+09:00
ACL Beginner Contest 参戦記
ACL Beginner Contest 参戦記
ABLA - Repeat ACL
1分で突破. 書くだけ.
K = int(input()) print('ACL' * K)ABLB - Integer Preference
2分半で突破、WA1 orz. A < B < C < D のパターンを見落とした.
A, B, C, D = map(int, input().split()) if A <= C <= B or A <= D <= B or C <= A <= D or C <= B <= D: print('Yes') else: print('No')ABLC - Connect Cities
3分半で突破. Union Find して Union 数 - 1 が答え.
from sys import setrecursionlimit, stdin def find(parent, i): t = parent[i] if t < 0: return i t = find(parent, t) parent[i] = t return t def unite(parent, i, j): i = find(parent, i) j = find(parent, j) if i == j: return parent[j] += parent[i] parent[i] = j readline = stdin.readline setrecursionlimit(10 ** 6) N, M = map(int, readline().split()) parent = [-1] * N for _ in range(M): A, B = map(lambda x: int(x) - 1, readline().split()) unite(parent, A, B) print(sum(1 for i in range(N) if parent[i] < 0) - 1)ABLD - Flat Subsequence
32分で突破、WA1 orz. Go のコードを Python で提出した、アホすぎる. それまでの各値の最大経由数を記録しておけば SegmentTree で O(logN) で現値の最大経由数が求まるので O(NlogN) になり解ける.
package main import ( "bufio" "fmt" "os" "strconv" ) func min(x, y int) int { if x < y { return x } return y } func max(x, y int) int { if x > y { return x } return y } type segmentTree struct { offset int data []int op func(x, y int) int e int } func newSegmentTree(n int, op func(x, y int) int, e int) segmentTree { var result segmentTree t := 1 for t < n { t *= 2 } result.offset = t - 1 result.data = make([]int, 2*t-1) for i := 0; i < len(result.data); i++ { result.data[i] = e } result.op = op result.e = e return result } func (st segmentTree) update(index, value int) { i := st.offset + index st.data[i] = value for i >= 1 { i = (i - 1) / 2 st.data[i] = st.op(st.data[i*2+1], st.data[i*2+2]) } } func (st segmentTree) query(start, stop int) int { result := st.e l := start + st.offset r := stop + st.offset for l < r { if l&1 == 0 { result = st.op(result, st.data[l]) } if r&1 == 0 { result = st.op(result, st.data[r-1]) } l = l / 2 r = (r - 1) / 2 } return result } const ( maxA = 300000 ) func main() { defer flush() N := readInt() K := readInt() st := newSegmentTree(maxA+1, max, 0) for i := 0; i < N; i++ { A := readInt() st.update(A, st.query(max(A-K, 0), min(A+K+1, maxA+1))+1) } println(st.query(0, maxA+1)) } const ( ioBufferSize = 1 * 1024 * 1024 // 1 MB ) var stdinScanner = func() *bufio.Scanner { result := bufio.NewScanner(os.Stdin) result.Buffer(make([]byte, ioBufferSize), ioBufferSize) result.Split(bufio.ScanWords) return result }() func readString() string { stdinScanner.Scan() return stdinScanner.Text() } func readInt() int { result, err := strconv.Atoi(readString()) if err != nil { panic(err) } return result } var stdoutWriter = bufio.NewWriter(os.Stdout) func flush() { stdoutWriter.Flush() } func println(args ...interface{}) (int, error) { return fmt.Fprintln(stdoutWriter, args...) }追記: Python では TLE になってしまうが、PyPy なら AC した.
class SegmentTree: def __init__(self, size, op, e): self._op = op self._e = e self._size = size t = 1 while t < size: t *= 2 self._offset = t - 1 self._data = [e] * (t * 2 - 1) def update(self, index, value): op = self._op data = self._data i = self._offset + index data[i] = value while i >= 1: i = (i - 1) // 2 data[i] = op(data[i * 2 + 1], data[i * 2 + 2]) def query(self, start, stop): def iter_segments(data, l, r): while l < r: if l & 1 == 0: yield data[l] if r & 1 == 0: yield data[r - 1] l = l // 2 r = (r - 1) // 2 op = self._op it = iter_segments(self._data, start + self._offset, stop + self._offset) result = self._e for v in it: result = op(result, v) return result max_A = 300000 N, K, *A = map(int, open(0).read().split()) st = SegmentTree(max_A + 1, max, 0) for a in A: st.update(a, st.query(max(a - K, 0), min(a + K + 1, max_A + 1)) + 1) print(st.query(0, max_A + 1))ABLE - Replace Digits
突破できず. Lazy Segtree なんだろうなとは思ったけど、ACL の lazy_segtree の mapping, composition, id が理解できなくて利用できなかった.
- 投稿日:2020-09-26T20:38:16+09:00
cgoのg++用インクルードパス
分かってしまったらなんてことはないが、調べてもなかなか出てこなくて詰まったのでメモ。
環境:go version go1.15.2 linux/amd64
CGOでインクルードパスを指定するときには
// #cgo CFLAGS: -I<インクルードパス>
とすればよいが、これはgccでのコンパイルのときにしか使用されない。
cppファイルはg++でコンパイルされるので、この時のインクルードパスを指定したければ
// #cgo CPPFLAGS: -I<インクルードパス>
とする。以上。
- 投稿日:2020-09-26T19:29:08+09:00
golangでPre-Signed URL(署名付きURL)を生成する
GolangでPreSignedUrlを発行するコードを書いたので残しておきます。
package main import ( "context" "fmt" "time" "os" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" ) // S3の接続情報 accessKey := os.Getenv("AWS_ACCSESS_KEY") secretKey := os.Getenv("AWS_SECRET_KEY") region := aws.String(os.Getenv("CUEE_DB_REGION")) creds := credentials.NewStaticCredentials(accsessKey, secretKey, "") session := session.Must(session.NewSession(&aws.Config{ Credentials: creds, Region: region, })) svc := s3.New(session) c, _ := svc.PutObjectRequest(&s3.PutObjectInput{ Bucket: aws.String("バケット名"), Key: aws.String("/diectory/filename"), }) url, err := c.Presign(15 * time.Minute) if err != nil { fmt.Println("error presigning request", err) return nil, err } fmt.Println(url) }ここで有効期限を設定できます
url, err := c.Presign(15 * time.Minute)公式
https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/s3-example-presigned-urls.htmlBodyにファイルを含め、発行されたURLにアクセスすればアップロードできます
- 投稿日:2020-09-26T15:02:31+09:00
Golangの環境構築
本記事はGo初心者の備忘録になります。
環境
OS: Mac Catalina
Shell: ZshGo のインストール
homebrew でGoをインストール
brew install go
インストールが完了したか確認(バージョンも合わせて確認)
go version # 出力結果例) go version go1.15.2 darwin/amd64
環境変数$GOROOTの確認(Goのインストール先)
go env GOROOT # 出力結果例) /usr/local/Cellar/go/1.15.2/libexec環境変数$GOPATHの確認
go env GOPATH # 出力結果例) /Users/ユーザー名前/go ls $(go env GOPATH) # 出力結果例) ls: /Users/ユーザー名前/go: No such file or directory※パッケージのインストールを終えていない段階ではまだ
$GOPATHのディレクトリは存在しない
。Goの環境設定
パッケージのインストール(パッケージはなんでもいい)
go get github.com/motemen/gore/cmd/goreGOPATHの確認とパッケージの確認
ls $(go env GOPATH) # 出力結果) bin/ src/※
bin/
にはバイナリコード、src/
にはソースコードが格納されている。GOPATHの設定をする(インストールしたパッケージをコマンドとして利用可能にする)
vim ~/.zshrc下記の内容を
~/.zshrc
の最後に追記~/.zshrc"Golangのパス設定 export GOPATH=$(go env GOPATH) export PATH=$PATH:$GOPATH/bin保存して閉じた後、
~/.zshrc
の再読み込みsource ~/.zshrc
インストールしたパッケージがコマンドラインで使えるようになったかの確認
gore は ruby の irb のように対話的にgoを実行できる
gore -autoimport gore> gore> fmt.Println(Hello World) Hello World 12 <nil>↑12バイト出力されて、エラーがなかったという意味
ctrl + d で終了goreがより便利になる拡張ツールも合わせてインストール
コード補完、出力のハイライト、APIドキュメントの参照などができるようにする。
go get github.com/mdempsky/gocode go get github.com/k0kubun/ppプロジェクト管理ツールの設定
プロジェクト管理ツール ghq のインストール
brew install ghq
ghq の確認(自端末にインストールされているGoパッケージ(プロジェクト)の一覧を表示する)
ghq list # 出力結果例) # golang.org/x/tools # golang.org/x/xerrors # golang.org/x/sys # golang.org/x/text # golang.org/x/mod # github.com/peterh/liner # github.com/k0kubun/pp # github.com/motemen/gore # github.com/motemen/go-quickfix # github.com/mdempsky/gocode # github.com/mattn/go-isatty # github.com/mattn/go-runewidth # github.com/mattn/go-colorable※自分で何かプロジェクトの作成を行う際も上記と同じ階層で開発を行う。
ghqでソースを取得してくるディレクトリを指定
git config --global ghq.root $GOPATH/srcghq は裏で git コマンドを叩いていると思われる。
他の細かいところは必要になったら対応する。
- 投稿日:2020-09-26T07:53:44+09:00
[Golang]`errors.Is()` `errors.As()` 完全ガイド〜使い方と違いをしっかり調査しました〜
はじめに
errors.As()
を雰囲気で使っていたらハマったので、errors.Is()
も含めて、しっかりと調査してドキュメントとコードを読んだ上でまとめてみました。ハマったところ、ハマりそうなところを重点的にまとめてみたので、お役に立てれば幸いです。
何をするメソッドなのか
簡単に
error
を比較してbool
を返してくれます。
使いみちとしては、アプリケーションのエラーを外部のエラー(例:gRPCのエラー)に変換したり、ライブラリで使用されているエラーをハンドリングして、アプリケーションのエラーに変換したりするときがあると思います。
errors.Is()
- 比較対象が保持している値と比較します。
- 値が同じなら
true
を返します。- 値が異なるなら
false
を返します。interface
など比較できない者同士だと必ずfalse
になります。
errors.As()
- 比較対象を型レベルで比較します。
- 型が同じなら
true
を返します。- 型が異なるなら
false
を返します。- 値は違っても
true
を返します。- 引数
target
(第2引数)には、nil
ではないポインタ型を渡しましょう。詳しく
errors.Is()
のGoDocとコードを読みましたIs reports whether any error in err's chain matches target.
The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap.
An error is considered to match a target if it is equal to that target or if it implements a method Is(error) bool such that Is(target) returns true.
An error type might provide an Is method so it can be treated as equivalent to an existing error.
https://golang.org/pkg/errors/#Isそのまま訳すと...
Is() は、
err
のチェーン内のエラーがターゲットにマッチするかどうかを報告します。
このチェーンはerr
自身の後に、Unwrap
を繰り返し呼び出すことで得られる一連のエラーで構成されています。
エラーがターゲットと等しい場合、またはIs(target)
がtrue
を返すようなIs(error) bool
メソッドを実装している場合、エラーはターゲットと一致しているとみなされます。
エラーの型は、既存のエラーと同等の扱いができるように、Is()
メソッドを提供している場合があります。要するに、
errors.Is()
は、エラーを比較して同じ値を持っていたらtrue
、持っていないならfalse
を返してくれます。注目する点は...
エラーがターゲットと等しい場合
は、
Is()
の実装は必要無いということです。もっというと、
エラーの値が比較可能であれば、
Is()
の実装は必要無いです。
エラーの値が比較不可能であれば、Is()
の実装は必要です。あとは、Wrapしたエラーには使える無いも抑えておくべきです!
→ Wrapしたエラーと比較するときは、errors.As()
を使いましょう。エラーの値が比較可能なとき
例えば、
struct
にError()
を実装しerror interface
を満たして、error
として扱っている場合のことです。
struct
同士は比較可能なので、Is()
の実装は必要ありません。実際のコードを見ると、値を比較できるまで
Unwrap()
して、比較可能になった時点で、比較していることがわかります。isComparable := reflectlite.TypeOf(target).Comparable() for { if isComparable && err == target { return true } if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { return true } // TODO: consider supporting target.Is(err). This would allow // user-definable predicates, but also may allow for coping with sloppy // APIs, thereby making it easier to get away with them. if err = Unwrap(err); err == nil { return false } }https://github.com/golang/go/blob/master/src/errors/wrap.go#L44-L58
エラーの値が比較 不 可能なとき
例えば、
error interface
を内包したstruct
同士を比較したときなどです。
interface
同士の比較はできません。isComparable := reflectlite.TypeOf(target).Comparable()https://github.com/golang/go/blob/master/src/errors/wrap.go#L44
ここに
false
が入るわけです。例
下記のサンプルコードでは、
interface error
は比較できないので、false
になっています。type originalError struct{ err error } func (e originalError) Error() string { return e.err.Error() } func main() { is := errors.Is(originalError{err: errors.New("1")}, originalError{err: errors.New("1")}) fmt.Printf("is = %v because err is not comparable\n", is) }https://play.golang.org/p/IN7BHbriu26
なので、
Is()
を実装して、比較する必要があります。
実装されたIs()
では、err.Error()
の結果string
を使って比較を行っているので、true
が返ります。type originalError struct{ err error } func (e originalError) Error() string { return e.err.Error() } // implemented!! func (e originalError) Is(target error) bool { return e.err.Error() == target.Error() } func main() { is := errors.Is(originalError{err: errors.New("1")}, originalError{err: errors.New("1")}) fmt.Printf("is = %v because originaleError implements Is()\n", is) }https://play.golang.org/p/5p8u-D1Hr6q
Wrap
したエラーには使えないなぜなら、値を比較するので
Wrap
された時点で値は比較対象とは異なるはずだからです。
先程も書きましたが、Wrap
されたエラーと比較したいならerrors.As()
を使いましょう!ちなみに、エラーをラップするためには標準パッケージを使うと
fmt.Errorf("failed to do something: %w", err)みたいな感じで
Wrap
できます。
errors.As()
のGoDocとコードを読みましたAs finds the first error in err's chain that matches target, and if so, sets target to that error value and returns true. Otherwise, it returns false.
The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap.
An error matches target if the error's concrete value is assignable to the value pointed to by target, or if the error has a method As(interface{}) bool such that As(target) returns true. In the latter case, the As method is responsible for setting target.
An error type might provide an As method so it can be treated as if it were a different error type.
As panics if target is not a non-nil pointer to either a type that implements error, or to any interface type.
https://golang.org/pkg/errors/#Asそのまま訳すと...
As()
はerr
のチェインの中で最初のエラーが target にマッチするものを見つけ、マッチしていれば target をそのエラー値に設定してtrue
を返します。そうでなければfalse
を返します。
チェーンは err 自体の後に、Unwrap
を繰り返し呼び出すことで得られる一連のエラーで構成されています。
エラーの具体的な値が target が指す値に代入可能な場合、またはエラーがAs(target)
がtrue
を返すようなAs(interface{}) bool
メソッドを持っている場合、エラーは target にマッチします。後者の場合は、As()
メソッドがtargetの設定を担当します。
エラータイプがAs()
メソッドを提供している場合は、それが別のエラータイプであるかのように扱うことができます。
As()
は、target がエラーを実装した型、または任意のinterface
へのnil
ではないポインタである場合にパニックを起こします。要するに、
errors.As()
は、エラーを比較して同じ型であればtrue
、異なる型ならfalse
を返してくれます。注目する点は...
panic
になる条件を抑えること。- 比較対象の値は同じでは無くて良くて、代入可能であればいいこと。
Wrap
したエラーにも使えること。です。
panic
になる条件① 比較対象がnil
もしくは、pointerではない型であるif target == nil { panic("errors: target cannot be nil") } val := reflectlite.ValueOf(target) typ := val.Type() if typ.Kind() != reflectlite.Ptr || val.IsNil() { panic("errors: target must be a non-nil pointer") }https://github.com/golang/go/blob/master/src/errors/wrap.go#L78-L85
実際に
panic
を起こしてみる比較対象が
nil
のときtype originalError struct{ err error } func (e originalError) Error() string { return e.err.Error() } func main() { err := &originalError{err: errors.New("1")} as := errors.As(err, nil) fmt.Printf("as = %v\n", as) }https://play.golang.org/p/UpCzRpoYPqW
実行結果
./prog.go:14:8: second argument to errors.As must be a non-nil pointer to either a type that implements error, or to any interface type Go vet exited. panic: errors: target cannot be nil goroutine 1 [running]: errors.As(0x4deb00, 0xc000010210, 0x0, 0x0, 0xc000032778) /usr/local/go-faketime/src/errors/wrap.go:79 +0x5f5 main.main() /tmp/sandbox800961272/prog.go:14 +0x9f比較対象がpointerではない型のとき
type originalError struct{ err error } func (e originalError) Error() string { return e.err.Error() } func main() { err := &originalError{err: errors.New("1")} var target originalError as := errors.As(err, target) // 本当は &target とするべき fmt.Printf("as = %v\n", as) }https://play.golang.org/p/iF-43pCJf3P
実行結果
./prog.go:15:8: second argument to errors.As must be a non-nil pointer to either a type that implements error, or to any interface type Go vet exited. panic: errors: target must be a non-nil pointer goroutine 1 [running]: errors.As(0x4deae0, 0xc00010a050, 0x4ae1c0, 0xc00010a060, 0xc000068f48) /usr/local/go-faketime/src/errors/wrap.go:84 +0x54f main.main() /tmp/sandbox214256996/prog.go:15 +0xd1
panic
になる条件② 比較対象がerror interface
を実装していないそもそもコンパイルできないので具体例を載せることは割愛しますが、要注意です!
if e := typ.Elem(); e.Kind() != reflectlite.Interface && !e.Implements(errorType) { panic("errors: *target must be interface or implement error") }https://github.com/golang/go/blob/master/src/errors/wrap.go#L86-L88
「
panic
になる条件」と「比較対象の値は同じでは無くて良くて、代入可能であればいいこと」を踏まえて実装してみる2つのパターンを用意してみました。
enum
を使った実装type ErrorCode uint64 const ( Zero ErrorCode = iota One ) func (code ErrorCode) Error() string { return [...]string{ "Error: Zero", "Error: One", }[code] } func main() { var code ErrorCode as := errors.As(Zero, &code) fmt.Printf("as = %v\n", as) }https://play.golang.org/p/Zad83sF-Dxv
struct
を使った実装type originalError struct{ err error } func (e originalError) Error() string { return e.err.Error() } func main() { var err originalError as := errors.As(originalError{err: errors.New("1")}, &err) fmt.Printf("as = %v\n", as) }https://play.golang.org/p/WA-pdwXcM9W
注意点:
pointer
を意識してください
errors.As()
の実装以外でもハマりがちなのが、pointerです。
下記を例にすると、originalError
と*originalError
は違います。
よって、errors.As()
はfalse
を返します。type originalError struct{ err error } func (e originalError) Error() string { return e.err.Error() } func main() { err := originalError{err: errors.New("1")} var target *originalError as := errors.As(err, &target) fmt.Printf("err = %T, target = %T\n", err, target) fmt.Printf("as = %v\n", as) }https://play.golang.org/p/V3WravAWHpb
そして、当たり前なんですが、
err
の方をpointerにすれば、true
を返します。type originalError struct{ err error } func (e originalError) Error() string { return e.err.Error() } func main() { err := &originalError{err: errors.New("1")} // pointer var target *originalError // pointer as := errors.As(err, &target) fmt.Printf("as = %v\n", as) }https://play.golang.org/p/2RHK2k7ZBJk
Wrap
したエラーとの比較に使えます
As()
は値の差異は関係ないので、型があっているとtrue
を返します。
なので、エラーが持っているメッセージは関係なく、型レベルで同じか確かめたいときに有効です!type ErrorCode uint64 const ( Zero ErrorCode = iota One ) func (code ErrorCode) Error() string { return [...]string{ "Error: Zero", "Error: One", }[code] } func main() { wrappedError := fmt.Errorf("wrap: %w", Zero) var code ErrorCode as := errors.As(wrappedError, &code) fmt.Printf("as = %v\n", as) }https://play.golang.org/p/9Sdw-th7znr
さいごに
結構詳しめに
errors.Is()
errors.As()
について調べて疲れましたw
ただ利用頻度が高いライブラリだと思うので、しっかりと抑えて今日学んだ知識を活かしていきたいと思います。
- 投稿日:2020-09-26T00:26:42+09:00
Go: panicキャッチ後にStackTraceを表示する
Panicをrecoverした後に、StackTraceを表示したかった
いろんなライブラリをあさって発見したのでメモpackage main import( "runtime" "log" ) func main() { defer func() { if err := recover(); err != nil { stackTrace() } }() panic("Panic Error!") } func stackTrace() { stack := make([]byte, 1024*8) stack = stack[:runtime.Stack(stack, false)] log.Println(string(stack)) }