言語を学ぶためのGoコードはそれほど多くはありません。私がそれを実験しているのは私だけではないと思います。ですから、言語について何か面白いものを見つけたら、ここに例を投稿してください。
私も探しています
- Goで物事を行う慣用的な方法、
- Goに「移植」されたC / C ++の考え方
- 構文に関する一般的な落とし穴、
- 本当に面白いものは何でも。
言語を学ぶためのGoコードはそれほど多くはありません。私がそれを実験しているのは私だけではないと思います。ですから、言語について何か面白いものを見つけたら、ここに例を投稿してください。
私も探しています
回答:
ステートメントを延期する
「defer」ステートメントは、周囲の関数が戻る瞬間まで実行が延期される関数を呼び出します。
DeferStmt = "defer" Expression。
式は、関数またはメソッドの呼び出しでなければなりません。「defer」ステートメントが実行されるたびに、関数呼び出しのパラメーターが評価され、新しく保存されますが、関数は呼び出されません。据え置き関数呼び出しは、周囲の関数が戻る直前、ただし戻り値がある場合はそれが評価された後、LIFO順に実行されます。
lock(l);
defer unlock(l); // unlocking happens before surrounding function returns
// prints 3 2 1 0 before surrounding function returns
for i := 0; i <= 3; i++ {
defer fmt.Print(i);
}
更新:
defer
また、現在処理するための慣用的な方法であるpanic
には例外のような方法:
package main
import "fmt"
func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i+1)
}
Goオブジェクトファイルには、実際にはクリアテキストヘッダーが含まれています。
jurily@jurily ~/workspace/go/euler31 $ 6g euler31.go
jurily@jurily ~/workspace/go/euler31 $ cat euler31.6
amd64
exports automatically generated from
euler31.go in package "main"
import
$$ // exports
package main
var main.coin [9]int
func main.howmany (amount int, max int) (? int)
func main.main ()
var main.initdone· uint8
func main.init ()
$$ // local types
type main.dsigddd_1·1 struct { ? int }
$$
!
<binary segment>
「なぜi = 0; i < len; i++
この時代になぜ私たちは言わなければならないのか」という流れに沿って、for-loopについて不平を言う人が何人かいます。
私は同意しません、私はfor構文が好きです。必要に応じて長いバージョンを使用できますが、慣用的なGoは
var a = []int{1, 2, 3}
for i, v := range a {
fmt.Println(i, v)
}
for .. range
索引-構築物は、すべての要素および消耗二つの値をループi
し、値v
。
range
マップとチャンネルでも機能します。
それでも、for
何らかの形で嫌いな場合は、数行each
でmap
などを定義できます。
type IntArr []int
// 'each' takes a function argument.
// The function must accept two ints, the index and value,
// and will be called on each element in turn.
func (a IntArr) each(fn func(index, value int)) {
for i, v := range a {
fn(i, v)
}
}
func main() {
var a = IntArr([]int{2, 0, 0, 9}) // create int slice and cast to IntArr
var fnPrint = func(i, v int) {
fmt.Println(i, ":", v)
} // create a function
a.each(fnPrint) // call on each element
}
プリント
0 : 2
1 : 0
2 : 0
3 : 9
私は多くのGoが好きになり始めています:)
range
for-3ループと同じコードにコンパイルされている場合にのみ、優れています。
これはこの回答の翻訳です。
package main
import (
"json"
"fmt"
"http"
"os"
"strings"
)
func die(message string) {
fmt.Printf("%s.\n", message);
os.Exit(1);
}
func main() {
kinopiko_flair := "https://stackoverflow.com/users/flair/181548.json"
response, _, err := http.Get(kinopiko_flair)
if err != nil {
die(fmt.Sprintf("Error getting %s", kinopiko_flair))
}
var nr int
const buf_size = 0x1000
buf := make([]byte, buf_size)
nr, err = response.Body.Read(buf)
if err != nil && error != os.EOF {
die(fmt.Sprintf("Error reading response: %s", err.String()))
}
if nr >= buf_size { die ("Buffer overrun") }
response.Body.Close()
json_text := strings.Split(string(buf), "\000", 2)
parsed, ok, errtok := json.StringToJson(json_text[0])
if ! ok {
die(fmt.Sprintf("Error parsing JSON %s at %s", json_text, errtok))
}
fmt.Printf("Your stackoverflow.com reputation is %s\n", parsed.Get ("reputation"))
}
.Read()のヘルプを提供してくれたScott Walesに感謝します。
2つの文字列と2つのバッファがあるため、これはかなり不格好に見えます。そのため、Goの専門家がアドバイスを持っている場合は、私に知らせてください。
gofmt
:-)あなたのコード
これは、Kinopikoの投稿からのiotaの良い例です。
type ByteSize float64
const (
_ = iota; // ignore first value by assigning to blank identifier
KB ByteSize = 1<<(10*iota)
MB
GB
TB
PB
YB
)
// This implicitly repeats to fill in all the values (!)
並列代入により変数を交換できます:
x, y = y, x
// or in an array
a[j], a[i] = a[i], a[j]
シンプルですが効果的です。
これは、Effective Goページのイディオムです
switch {
case '0' <= c && c <= '9':
return c - '0'
case 'a' <= c && c <= 'f':
return c - 'a' + 10
case 'A' <= c && c <= 'F':
return c - 'A' + 10
}
return 0
式が指定されていない場合、switchステートメントはtrueに切り替えます。したがって、これは
if '0' <= c && c <= '9' {
return c - '0'
} else if 'a' <= c && c <= 'f' {
return c - 'a' + 10
} else if 'A' <= c && c <= 'F' {
return c - 'A' + 10
}
return 0
現時点では、スイッチのバージョンは少しきれいに見えます。
Switch True
switch i := x.(type) {
case nil:
printString("x is nil");
case int:
printInt(i); // i is an int
case float:
printFloat(i); // i is a float
case func(int) float:
printFunction(i); // i is a function
case bool, string:
printString("type is bool or string"); // i is an interface{}
default:
printString("don't know the type");
}
パッケージをインポートするとき、名前を好きなように再定義できます。
package main
import f "fmt"
func main() {
f.Printf("Hello World\n")
}
名前付き結果パラメーター
Go関数の戻り値または結果の「パラメータ」には、入力パラメータと同様に、名前を付けて通常の変数として使用できます。名前が付けられると、関数の開始時にそれらの型のゼロ値に初期化されます。関数が引数なしでreturnステートメントを実行する場合、結果パラメーターの現在の値が戻り値として使用されます。
名前は必須ではありませんが、コードを短く明確にすることができます。これらはドキュメントです。nextIntの結果に名前を付けると、返されたintがどれであるかが明らかになります。
func nextInt(b []byte, pos int) (value, nextPos int) {
名前付きの結果は初期化され、飾り気のない戻り値に結び付けられているため、単純化および明確化できます。以下は、io.ReadFullを適切に使用するバージョンです。
func ReadFull(r Reader, buf []byte) (n int, err os.Error) {
for len(buf) > 0 && err == nil {
var nr int;
nr, err = r.Read(buf);
n += nr;
buf = buf[nr:len(buf)];
}
return;
}
foo := <-ch // This blocks.
foo, ok := <-ch // This returns immediately.
また、潜在的な落とし穴:受信オペレーターと送信オペレーターの微妙な違い:
a <- ch // sends ch to channel a
<-ch // reads from channel ch
/*
* How many different ways can £2 be made using any number of coins?
* Now with 100% less semicolons!
*/
package main
import "fmt"
/* This line took me over 10 minutes to figure out.
* "[...]" means "figure out the size yourself"
* If you only specify "[]", it will try to create a slice, which is a reference to an existing array.
* Also, ":=" doesn't work here.
*/
var coin = [...]int{0, 1, 2, 5, 10, 20, 50, 100, 200}
func howmany(amount int, max int) int {
if amount == 0 { return 1 }
if amount < 0 { return 0 }
if max <= 0 && amount >= 1 { return 0 }
// recursion works as expected
return howmany(amount, max-1) + howmany(amount-coin[max], max)
}
func main() {
fmt.Println(howmany(200, len(coin)-1))
}
intなどのプリミティブを含む型を何度でも再定義して、さまざまなメソッドをアタッチできるのが気に入っています。RomanNumeral型を定義するように:
package main
import (
"fmt"
"strings"
)
var numText = "zero one two three four five six seven eight nine ten"
var numRoman = "- I II III IV V VI VII IX X"
var aText = strings.Split(numText, " ")
var aRoman = strings.Split(numRoman, " ")
type TextNumber int
type RomanNumber int
func (n TextNumber) String() string {
return aText[n]
}
func (n RomanNumber) String() string {
return aRoman[n]
}
func main() {
var i = 5
fmt.Println("Number: ", i, TextNumber(i), RomanNumber(i))
}
どちらが印刷されるか
Number: 5 five V
RomanNumber()
コールは、それがint型のより具体的な型としてint型を再定義し、基本的にキャストです。そして、舞台裏にPrintln()
電話しますString()
。
これは非常に重要な真のイディオムです。データをチャネルに送り、後で閉じる方法です。これにより、単純なイテレーター(範囲はチャネルを受け入れるため)またはフィルターを作成できます。
// return a channel that doubles the values in the input channel
func DoublingIterator(input chan int) chan int {
outch := make(chan int);
// start a goroutine to feed the channel (asynchronously)
go func() {
for x := range input {
outch <- 2*x;
}
// close the channel we created and control
close(outch);
}();
return outch;
}
チャネル読み取りのタイムアウト:
ticker := time.NewTicker(ns);
select {
case v := <- chan_target:
do_something_with_v;
case <- ticker.C:
handle_timeout;
}
デイヴィス・リューから盗まれた。
$ GOROOT / srcで使用できるmakeシステムがセットアップされています
でメイクファイルを設定する
TARG=foobar # Name of package to compile
GOFILES=foo.go bar.go # Go sources
CGOFILES=bang.cgo # Sources to run cgo on
OFILES=a_c_file.$O # Sources compiled with $Oc
# $O is the arch number (6 for x86_64)
include $(GOROOT)/src/Make.$(GOARCH)
include $(GOROOT)/src/Make.pkg
その後、make testを実行して自動テストツールを使用するか、make installを使用してcgoから$ GOROOTにパッケージと共有オブジェクトを追加できます。
Goのもう1つの興味深い点は、それgodoc
です。あなたはそれを使ってあなたのコンピュータのウェブサーバーとしてそれを動かすことができます
godoc -http=:8080
ここで、8080はポート番号で、golang.orgのWebサイト全体はで利用できますlocalhost:8080
。
これはスタックの実装です。型にメソッドを追加する方法を示しています。
スタックの一部をスライスにしてスライスのプロパティを使用したかったのですが、なしで機能させるtype
ことはできましたが、でスライスを定義するための構文がわかりませんでしたtype
。
package main
import "fmt"
import "os"
const stack_max = 100
type Stack2 struct {
stack [stack_max]string
size int
}
func (s *Stack2) push(pushed_string string) {
n := s.size
if n >= stack_max-1 {
fmt.Print("Oh noes\n")
os.Exit(1)
}
s.size++
s.stack[n] = pushed_string
}
func (s *Stack2) pop() string {
n := s.size
if n == 0 {
fmt.Print("Underflow\n")
os.Exit(1)
}
top := s.stack[n-1]
s.size--
return top
}
func (s *Stack2) print_all() {
n := s.size
fmt.Printf("Stack size is %d\n", n)
for i := 0; i < n; i++ {
fmt.Printf("%d:\t%s\n", i, s.stack[i])
}
}
func main() {
stack := new(Stack2)
stack.print_all()
stack.push("boo")
stack.print_all()
popped := stack.pop()
fmt.Printf("Stack top is %s\n", popped)
stack.print_all()
stack.push("moo")
stack.push("zoo")
stack.print_all()
popped2 := stack.pop()
fmt.Printf("Stack top is %s\n", popped2)
stack.print_all()
}
fmt.Printf(...); os.Exit();
できますpanic(...)
。
GoからCコードを呼び出す
cランタイムを使用して、低レベルのgoにアクセスできます。
C関数は次の形式です
void package·function(...)
(ドットセパレーターはユニコード文字であることに注意してください)引数は基本的なgoタイプ、スライス、文字列などです。値の呼び出しを返すには
FLUSH(&ret)
(複数の値を返すことができます)
たとえば、関数を作成するには
package foo
bar( a int32, b string )(c float32 ){
c = 1.3 + float32(a - int32(len(b))
}
Cではあなたが使う
#include "runtime.h"
void foo·bar(int32 a, String b, float32 c){
c = 1.3 + a - b.len;
FLUSH(&c);
}
ただし、関数はgoファイルで宣言する必要があり、メモリは自分で処理する必要があります。これを使用して外部ライブラリを呼び出すことが可能かどうかはわかりませんが、cgoを使用した方がよい場合があります。
ランタイムで使用されている例については、$ GOROOT / src / pkg / runtimeを参照してください。
goでc ++コードをリンクする方法については、この回答もご覧ください。
以下は、sqlite3パッケージを使用したgoの例です。
この話を見ましたか?それはあなたができるたくさんのクールなものを示しています(話の終わり)
他の回答に基づくスタックですが、スライスの追加を使用してサイズ制限はありません。
package main
import "fmt"
import "os"
type Stack2 struct {
// initial storage space for the stack
stack [10]string
cur []string
}
func (s *Stack2) push(pushed_string string) {
s.cur = append(s.cur, pushed_string)
}
func (s *Stack2) pop() (popped string) {
if len(s.cur) == 0 {
fmt.Print("Underflow\n")
os.Exit(1)
}
popped = s.cur[len(s.cur)-1]
s.cur = s.cur[0 : len(s.cur)-1]
return
}
func (s *Stack2) print_all() {
fmt.Printf("Stack size is %d\n", len(s.cur))
for i, s := range s.cur {
fmt.Printf("%d:\t%s\n", i, s)
}
}
func NewStack() (stack *Stack2) {
stack = new(Stack2)
// init the slice to an empty slice of the underlying storage
stack.cur = stack.stack[0:0]
return
}
func main() {
stack := NewStack()
stack.print_all()
stack.push("boo")
stack.print_all()
popped := stack.pop()
fmt.Printf("Stack top is %s\n", popped)
stack.print_all()
stack.push("moo")
stack.push("zoo")
stack.print_all()
popped2 := stack.pop()
fmt.Printf("Stack top is %s\n", popped2)
stack.print_all()
}
const ever = true
for ever {
// infinite loop
}
for { /* infinite loop */ }
十分です
forever
キーワードが好きです。Qtにもそのためのマクロがあります。
for ever
(変数を宣言した後)必要に応じてGoで実行できるかわいいことです。それは英語のように見えます(ブランクを法として)。
#define ever (;;)
test
メインディレクトリにはたくさんの小さなプログラムがあります。例:
peano.go
階乗を出力します。hilbert.go
行列の乗算があります。iota.go
奇妙なイオタの例があります。