为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
HUH函数 2022-06-27 16:08:58
我正在浏览切片技巧文档,看到一些单线弹出和弹出前面。例如,这两个工作正常:// pops := []int{1, 2, 3}     last, s := s[len(s)-1], s[:len(s)-1]    fmt.Println(last, s) // Prints 3 [1 2]// pop fronts := []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]
查看完整描述

1 回答

?
慕森卡

TA贡献1806条经验 获得超8个赞

Go 规范中关于 assignments的部分告诉我们,这是

第二种形式

元组赋值。它接着说:

任务分两个阶段进行。首先,左侧的索引表达式指针间接(包括选择器中的隐式指针间接)的操作数和右侧的表达式都按通常的顺序进行计算。其次,作业是按从左到右的顺序进行的。

因此,编译器计算seconds通过评估它们来进行赋值——这只是产生它们的名称,或多或少1——并且还评估右侧的表达式

通常的顺序

这意味着我们必须通过链接查看“通常的顺序”是什么意思。这使我们得到评估顺序

这里的文字有点棘手,但这个例子很有启发性:

在(函数局部)赋值中

y[f()], ok = g(h(), i()+x[j()], <-c), k()

f()函数调用和通信按, h()i()j()<-cg(), 和的顺序发生k()。然而,这些事件与评估和索引x以及评估相比的顺序y没有指定。

让我们将其与您自己的表达式进行比较:

second, s := s[1], append(s[0:1], 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,这些都变成了对变量的赋值,但最终结果都是一样的。

2append函数决定是进行就地修改还是创建新的后备数组。但是,在这种情况下,由于我们正在缩小总长度,这显然是可能的,并且实际实现每次都会利用这一点——尽管也没有具体说明!


结论

这里要做的事情是编写一个执行提取和更新的小函数,而不是作为单行程序,并简单地允许编译器将其内联成相当快的东西。当然,这使我们希望使用泛型,因此希望使用 Go 2。


查看完整回答
反对 回复 2022-06-27
  • 1 回答
  • 0 关注
  • 119 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号