web-dev-qa-db-ja.com

配列へのポインターを使用する

私はグーグルのGo言語で少し遊んでいて、Cでかなり基本的なものに遭遇しましたが、これまで見たドキュメントではカバーされていないようです

スライスへのポインタを関数に渡すとき、次のようにアクセスする方法があると推測しました。

func conv(x []int, xlen int, h []int, hlen int, y *[]int)

    for i := 0; i<xlen; i++ {
        for j := 0; j<hlen; j++ {
            *y[i+j] += x[i]*h[j]
        }
    }
 }

しかし、Goコンパイラはこれを好まない:

sean@spray:~/dev$ 8g broke.go
broke.go:8: invalid operation: y[i + j] (index of type *[]int)

結構です-それは単なる推測でした。私はかなり簡単な回避策を持っています:

func conv(x []int, xlen int, h []int, hlen int, y_ *[]int) {
    y := *y_

    for i := 0; i<xlen; i++ {
        for j := 0; j<hlen; j++ {
            y[i+j] += x[i]*h[j]
        }
    }
}

しかし、確かにもっと良い方法があります。面倒なことは、ほとんどの検索用語であらゆる種類のC/C++ /無関係な結果が表示されるため、Goの情報をグーグルで検索してもあまり役に立たないことです。

32
Sean

Google Goのドキュメント 配列の受け渡しについて次のように述べています -彼らは通常、(ポインタではなく)スライスを渡したいと言っています:

更新しました:

@Chickenchaのコメントで示されているように、配列スライスは参照であるため、渡すのに効率的です。したがって、「生の」ポインタの代わりにスライスメカニズムを使用することになるでしょう。

FromGoogle Effective Go dochttp://golang.org/doc/effective_go.html#slices

スライスは参照タイプです


元の

見出しの下にあります

タイプについての幕間

[... snip ...]配列を関数に渡すとき、ほとんどの場合、仮パラメータをスライスとして宣言する必要があります。関数を呼び出すとき、配列のアドレスを取得すると、Goは(効率的に)スライス参照を作成し、それを渡します。

編集者注:これはもはや事実ではありません

スライスを使用して、この関数を(sum.goから)作成できます。

09    func sum(a []int) int {   // returns an int
10        s := 0
11        for i := 0; i < len(a); i++ {
12            s += a[i]
13        }
14        return s
15    }

次のように呼び出します:

19        s := sum(&[3]int{1,2,3})  // a slice of the array is passed to sum    

代わりに、配列全体をスライスとして渡すこともできます。 Googleは、Goがスライスを効率的に処理することを示しています。これは質問に対する別の回答ですが、たぶんより良い方法です。

25
John K

[]などの[]intが空のタイプは、実際にはスライスであり、配列ではありません。 Goでは、配列のサイズは型の一部であるため、実際に配列を作成するには、[16]intのようなものが必要であり、そのポインターは*[16]intになります。したがって、実際に既に行っていることはスライスの使用であり、スライスへのポインター*[]intは不要です。スライスは既に参照渡しされているからです。

また、&arrayを使用して、配列全体を参照するスライスを簡単に渡すことができます(スライスの要素タイプが配列の要素タイプと一致する場合)。 (もう違います。)

例:

package main
import "fmt"

func sumPointerToArray(a *[8]int) (sum int) {
    for _, value := range *a { sum += value }
    return
}
func sumSlice (a []int) (sum int) {
    for _, value := range a { sum += value }
    return
}
func main() {
    array := [...]int{ 1, 2, 3, 4, 5, 6, 7, 8 }
    slice := []int{ 1, 2, 3, 4 }
    fmt.Printf("sum arrray via pointer: %d\n", sumPointerToArray(&array))
    fmt.Printf("sum slice: %d\n", sumSlice(slice))
    slice = array[0:]
    fmt.Printf("sum array as slice: %d\n", sumSlice(slice))
}

編集:Goが最初に投稿されてからの変更を反映するように更新されました。

15
Arkku

セミコロンとアスタリスクが追加および削除されます。

* y [i + j] + = x [i] * h [j]
->
(* y)[i + j] + = x [i] * h [j];

5
Behrooz

動作するGoプログラムを次に示します。

package main

import "fmt"

func conv(x, h []int) []int {
    y := make([]int, len(x)+len(h)-1)
    for i := 0; i < len(x); i++ {
        for j := 0; j < len(h); j++ {
            y[i+j] += x[i] * h[j]
        }
    }
    return y
}

func main() {
    x := []int{1, 2}
    h := []int{7, 8, 9}
    y := conv(x, h)
    fmt.Println(len(y), y)
}

間違った推測を避けるには、Goのドキュメントを読んでください。 The Go Programming Language。

2
peterSO

長さは配列の型の一部であり、len()組み込み関数により配列の長さを取得できます。したがって、xlen、hlen引数を渡す必要はありません。

Goでは、関数に配列を渡すときにほとんど常にスライスを使用できます。この場合、ポインターは必要ありません。実際、y引数を渡す必要はありません。 Cが配列を出力する方法です。

Goスタイル:

func conv(x, h []int) []int {
    y := make([]int, len(x)+len(h))
    for i, v := range x { 
        for j, u := range h { 
            y[i+j] = v * u 
        }   
    }   
    return y
}

関数を呼び出します。

conv(x[0:], h[0:])
2
Stephen Hsu