猿问

我可以使用单线从切片中的位置 n 弹出一个元素吗?

我正在浏览切片技巧文档,看到一些单线弹出和弹出前面。例如,这两个工作正常:


// pop

s := []int{1, 2, 3}     

last, s := s[len(s)-1], s[:len(s)-1]    

fmt.Println(last, s) // Prints 3 [1 2]

// pop front

s := []int{1, 2, 3}     

first, s := s[0], s[1:]                 

fmt.Println(first, s)  // Prints 1 [2 3]

但是如果我尝试做这样的事情来弹出第二个元素:


s := []int{1, 2, 3}     

second, s := s[1], append(s[0:1], s[2:]...)

fmt.Println(second, s) // Prints 3 [1 3]

它弹出第二个元素,但second变量指向新切片中的第二个元素。为什么在这种情况下会发生这种情况,而前两个情况却没有?为了工作,我必须使用单独的行:


s := []int{1, 2, 3}                     

second := s[1]          

s = append(s[0:1], s[2:]...)

fmt.Println(second, s) // Prints 2 [1 3]


HUH函数
浏览 120回答 1
1回答

慕森卡

Go 规范中关于 assignments的部分告诉我们,这是第二种形式元组赋值。它接着说:任务分两个阶段进行。首先,左侧的索引表达式和指针间接(包括选择器中的隐式指针间接)的操作数和右侧的表达式都按通常的顺序进行计算。其次,作业是按从左到右的顺序进行的。因此,编译器计算second并s通过评估它们来进行赋值——这只是产生它们的名称,或多或少1——并且还评估右侧的表达式通常的顺序这意味着我们必须通过链接查看“通常的顺序”是什么意思。这使我们得到评估顺序。这里的文字有点棘手,但这个例子很有启发性:在(函数局部)赋值中y[f()],&nbsp;ok&nbsp;=&nbsp;g(h(),&nbsp;i()+x[j()],&nbsp;<-c),&nbsp;k()f()函数调用和通信按,&nbsp;h(),&nbsp;i(),&nbsp;j(),&nbsp;<-c,&nbsp;g(), 和的顺序发生k()。然而,这些事件与评估和索引x以及评估相比的顺序y没有指定。让我们将其与您自己的表达式进行比较:second,&nbsp;s&nbsp;:=&nbsp;s[1],&nbsp;append(s[0:1],&nbsp;s[2:]...)我们知道append它将在之前被调用......好吧,我们不太确定:没有进一步的(调用权)函数调用或通道调用。s但显然它必须在被分配之前被调用。但是,与此同时,此调用相对于“评估和索引”的顺序s[1]并未明确指定。如果先完成,该append操作将(可能是2 次)就地修改支持 slice 的数组s[0:1]。那么,显然正在发生的事情append(s[0:1], s[2:]...)实际上是在原地修改数组。然后进行评估和索引s[1],修改数组。这会获取 的修改后的值s[1]并将其复制到变量second。为什么在这种情况下会发生这种情况,而前两个情况却没有?那些不会调用append,因此不允许append就地修改数组。1由于在内部使用了 SSA,这些都变成了对新变量的赋值,但最终结果都是一样的。2由append函数决定是进行就地修改还是创建新的后备数组。但是,在这种情况下,由于我们正在缩小总长度,这显然是可能的,并且实际实现每次都会利用这一点——尽管也没有具体说明!结论这里要做的事情是编写一个执行提取和更新的小函数,而不是作为单行程序,并简单地允许编译器将其内联成相当快的东西。当然,这使我们希望使用泛型,因此希望使用 Go 2。
随时随地看视频慕课网APP

相关分类

Go
我要回答