可以用方括号对内部矩阵进行切片吗?

时间:2019-03-29 02:30:29

标签: go matrix slice

我正在修改矩阵的周长值,然后尝试递归到内部值。我希望可以使用matrix[1:3][1:3]之类的东西来访问内部值。事实并非如此,我对于Go如何处理顺序括号的基本逻辑有些困惑。

package main

import (
    "fmt"
)

var m = [][]int{
    []int{0, 1, 2, 3},
    []int{4, 5, 6, 7},
    []int{8, 9, 10, 11},
    []int{12, 13, 14, 15},
}

我正在尝试访问上述矩阵(“内部”矩阵)中的值5、6、9、10。

func main() {
    inner := m[1:3][1:3]
    fmt.Printf("%#v\n", inner)
    // Expected Output: [][]int{
    // []int{5, 6},
    // []int{9, 10}
    // }
    // Actual Ouput: [][]int{
    // []int{8, 9, 10, 11},
    // []int{12, 13, 14, 15}
    // }
    inner = m[1:3]
    fmt.Printf("%#v\n", inner)
    // Output:
    // [][]int{
    // []int{4, 5, 6, 7},
    // []int{8, 9, 10, 11}
    // }
    inner = innerMatrix(m)
    fmt.Printf("%#v\n", inner)
    // [][]int{
    // []int{5, 6},
    // []int{9, 10}
}

func innerMatrix(m [][]int) (inner [][]int) {
    innerRows := m[1:3]
    for _, row := range innerRows {
        inner = append(inner, row[1:3])
    }
    return
}

函数innerMatrix能够产生我期望的输出。我不知道为什么(1)m[1:3][1:3]效果不一样,(2)它似乎转换为m[2:4]。发生了什么事?

Playground

2 个答案:

答案 0 :(得分:1)

在Go中创建子切片时,您可以将该子切片再次增大回原始切片的容量,例如:

package main

import "fmt"

func main() {
    a := []int{1, 2, 3, 4, 5}

    b := a[1:3]
    fmt.Printf("a[1:3]: %v\n", b)

    c := b[1:3]
    fmt.Printf("b[1:3]: %v\n", c)
}

输出:

a[1:3]: [2 3]
b[1:3]: [3 4]

请注意,b仅包含两个元素,但是我们允许创建第二个元素和第三个元素的切片,因为该切片作为子切片的容量足够大,并且所有切片共享相同的基础数组。请参见this page

上“ Slice内部”一节中的最后一个示例

所以您的情况是m[1:3]等效于:

var m1 = [][]int{
    []int{4, 5, 6, 7},   // second element of m
    []int{8, 9, 10, 11}, // third element of m
}

m[1:3][1:3]等效于m1[1:3],等效于:

var m2 = [][]int{
    []int{8, 9, 10, 11},   // second element of m1
    []int{12, 13, 14, 15}, // "third" element of m1
}

出现“第三”元素仅是因为m的容量足以容纳它,并且实际上包含了它。如果m仅包含三个元素,则会引起恐慌。

换句话说,m[1:3][1:3]在这里与m[2:4]完全等效,因为m[1:3][1:3]为您提供m[1:3]的第二个和第三个元素。使用图表可能更容易理解:

m            : []int{1, 2, 3, 4}
m[1:3]       : []int{   2, 3   }
m[1:3][1:3]  : []int{      3, 4}
m[2:4]       : []int{      3, 4}

为简化起见,您可以想象方括号将所请求的元素放在其左侧的任何内容中,因此是一个极端的示例:

package main

import "fmt"

func main() {
    a := []int{1, 2, 3, 4, 5}

    b := a[1:5]
    fmt.Printf("b: %v, a[1:5]               : %v\n",
        b, a[1:5])

    c := b[1:4]
    fmt.Printf("c: %v  , a[1:5][1:4]          : %v\n",
        c, a[1:5][1:4])

    d := c[1:3]
    fmt.Printf("d: %v    , a[1:5][1:4][1:3]     : %v\n",
        d, a[1:5][1:4][1:3])

    e := d[1:2]
    fmt.Printf("e: %v      , a[1:5][1:4][1:3][1:2]: %v\n",
        e, a[1:5][1:4][1:3][1:2])
}

输出:

b: [2 3 4 5], a[1:5]               : [2 3 4 5]
c: [3 4 5]  , a[1:5][1:4]          : [3 4 5]
d: [4 5]    , a[1:5][1:4][1:3]     : [4 5]
e: [5]      , a[1:5][1:4][1:3][1:2]: [5]

答案 1 :(得分:1)

这是您的数据:

var m = [][]int{
    []int{0, 1, 2, 3},
    []int{4, 5, 6, 7},
    []int{8, 9, 10, 11},
    []int{12, 13, 14, 15},
}

首先,您要问内部是什么:= m [1:3] [1:3]?

好吧,一次取一个,m [1:3]是抓取元素1到3(不包括3)时得到的子切片。因此元素1和2是元素0。[] int {0,1 ,2、3},元素1为[] int {4、5、6、7},元素2为[] int {8、9、10、11},

所以m [1:3]是

[][]int{
    []int{4, 5, 6, 7},
    []int{8, 9, 10, 11},
}

现在,要获得m [1:3] [1:3],我们在此结果上重复同样的事情。 但是,这一次只有两个要素。我们再次需要元素1和2。我们跳过元素0,它是[] int {4,5,6,7}。元素1为[] int {8,9,10,11}。没有元素2。但是,m [1:3]的结果是一个子切片,而基础切片中还有一个附加元素。 因此,通过扩展此子切片,我们可以再次获取该元素,这就是被隐藏的元素2,即[] int {12,13,14,15}。 所以m [1:3] [1:3]是

[][]int{
    []int{8, 9, 10, 11},
    [[]int{12, 13, 14, 15},
}

最后,您想知道func innerMatrix为什么会有所不同?首先产生m [1:3],即

[][]int{
    []int{4, 5, 6, 7},
    []int{8, 9, 10, 11},
}

然后,代码不会像m [1:3] [1:3]是两个连续的子切片那样使用另一个子切片。取而代之的是,您获取切片中的每个元素并从每个元素中获取子切片。第一次,您从[] int {4,5,6,7}中获取子切片[1:3]。结果是

[]int{5, 6}

您第二次对第二个元素[] int {8,9,10,11}执行相同的操作

[]int{9, 10} 

最后,每次将结果附加到结果切片中,因此将[] int {5,6}附加到[],得到[] [] {int {5,6}},然后您附加[] int {9,10}以提供

[][]{int{5, 6}, []int{9, 10} }