20210916のGoに関する記事は6件です。

Goでカタカナバリデーションを実装しようとしたが、unicodeパッケージのKatakanaテーブルには記号なども含まれていた。

現象 Goでカタカナチェックを実装する際に、 unicodeパッケージのKatakanaテーブルを利用していたのだが、 if !unicode.In(r, unicode.Katakana) { return false } なんか想定外の㋐などの記号も通してしまうので、 Katakanaのテーブルにはどのような文字が入っているのか確認した。 解析 unicodeパッケージのソースを見る限り、下記コードポイントの範囲で指定されている。 var _Katakana = &RangeTable{ R16: []Range16{ {0x30a1, 0x30fa, 1}, {0x30fd, 0x30ff, 1}, {0x31f0, 0x31ff, 1}, {0x32d0, 0x32fe, 1}, {0x3300, 0x3357, 1}, {0xff66, 0xff6f, 1}, {0xff71, 0xff9d, 1}, }, R32: []Range32{ {0x1b000, 0x1b164, 356}, {0x1b165, 0x1b167, 1}, }, } コードポイントを調べた所、アイウエオのような一般的なカタカナ以外にも、 ㌠みたいな、カタカナを利用して構築してある記号や、 ヰのような旧字体かなや変体仮名も、カナとしての対象として含まれていた。 Range16 0x30a1 ァ 0x30fa ヺ 0x30fd ヽ 0x30ff ヿ 0x31f0 ㇰ 0x31ff ㇿ 0x32d0 ㋐ 0x32fe ㋾ 0x3300 ㌀ 0x3357 ㍗ 0xff66 ヲ 0xff6f ッ 0xff71 ア 0xff9d ン Range32 0x1b000 ? 変体仮名 片仮名 ア行エ段 0x1b164 ヰ 旧字体かな文字 0x1b165 ヱ 旧字体かな文字 0x1b167 ? 旧字体かな文字 ンの仮名? 対応 要件としては、記号系のカナはバリデーション対象外とする必要があり、 unicodeパッケージのKatakanaテーブルは使えないので、自作する事にした。 参考 https://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_3000-3FFF https://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_F000-FFFF https://ja.wikipedia.org/wiki/%E4%BB%AE%E5%90%8D%E8%A3%9C%E5%8A%A9 https://unicode-table.com/en/ https://pkg.go.dev/unicode
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Goでプリミティブなアトミック操作をするならuber-go/atomicを使ってほしい話と、比較不能かつコスト0な型の話

atomicを直接使う人はそう多くないと思われるが、仮に使うようなことがあった場合は全人類標準ライブラリのsync/atomicではなくuber-go/atomicを使うべきである。 なぜsync/atomicはよくないのか 以下のようなコードにおいて、sync/atomicは型による保護機構が欠如しているために誤った操作に気がつくことができない。 package main import ( "fmt" "sync/atomic" ) type Money struct { price int32 } func main() { ended := make(chan int) mine := &Money{price: 1} go func() { // atomic.AddInt32(&mine.price, 1) とすべきところで間違いに気が付かない mine.price += 1 ended <- 0 }() // atomic.AddInt32(&mine.price, 1) とすべきところで間違いに気が付かない mine.price += 1 _ = <- ended fmt.Println(mine) } せっかく静的な型システムがあるのにこれは残念すぎる。 これは uber-go/atomic を使うことで対処可能である。 こちらで定義されている型を使った場合、以下のようにアトミックな操作でないコンパイル時にはじくことができる。 package main import ( "fmt" "go.uber.org/atomic" ) type Money struct { price *atomic.Int32 } func main() { ended := make(chan int) x := atomic.NewInt32(1) mine := &Money{price: x} go func() { // mine.price += 1 // > コンパイルエラー // > invalid operation: mine.price += 1 (mismatched types *"go.uber.org/atomic".Int32 and int) mine.price.Add(1) ended <- 0 }() // mine.price += 1 // > コンパイルエラー // > invalid operation: mine.price += 1 (mismatched types *"go.uber.org/atomic".Int32 and int) mine.price.Add(1) _ = <- ended fmt.Println(mine.price.Load()) } またすべての非アトミック操作ができなくなるわけではないが(dereferenceはできる)、以下のように比較しようとした場合にコンパイルエラーになる。 x := atomic.NewInt32(1) y := atomic.NewInt32(1) fmt.Println(*x == *y) // <-- invalid operation: *xx == *yy (struct containing "go.uber.org/atomic".nocmp cannot be compared) もちろんLoad()を使えばこれは解決できる。 比較不能な型 nocmpという型を定義してそれを使っている。 コードを抜粋すると以下の箇所。 // nocmp is an uncomparable struct. Embed this inside another struct to make // it uncomparable. // // type Foo struct { // nocmp // // ... // } // // This DOES NOT: // // - Disallow shallow copies of structs // - Disallow comparison of pointers to uncomparable structs type nocmp [0]func() ここでnocmpの定義は[0]func()となっており、これをフィールドとして持っている構造体は比較不能となっている。 これはgo4org/memからもってきたそうで、こちらの定義のコメントによると // (...). Its various methods should inline & compile to the equivalent operations // working on a string or []byte directly. とのことでsync/atomicに比べて余計なコストがかかるというパフォーマンス上の心配はしなくてもよさそうだ。 なぜこの形で定義されているのかまでは調べきれなかったが、func型が比較不能型である点と上記のコメントのようにサイズ0の配列は最適化で消え去ることで0コストになるあたりを組み合わせた結果このような形になったものと思われる。 このあたりのロジックはGo本体の src/cmd/compile/internal/typecheck/expr.go のtoArith関数内 src/cmd/compile/internal/types/alg.go の AlgType関数内 にある。調べた時点でのGoのバージョン(gitのrevisionだが)は $ git rev-parse HEAD cfa233d76bcff00f46f5e5acdb17cb819a309d2b だが以前のバージョンでもそれほど変わっていないものと思われる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaで飯食ってるエンジニアがGolangを学んでみる~序章~

とりあえずJavaとの差を意識してメモ書きのように書いてみる。Go→Javaの順 序章と書いたが続くかは不明。。。 社会人になり、弊社に入社してからJavaを学んだのだが、 その時は学生時代に研究で利用していたFortranとの対比で基本的な文法は学んで言った記憶があった。 (やり方がいいかは別) 今回もJavaとGoがいずれも静的型付け言語であることを利用し、 同じことをそれぞれの文法で書いたらどうなるかを実際にメモ書きしてみた感じ。 ※Editorじゃないのでミスっていたらすみませんコメントで教えてください ※全部はやってません コンソール出力 import "fmt" func main(){ var human string = "TOM" fmt.Printf("%sは人間です",human) } public static void main(String[] args){ String human = "TOM"; System.out.printf("%sは人間です",human); } if文 import "fmt" func main(){ var score int = 100 if score == 80 { fmt.Println("素晴らしい") }else if score >= 60 { fmt.Println("よくできました") }else { fmt.Println("頑張りましょう") } } public static void main(String[] args){ int score = 100; if(score == 80) { System.out.println("素晴らしい"); }else if(score >= 60){ System.out.println("よくできました"); }else { System.out.println("頑張りましょう"); } } for文 import "fmt" func main(){ for i := 1 ; i <= 100 ; i++{ fmt.Println(i) } } public static void main(String[] args){ for(int i = 1 ; i <= 100 ; i++){ System.out.println(i); } } 乱数 import "fmt" import "math/rand" func main(){ fmt.Println(rand.Intn(10)) } import java.util.Random; public static void main(String[] args){ System.out.println(new Random().nextInt(10)); } 完全な乱数 import "fmt" import "math/rand" import "time" func main(){ rand.Seed(time.Now().Unix()) fmt.Println(rand.Intn(10)) } コンソール入力 import "fmt" func main(){ var input string fmt.Scan(&input) fmt.Println(input) } import java.util.Scanner; public static void main(String[] args){ Scanner sc = new Scanner(System.in); String input = sc.nextLine(); System.out.println(input); } 引数のあるメソッド import "fmt" func main(){ hoge("あいう") } func hoge(fuga string){ fmt.Println(fuga) } public static void main(String[] args){ hoge("あいう") } private void hoge(String fuga){ System.out.println(fuga); } 戻り値のあるメソッド import "fmt" func main(){ hogehoge := hoge("あいう") fmt.Println(hogehoge) } func hoge(fuga string) string{ return fuga } public static void main(String[] args){ String hogehoge = hoge("あいう") System.out.println(hogehoge); } private String hoge(String fuga){ return fuga; } ポインタ型 import "fmt" func main(){ var hogehoge string = "あいう" var hogehogePtr *string =&hogehoge fmt.Println(hogehoge) fmt.Println(hogehogePtr) } 出力結果 >あいう >0xc420010220 ポインタ型を利用した値の更新 import "fmt" func main(){ var hogehoge string = "あいう" fmt.Println(hogehoge) change(&hogehoge) fmt.Println(hogehoge) } func change(hogehogePtr *string){ *hogehogePtr = "かきく" } 出力結果 >あいう >かきく
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Goで Chokudai SpeedRun 001 を解く

Chokudai SpeedRun 001 を一通り解いたので、解き方やコードを載せます。 このコンテストは、私がAtCoderで過去に行われたコンテストを色々漁っているうちに見つけました。開かれた経緯が不明(有識者求む)の謎のコンテストですが、問題セットが典型問題で構成されています。そのため、私のような競プロ初心者には、典型解法の確認やライブラリの整理に使えました。問題を解く際の注意点ですが、他のコンテストと違い、共通の制約がコンテストのトップページに書かれていて、問題文に書かれていないので、合わせて読まないと罠にはまる可能性があります。最後ですが、もっといい解法があれば連絡いただけると助かります。 A 最大値 for文を使い、入力を受け取るたびに、最大値を更新するだけ。for文を使うのが面倒でなら、$n$の上界が100しかないので、入力を全て受け取って、ソートして端を取り出すのもあると思います。私は、Goには int の max関数はないので以下を定義し、max(a...) で呼び出しました。 func max(arg ...int) int { max := arg[0] for _, x := range arg { if max < x { max = x } } return max } B 和 Aと同様にfor文を回すだけです。オーバーフローの心配もありません。 C カンマ区切り Goでは、以下の関数を使って文字列スライス($s$)を受け取って、カンマ区切りの文字列を求めることができます。 この関数を使うため、入力を数値ではなく文字列として受け取るのがポイントと思います。 strings.Join(s, ",") D ソート ソート自体はpackage sortのsort.Intsでお手軽に昇順ソートができます。 面倒なのは、それをスペース区切りで表示しないといけないところで、もう既に数値として受け取っているため、C問題のような方法は使えないです。素直にfor文を回す必要があります。順位表の上から何人かのC++のコードを眺めたところスペース区切りを出力する関数を定義して呼び出す人はいらっしゃらず、for文で丁寧に出力していました。以下のように、最初にスペースを出力するかの条件文を書くのがよさそうです。 for i := 0; i < n; i++ { if i > 0 { fmt.Fprintf(writer, " ") } fmt.Fprintf(writer, "%v", a[i]) } fmt.Fprintf(writer, "\n") E 1は何番目? 他の問題と同様に、for文を回すだけです。1が複数現れるパターンはトップページの共通制約($i\ne j \Rightarrow a_i\ne a_j$)により存在しないため、「最初に見つかった位置+1」 を返せばいいです。 F 見える数 ここから、制約を意識する必要があり、かつ問題文が難解であるため、急に難化した印象を受けます。 問題文が難解ですが、要するに数列のある位置にある数値が、それより左にある全ての数値より大きければ1カウントします。合計何カウントですか、という問題です。最初の入力例 3 1 5 4 2 なら 3(左に数がない) と 5(左にある3,1より大きい) で答えは2になります。 各iに対してjを見ていくと、$O(n^2)$になり、TLE(Time Limit Exceeded)します。そのため、計算量を落とすための前処理として、左から各位置での累積max $c$を計算します。$c$を計算後、各$a_i$ごとに$c_{i-1}$を比較して、$a_i$がほうが大きいときをカウントすることで $O(n)$で答えを求めることができます。 c := make([]int, n) copy(c, a) for i := 1; i < n; i++ { c[i] = max(c[i], c[i-1]) } ans := 0 for i := 0; i < n; i++ { if i == 0 { ans++ continue } if c[i-1] < a[i] { ans++ } } G あまり 連結した整数の桁数は $n = 20$ で 30桁程度あり, int型で扱える整数は高々19桁であるため簡単にオーバーフローします。 そのため、以下の手順で答えを求めます。 入力を文字列で受け取り、連結した整数を文字列で表す 19 11 10 7 8 9 -> "191110789" それらを桁ごとに分解し、整数型スライスで管理 "19110789" -> []int{1,9,1,1,0,7,8,9} 桁ごとに、各桁の数字 * powMod(10, 何桁目か)を求め合計する。 手順が煩雑ですが、要は巨大な数の余りは直接求めることはできないので、余りの加算乗算は自由に行えることを利用して、桁ごとに分解して($321$ なら $3*10^{2}+2*10^{1}+1*10^{0}$)、各桁の余りを求め、加算して余りを求めます。powMod は冪剰余を求める関数で、単純な累乗では当初の問題と同様にオーバーフローしてしまうため、使用しています。 func powMod(a, x, d int) int { var r int = 1 for x > 0 { if x&1 == 1 { r = r * a % d } a = a * a % d x >>= 1 } return r } H LIS 最長増加部分列の文字数を求める問題であり、蟻本にもある有名な典型問題です。 ライブラリにしているものをそのまま使用しました。 以下の実装は、典型90 を借用し、Goで書き換えたコードですが、INF値を使わなくていい点や、追加される一文字ごとのLISの文字数が求まり、応用しやすい点で気に入ってます。 func lis(a []int) (lis []int) { lis = make([]int, len(a)) b := make([]int, 0) for i := 0; i < len(a); i++ { cnt := sort.Search(len(b), func(j int) bool { return a[i] < b[j] }) if cnt == len(b) { b = append(b, a[i]) } else { b[cnt] = a[i] } lis[i] = cnt + 1 } return } I 和がNの区間 区間の和がN以下を真とした、しゃくとり法が使えます。 実装は以下を参考にし、queue を用いています。 https://qiita.com/keroru/items/6e0a22e8c9bf2a24dc68 q := queue{} ans := 0 sum := 0 for i := 0; i < n; i++ { q.push(a[i]) sum += a[i] for !q.empty() && sum > n { sum -= q.pop() } if sum == n { ans++ } } fmt.Fprintf(writer, "%v\n", ans) type queue []int func (q *queue) push(n int) { *q = append(*q, n) } func (q *queue) pop() int { v := (*q)[0] (*q) = (*q)[1:] return v } func (q *queue) front() int { return (*q)[0] } func (q *queue) empty() bool { return len(*q) == 0 } J 転倒数 バブルソートでソートするのに必要なスワップ数と転倒数は同値です。 その転倒数の求め方ですが、以下が詳しいです。 https://scrapbox.io/pocala-kyopro/%E8%BB%A2%E5%80%92%E6%95%B0 私はBITの実装を持っていなかったのでセグ木を区間の加算を求めるものにして代用しました。 余談ですが、バブルソート自体は最悪$O(n^2)$に対して、スワップ数は$O(nlog(n))$で求まるのは面白いですね。 type RMQ struct { n int x []int unit int op func(x ...int) int } func add(arg ...int) (sum int) { for i := range arg { sum += arg[i] } return } func (rmq *RMQ) Create(seq []int) { rmq.n = len(seq) rmq.x = make([]int, len(seq)*2) rmq.unit = 0 rmq.op = add for i := range rmq.x { rmq.x[i] = rmq.unit } for i, x := range seq { rmq.x[i+len(seq)] = x } for i := len(seq) - 1; i > 0; i-- { rmq.x[i] = rmq.op(rmq.x[i<<1], rmq.x[i<<1|1]) } } // i 番目の要素をxに更新。O(log(n)) func (rmq *RMQ) Update(i, x int) { i += rmq.n rmq.x[i] = x for i > 1 { i >>= 1 rmq.x[i] = rmq.op(rmq.x[i<<1], rmq.x[i<<1|1]) } } // [l,r) での最小の要素を取得。O(log(n)) func (rmq *RMQ) Query(l, r int) int { l += rmq.n r += rmq.n vl := rmq.unit vr := rmq.unit for l < r { if l&1 > 0 { vl = rmq.op(vl, rmq.x[l]) l += 1 } if r&1 > 0 { r -= 1 vr = rmq.op(rmq.x[r], vr) } l >>= 1 r >>= 1 } return rmq.op(vl, vr) } tree := RMQ{} tree.Create(make([]int, n)) ans := 0 for i := 0; i < n; i++ { ans += tree.Query(a[i]-1, n) tree.Update(a[i]-1, 1) } fmt.Fprintf(writer, "%v\n", ans) K 辞書順で何番目? 入力例1で説明します。31542が辞書順で何番目にくるかを最小の12345から初めて、左から順に数を固定する感じで数え上げていきます。 3xxxx になるまえに、1xxxx, 2xxxx のパターンがあるためそれらを数えると 2 * 4! 通り 31xxx は 1より小さい数字がないため、1通り 315xx になるまえに、312xx, 314xx のパターンがあるためそれらを数えると 2 * 2! 通り 3154x になるまえに、3152x のパターンがあるため、1 * 1 通り よって答えは54通りになります。これをどうプログラミングするかですが、上記から、右に進むたびに固定した数字と固定する予定の数字は使えないことがわかります。固定する予定の数字は自分自身であるため考えないとして、この固定された数字の個数をどう求めるかですが、前問の転倒数の数え方がヒントになっています。つまり、右に進むたびにBITやセグ木で固定した数字を記録し、必要になるたびに固定した数字の個数を求めればいいです。これで $O(nlog(n))$ で問題を解くことができます。また、階乗計算ですが、何度も計算をやり直すと 右に進むたびに、$O(n)$かかってしまうため、初歩的なDPで一度計算した結果をメモするといいです。 factMod := make([]int, n) factMod[0] = 0 factMod[1] = 1 for i := 2; i < n; i++ { factMod[i] = i * factMod[i-1] % mod } ans := 0 for i := 0; i < n; i++ { used := tree.Query(0, a[i]) + 1 tree.Update(a[i]-1, 1) ans += (a[i] - used) * factMod[n-i-1] ans %= mod } ans++ // 辞書順x番目は 1-indexed fmt.Fprintf(writer, "%v\n", ans) L N回スワップ Jの問題と違い、任意の2つの要素のスワップの問題です。隣り合う2つの要素のスワップの問題ではないです。 ちなみに、そう勘違いして転倒数を求めて解いても何故かACできます。(最初そうやってACしました...) 以下のように、スワップ数($c$)を求めていきます。1回スワップするたびに、少なくとも1つの数が正しい位置に移動するため $c$は高々$n$回ですみます。$c=n$のとき、そのままYESを返し、$n$以下なら偶数回スワップしてn回スワップにできるので、$n-c$ の偶奇でYES/NOを返せばいいです。 cnt := 0 for i := 0; i < n; i++ { for a[i] != i+1 { a[i], a[a[i]-1] = a[a[i]-1], a[i] cnt++ } } 次回 https://atcoder.jp/contests/chokudai_S002 につづく...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者による初心者のためのGo|製作途中だからどんどん書き加えていくよ

Goの基礎を備忘録ってことで、綴っていきます。 初心者なので、間違ってたりしたら教えてください。 変数を宣言する 明示的に変数を定義する場合 var [NAME] [TYPE] 暗黙的に変数を定義する場合 [NAME] := [VALUE] 後者に関しては、型を意識する必要がない。 条件分岐 IF 単純にこう。 if <条件式> { <処理> } else if <条件式> { <処理> } else { <処理> } ちなみに、複数の条件式があり、ANDを使いたい場合は&&とすれば良し。 ORであれば、||でOK。 配列の長さを取得する <長さを格納する変数> := len(<サイズを取得したい変数>) コマンドライン引数を受け取る FLAG編 まずは基本から package main import "flag" import "fmt" func main() { flag.Parse() argv := flag.Args() fmt.Println(argv) } コマンドライン引数が渡されなかったときのエラー処理 package main import "fmt" import "flag" func main() { flag.Parse() args := flag.Args() args_size := len(args) if args_size == 0 { fmt.Printf("エラーだよ :( ") } fmt.Println(args) } コマンドライン引数を受け取る OS編 FLAGのやり方よりも簡単そうです。 fmt.Println(os.Args) os.Args に関しては、1つ目(os.Args[0]で指定するもの)はファイル名みたいなものになってるから注意。 つまり、引数自体は[1]以降である。 制御構文 FOR 基本はCのFOR文と同じみたいね ※n回ループする for i := 0; i < n; i++ { <処理> } 文字を表示させる Cのように、書式指定子があるっぽい「が、」全て%vで解決するらしい! つまり、これで全てが解決するってこと。 fmt.Printf("%v\n", <表示させたい内容>) 文字列の正規表現 あまり解説しているサイトがないからちょっと深く掘っていく 詳しくはここを見たらいいと思う Goの正規表現【プログラミング初心者向け教材】 Goには標準で「MatchString関数」が入ってあるからこれを使う。 (regexpをIMPORTする必要あり!) この関数はTrueかFalseを返すのでその帰り値によってIFとかやればいいと思う。 str := "I'm LabPixel." //確認する文字 regular := regexp.MustCompile("LabPixel") //正規表現確認の変数を作る regular.MatchString(str) //TrueもしくはFalseを返す BOOL型の比較 例えば、前のやつでregular.MatchString(str)がBOOLだけど、これをIF文で条件分岐させる。 if regular.MatchString(str) == true { <処理> } else if regular.MatchString(str) == false { <処理> } このようにすればよい。 注意点としては、Trueとか"True"とかしないこと! 文字列を数値に変換する AtoI コマンドライン引数から受け取った文字列を数値に変換したいことってよくあるよねって話。 num, err := strconv.Atoi("123") fmt.Println(num) よくerrのとこを_って書いてる記事あるけど、これはエラーが出た場合に返すための変数。 開発元の説明書に書いてあります↓ https://pkg.go.dev/strconv このerrのなかにnilが入ると(err == nilが正の時)正常終了したことを意味する 複数関数を作りたいときっ! 関数をメインの外に作っ使いたいときって結構ある。 その時もpackage mainのところは変える必要がないみたい。 package main import "fmt" func ft_print(str interface{}) { fmt.Println(str) } func main() { ft_print("HelloWorld") } こんな感じで、メインの中で呼び出す。 関数の引数と戻り値について 他に関数が作れることはわかったが、引数の渡し方について書く。 func <関数名> (<引数の名前> <引数の型>) (<戻り値1の型> ... <戻り値3の型>) { <処理> return <戻り値1> ... <戻り値3> } たくさんImportする グループ化 沢山パッケージを使いたいときってよくあります。 import ( "fmt" "os" ) のようにすればOKです! これはグループ化と呼ばれる方法です。 Importの詳しい解説がここにありました! 【Go】import 書き方まとめ 関数のエラー処理で「!変数名」って形 まず、!変数名の形はBOOL型変数しか使えないらしいから注意。 だから、こんな感じでエラー処理を書いていく。 package main import ( "fmt" "os" ) func hoge(str_number []string) (string, bool) { fmt.Println(str_number) return_string := "なんか出力" return return_string, true } const ERROR_MSG string = "Arguments is invalid." func main() { s, ok := calculationStr(os.Args) //戻り値2つを取る if !ok { //OKが無かったら <エラーの時の処理> } fmt.Print(s) } 文字列連結 型が同じであれば、+で連結できる。 例えば、連結して変数に入れることもできる。 space := " " apple_pen := "apple" + spece + "pen" スライス スライスを扱う。 このような感じで、範囲を指定していく var orig = []int{0, 1, 2, 3, 4, 5} fmt.Printf(orig[0:6]) スライスの作り方 空のスライスを容量と容量を決めて作成する。 スライスではlength(長さ)とcapacity(容量)があって、この2つには違いがある。 lenは実際のスライスの長さ、capはスライスを実装するために内部的に準備している配列の長さ。 myOwnSlice := make([]int, 0, 10) このような感じで容量と内容を決めて空のスライスを作成することが出来る! スライスで要素を追加 APPEND このような感じで、要素を追加できる。 ただしappendに関しては、新しい配列を返すので、そこだけ注意。 func main(){ old_slice := [] string{"a", "b"} new_slice := append(old_slice, "c") fmt.Println(new_slice) } このように新しく変数を作ってやるとうまくいく。 複数個付け加えたいなら、append(old_slice, "c", "d")とすると良き。 スライスで要素を追加 追加する場所が決まっいるならappendを使わなくてもいける。 このような感じで書けばOK slice[追加したい場所] = <追加したい要素> コマンドラインオプション 書きかけ どうやらflagパッケージにて、オプションを作ったりするのが簡易化されてるっぽい 参考文献 同じ記事が複数あったらごめんなさい。 同じのを見たってことでw
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者による初心者のためのGo

Goの基礎を備忘録ってことで、綴っていきます。 初心者なので、間違ってたりしたら教えてください。 変数を宣言する 明示的に変数を定義する場合 var [NAME] [TYPE] 暗黙的に変数を定義する場合 [NAME] := [VALUE] 後者に関しては、型を意識する必要がない。 条件分岐 IF 単純にこう。 if <条件式> { <処理> } else if <条件式> { <処理> } else { <処理> } ちなみに、複数の条件式があり、ANDを使いたい場合は&&とすれば良し。 ORであれば、||でOK。 配列の長さを取得する <長さを格納する変数> := len(<サイズを取得したい変数>) コマンドライン引数を受け取る FLAG編 まずは基本から package main import "flag" import "fmt" func main() { flag.Parse() argv := flag.Args() fmt.Println(argv) } コマンドライン引数が渡されなかったときのエラー処理 package main import "fmt" import "flag" func main() { flag.Parse() args := flag.Args() args_size := len(args) if args_size == 0 { fmt.Printf("エラーだよ :( ") } fmt.Println(args) } コマンドライン引数を受け取る OS編 FLAGのやり方よりも簡単そうです。 fmt.Println(os.Args) os.Args に関しては、1つ目(os.Args[0]で指定するもの)はファイル名みたいなものになってるから注意。 つまり、引数自体は[1]以降である。 制御構文 FOR 基本はCのFOR文と同じみたいね ※n回ループする for i := 0; i < n; i++ { <処理> } 文字を表示させる Cのように、書式指定子があるっぽい「が、」全て%vで解決するらしい! つまり、これで全てが解決するってこと。 fmt.Printf("%v\n", <表示させたい内容>) 文字列の正規表現 あまり解説しているサイトがないからちょっと深く掘っていく 詳しくはここを見たらいいと思う Goの正規表現【プログラミング初心者向け教材】 Goには標準で「MatchString関数」が入ってあるからこれを使う。 (regexpをIMPORTする必要あり!) この関数はTrueかFalseを返すのでその帰り値によってIFとかやればいいと思う。 str := "I'm LabPixel." //確認する文字 regular := regexp.MustCompile("LabPixel") //正規表現確認の変数を作る regular.MatchString(str) //TrueもしくはFalseを返す BOOL型の比較 例えば、前のやつでregular.MatchString(str)がBOOLだけど、これをIF文で条件分岐させる。 if regular.MatchString(str) == true { <処理> } else if regular.MatchString(str) == false { <処理> } このようにすればよい。 注意点としては、Trueとか"True"とかしないこと! 文字列を数値に変換する AtoI コマンドライン引数から受け取った文字列を数値に変換したいことってよくあるよねって話。 var num int num, err = strconv.Atoi("123") fmt.Println(num) よくerrのとこを_って書いてる記事あるけど、これはエラーが出た場合に返すための変数。 開発元の説明書に書いてあります↓ https://pkg.go.dev/strconv 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む