Örneklere ve deyimlere gidin [kapalı]


91

Dili öğrenmek için çok fazla Go kodu yok ve eminim onu ​​deneyen tek kişi ben değilim. Bu yüzden, dil hakkında ilginç bir şey bulursanız, lütfen buraya bir örnek gönderin.

Ben de arıyorum

  • Go'da bir şeyler yapmanın deyimsel yolları,
  • Go için "taşınan" C / C ++ tarzı düşünme,
  • sözdizimi ile ilgili yaygın tuzaklar,
  • gerçekten ilginç bir şey.

8 bit veya 16 bit gibi ARM desteği. D dili hala yok.

1
Kütüphane ( golang.org/pkg ) go'nun nasıl kullanıldığını öğrenmek için mükemmel bir kaynaktır. Kişisel olarak, veri yapılarının nasıl uygulandığını öğrenmenin dili öğrenmek için yararlı olduğunu düşünüyorum.
tkokasih

Yanıtlar:


35

İfadeleri ertele

Bir "erteleme" ifadesi, yürütülmesi çevreleyen işlevin döndüğü ana kadar ertelenen bir işlevi çağırır.

DeferStmt = "erteleme" İfadesi.

İfade bir işlev veya yöntem çağrısı olmalıdır. "Erteleme" ifadesi her yürütüldüğünde, işlev çağrısının parametreleri değerlendirilir ve yeniden kaydedilir, ancak işlev çağrılmaz. Ertelenen işlev çağrıları, çevreleyen işlev dönmeden hemen önce, ancak varsa, dönüş değerleri değerlendirildikten sonra LIFO sırasına göre yürütülür.


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);
}

Güncelleme:

deferşimdi aynı zamanda istisnaipanic bir şekilde başa çıkmanın deyimsel yoludur :

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)
}

17
Eski güzel RAII'ye benziyor (açıkça belirtilmiş).
Konrad Rudolph

4
Go hakkında çok şey okuduğumdan beri +1, ama yine de görmedim (sen bana gösterene kadar)!
u0b34a0f6ae

Zekice, ama FIFO sırasına göre (yukarıdan aşağıya) yürütülen ifadeleri ertelemek bana daha mantıklı gelse de, ama belki bu sadece benim ...
Mike Spross


4
@Mike: "try: .. nihayet:" bloklarıyla karşılaştırırsanız LIFO da aynı şekilde yuvalar. Kaynak açma / kapama çiftleri vb. İçin, mantıklı olan tek şey bu şekilde iç içe geçmektir (İlk açılış en son kapanacaktır).
u0b34a0f6ae

25

Go nesne dosyaları aslında bir açık metin başlığı içerir:

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>

6
Bu, deyimsel bir örnekten çok gizli bir özelliğe benziyor
hasen

22

"Bugün i = 0; i < len; i++ve bu çağda neden bunu söylemek zorundayız ?"

Katılmıyorum, inşa etmeyi seviyorum. İsterseniz uzun sürümü kullanabilirsiniz, ancak deyimsel Go şudur:

var a = []int{1, 2, 3}
for i, v := range a {
    fmt.Println(i, v)
}

for .. rangeEndeksi - yapı tüm elemanları ve malzemeleri iki değeri döngüler ive değer v.

range ayrıca haritalar ve kanallar üzerinde de çalışır.

Yine de, forherhangi bir biçimde beğenmezseniz each, mapbirkaç satırda tanımlayabilirsiniz :

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
}

baskılar

0 : 2
1 : 0
2 : 0
3 : 9

Go'yu çok sevmeye başladım :)


Yine de range, sadece for-3 döngüsü ile aynı koda derlenmişse güzeldir.
Thomas Ahle

19

Git ve stackoverflow itibarınızı kazanın

Bu, bu cevabın tercümesidir .

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 () ile ilgili yardım için Scott Wales'e teşekkürler .

Bu, iki dizeli ve iki tamponla hala oldukça hantal görünüyor, bu nedenle herhangi bir Go uzmanının tavsiyesi varsa, bana bildirin.


Biçimlendirmede neyin yanlış olacağından emin değilim; Onu restore ettim.

5
Go yazarları gofmtkodunuzu öneriyor :-)
ℝaphink

Derleyemiyorum: $ ../go/src/cmd/6g/6g SO.go SO.go: 34: undefined: json.StringToJson
ℝaphink

@Raphink: Bunu yaptığımdan beri dil değişti.

Evet, StringToJson'a en yakın eşdeğeri nedir biliyor musunuz? Eskiden dahili olarak bir kurucu kurardı, şimdi önceden tanımlanmış bir yerel yapıya sahip olmak zorunda mı?
macbirdie

19

İşte Kinopiko'nun gönderisinden güzel bir iota örneği :

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 (!)

5
Noktalı virgüllerin gereksiz olduğunu unutmayın.
mk12

18

Değişkenleri paralel atama ile takas edebilirsiniz:

x, y = y, x

// or in an array
a[j], a[i] = a[i], a[j]

basit ama etkili.


18

İşte Effective Go sayfasından bir deyim

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 ifadesi, herhangi bir ifade verilmediğinde true olarak açılır. Yani bu eşdeğerdir

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

Şu anda, anahtar versiyonu bana biraz daha temiz görünüyor.


6
Whoa, VB'den tamamen kopmuş. ;-) ( Switch True…)
Konrad Rudolph

@Konrad, beni yen! :) Bu deyimi daha önce VB6 kodunda kullandım ve belirli durumlarda okunabilirliğe kesinlikle yardımcı olabilir.
Mike Spross

'<=' Nedir? "<-" ile mi ilişkili?
ℝaphink

@Raphink: küçüktür veya eşittir.
Paul Ruane

17

Tip anahtarları :

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");
}


14

Adlandırılmış sonuç parametreleri

Bir Go işlevinin dönüş veya sonuç "parametrelerine" adlar verilebilir ve gelen parametreler gibi normal değişkenler olarak kullanılabilir. Adlandırıldıklarında, işlev başladığında türleri için sıfır değerleriyle başlatılırlar; işlev bağımsız değişken içermeyen bir dönüş ifadesi çalıştırırsa, sonuç parametrelerinin geçerli değerleri döndürülen değerler olarak kullanılır.

İsimler zorunlu değildir, ancak kodu daha kısa ve daha net hale getirebilirler: belgelerdir. NextInt'in sonuçlarını adlandırırsak, döndürülen int'in hangisi olduğu açık hale gelir.

func nextInt(b []byte, pos int) (value, nextPos int) {

Adlandırılmış sonuçlar başlatıldığından ve süslenmemiş bir dönüşe bağlandığından, basitleştirmenin yanı sıra açıklığa kavuşturabilirler. İşte onları iyi kullanan bir io.ReadFull sürümü:

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;
}

1
Merak ediyorum - başka bir dilde buna sahip mi?
u0b34a0f6ae

1
Matlab'da benzer bir şey var.
Dan Lorenc

pascal, bir değer döndürmek için benzer sözdizimi kullanır.
nes1983

1
@ nes1983 Pascal'da bilmeyenler için, klasik olarak işlev adına dönüş değerini atarsınız.
2013

FORTRAN'da hemen hemen bu var.
Hut8

14

Gönderen James Antill cevabı :

foo := <-ch     // This blocks.
foo, ok := <-ch // This returns immediately.

Ayrıca, potansiyel bir tuzak: alma ve gönderme operatörleri arasındaki ince fark :

a <- ch // sends ch to channel a
<-ch    // reads from channel ch

3
Kendisi operatörü alacak olan Go 1.0.3 itibariyle, artık bir engelleme çalışma. Spesifikasyon değiştirildi: golang.org/ref/spec#Receive_operator . Lütfen buradan engelleme davranışını (kilitlenme) deneyin: play.golang.org/p/0yurtWW4Q3
2013

13
/* 
 * 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))
}

4
Sorun çözme sitesinin adını ve kimlik numarasını kaldırmanızı öneririm. Belki soruyu yeniden ifade edin. Tökezleyen birine sorunu mahvetmemek için. Ya da bu konudaki sorunu internette arayarak hile yapmaya çalışmak.
Mizipzor

1
Kayıt için: bu, algoritmaist.com/index.php/Coin_Change adresindeki algoritmadır. Bu, "bozuk para değişimi" için ilk Google sonucudur.
György Andrasek

13

İnt gibi ilkelleri içeren türleri istediğiniz kadar yeniden tanımlayabilmenizi ve farklı yöntemler ekleyebilmenizi seviyorum. RomanNumeral türünü tanımlamak gibi:

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))
}

Hangi çıktı

Number:  5 five V

RomanNumber()Çağrı o int daha spesifik bir türü olarak int türünü yeniden tanımlayan, esasen bir atıldı. Ve perde arkasını Println()çağırır String().


12

Bir kanalı iade etmek

Bu oldukça önemli olan gerçek bir deyimdir: bir kanala veri nasıl beslenir ve daha sonra nasıl kapatılır. Bununla basit yineleyiciler (çünkü aralık bir kanalı kabul edeceğinden) veya filtreler yapabilirsiniz.

// 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;
}

+1. Ayrıca, kanalları kanallardan da geçirebilirsiniz.
György Andrasek

5
Ancak, a for x: = aralık chan {} döngüsünden kopmamaya dikkat edin, gorutini ve başvurduğu tüm belleği sızdırırsınız.
Jeff Allen

3
@JeffAllen defer close(outch);, gorutinin ilk ifadesi olarak ne dersiniz?

1
Erteleme işlevi, hangi dönüş noktası alınırsa alınsın, işlev geri döndüğünde yürütme için bir ifadeyi sıraya alır. Ancak kanal girişi hiçbir zaman kapatılmazsa, bu örnekteki anonim işlev asla for döngüsünden ayrılmayacaktır.
Jeff Allen

11

Kanal okumaları için zaman aşımı:

ticker := time.NewTicker(ns);
select {
    case v := <- chan_target:
        do_something_with_v;
    case <- ticker.C:
        handle_timeout;
}

Davies Liu'dan çalındı .


11
for {
    v := <-ch
    if closed(ch) {
        break
    }
    fmt.Println(v)
}

Aralık otomatik olarak kapalı bir kanalı kontrol ettiğinden, buna kısaltabiliriz:

for v := range ch {
    fmt.Println(v)
}

9

$ GOROOT / src'de kullanabileceğiniz bir make sistemi kurulumu var

Makefile'ınızı şu şekilde ayarlayın:

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

Daha sonra otomatik test araçlarını make test çalıştırarak kullanabilir veya make install ile paketi ve paylaşılan nesneleri cgo'dan $ GOROOT'unuza ekleyebilirsiniz.


7

Go'daki bir başka ilginç şey de bu godoc. Bunu kullanarak bilgisayarınızda bir web sunucusu olarak çalıştırabilirsiniz.

godoc -http=:8080

8080 bağlantı noktası numarasıdır ve golang.org'daki web sitesinin tamamı daha sonra adresinde mevcuttur localhost:8080.


Bu normal bir program mı yoksa bir arka plan programı mı?
György Andrasek

Düzenli bir programdır.
Jeremy

7

Bu, bir yığının uygulamasıdır. Bir türe yöntem eklemeyi gösterir.

Yığın parçasını bir dilim haline getirmek ve dilimin özelliklerini kullanmak istedim, ancak bunu kullanmadan çalışsam da type, bir dilimi a ile tanımlamak için sözdizimini göremedim 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()
}

10
Kullanmak yerine fmt.Printf(...); os.Exit();kullanabilirsiniz panic(...).
notnoop

1
Bu, istemediğim bir yığın izi veriyor.

3
Neden sınırlı? Go, yönetilen, gc'd bir dildir. Yığınınız istediğiniz kadar derin olabilir. Gerektiğinde C'nin yeniden tahsis edilmesi gibi bir şey yapacak olan yeni append () yerleşikini kullanın.
Jeff Allen

"Git jeneriklere ihtiyaç duymaz" dediler.
cubuspl42

4

Hareket halindeyken c kodunu çağırma

Daha düşük go düzeyine c çalışma zamanını kullanarak erişmek mümkündür.

C fonksiyonları formdadır

void package·function(...)

(nokta ayırıcının bir unicode karakter olduğuna dikkat edin) burada argümanlar temel go türleri, dilimler, dizeler vb. olabilir. Bir değer çağrısı döndürmek için

FLUSH(&ret)

(birden fazla değer döndürebilirsiniz)

Örneğin, bir işlev oluşturmak için

package foo
bar( a int32, b string )(c float32 ){
    c = 1.3 + float32(a - int32(len(b))
}

C'de kullanırsın

#include "runtime.h"
void foo·bar(int32 a, String b, float32 c){
    c = 1.3 + a - b.len;
    FLUSH(&c);
}

İşlevi yine de bir go dosyasında bildirmeniz gerektiğini ve hafızayı kendiniz halletmeniz gerektiğini unutmayın. Bunu kullanarak harici kitaplıkları çağırmanın mümkün olup olmadığından emin değilim, cgo kullanmak daha iyi olabilir.

Çalışma zamanında kullanılan örnekler için $ GOROOT / src / pkg / runtime'a bakın.

Ayrıca c ++ kodunu go ile bağlamak için bu yanıta bakın .


3
Gerçekten "uçan nokta" kullanıyor mu? Düzenleme yapmaya cesaret edemem ama bu biraz beklenmedik ve radikal görünüyor.
gevşeyin

Evet, 6c (veya 8c, vb.) İle derlemeniz gerekir. Gcc'nin unicode tanımlayıcıları işlediğini sanmıyorum.
Scott Wales

1
AltGr + dönem türlerinin aynı olduğunu düşünüyorum · ama unicode ile emin değilim. Kaynakta okuduğumu görmek beni çok şaşırttı .. neden ::?
u0b34a0f6ae

Karakter ORTA NOKTA U + 00B7'dir. Ayrıştırıcı, geçerli bir c tanımlayıcısı yapmak için bunu bir karakter olarak görmesi için sahte olabilir, ki bence ::.
Scott Wales

4
'·' Sadece geçici bir hack, soygun hala orada olmasına şaşırdı, daha az kendine özgü bir şeyle değiştirileceğini söyledi.
uriel



3

Diğer yanıta dayalı, ancak boyut sınırı olmaması için dilim eklemeyi kullanan bir yığın.

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()
}

3
const ever = true

for ever {
    // infinite loop
}

25
ahem. for { /* infinite loop */ }yeterlidir.
u0b34a0f6ae

2
Elbette. Burada olan tam olarak bu. foreverAnahtar kelimeyi beğendim . Qt'nin bile bunun için bir makrosu var.
György Andrasek

6
ancak Go'nun bunu yapmak için bir makroya veya sevimli bir true takma adına ihtiyacı yoktur.
u0b34a0f6ae

@ kaizer.se: Jurily'nin noktası, for ever(değişkeni bildirdikten sonra), isterseniz Go'da yapabileceğiniz sevimli bir şey olmasıdır. İngilizce'ye benziyor (boşluğu modulo).
Frank

8
C #define ever (;;)
dilinde

2

testAna dizinde çok sayıda küçük program var . Örnekler:

  • peano.go factorials yazdırır.
  • hilbert.go bazı matris çarpımına sahiptir.
  • iota.go tuhaf iota şeylerinin örnekleri var.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.