ลบองค์ประกอบในชิ้น


139
func main() {
    a := []string{"Hello1", "Hello2", "Hello3"}
    fmt.Println(a)
    // [Hello1 Hello2 Hello3]
    a = append(a[:0], a[1:]...)
    fmt.Println(a)
    // [Hello2 Hello3]
}

เคล็ดลับการลบด้วยฟังก์ชันผนวกนี้ทำงานอย่างไร

ดูเหมือนว่ามันจะคว้าทุกอย่างไว้ก่อนองค์ประกอบแรก (อาร์เรย์ว่าง)

จากนั้นผนวกทุกอย่างหลังจากองค์ประกอบแรก (ตำแหน่งศูนย์)

... (dot dot dot) ทำอะไร



3
ทำไมไม่ลองดูสเป็คภาษาล่ะ? ...มีคำอธิบายในรายละเอียดไหม?
Volker

9
ในอีกด้านหนึ่งเป็นจริงอย่างแน่นอน ( golang.org/ref/specทุกคน) ที่อื่นมีความไม่คุ้นเคยเกี่ยวกับสำนวนเหล่านี้สำหรับการย้าย Pythonistas และอื่น ๆ ที่ฉันไม่รังเกียจที่จะอธิบายให้ผู้อื่นค้นพบ
twotwotwo

คำตอบ:


276

aชิ้นไหนอยู่ที่ไหนและiเป็นดัชนีขององค์ประกอบที่คุณต้องการลบ:

a = append(a[:i], a[i+1:]...)

... เป็นไวยากรณ์สำหรับอาร์กิวเมนต์ variadic ใน Go

โดยทั่วไปเมื่อกำหนดฟังก์ชั่นมันทำให้ข้อโต้แย้งทั้งหมดที่คุณผ่านเป็นชิ้นเดียวของประเภทนั้น โดยการทำเช่นนั้นคุณสามารถส่งอาร์กิวเมนต์ได้มากเท่าที่คุณต้องการ (ตัวอย่างเช่นfmt.Printlnสามารถรับอาร์กิวเมนต์ได้มากเท่าที่คุณต้องการ)

ตอนนี้เมื่อเรียกใช้ฟังก์ชัน...ทำสิ่งที่ตรงกันข้าม: แยกแพ็คออกและส่งผ่านเป็นอาร์กิวเมนต์แยกไปยังฟังก์ชัน Variadic

ดังนั้นสิ่งที่บรรทัดนี้จะ:

a = append(a[:0], a[1:]...)

เป็นหลัก:

a = append(a[:0], a[1], a[2])

ตอนนี้คุณอาจสงสัยว่าทำไมไม่ทำ

a = append(a[1:]...)

นิยามของฟังก์ชันappendคือ

func append(slice []Type, elems ...Type) []Type

ดังนั้นอาร์กิวเมนต์แรกจะต้องเป็นชิ้นส่วนของประเภทที่ถูกต้องอาร์กิวเมนต์ที่สองคือ Variadic ดังนั้นเราจึงส่งผ่านชิ้นว่างเปล่าแล้วแกะชิ้นที่เหลือเพื่อเติมอาร์กิวเมนต์


35
คุณไม่ได้รับการยกเว้นช่วงถ้าฉันเป็นองค์ประกอบสุดท้ายจากชิ้น? a = append(a[:i], a[i+1:]...)
themihai

5
@DaveC ฉันจะได้รับข้อผิดพลาดนั้นเมื่อทำงานกับชิ้นของฉันในโครงการของฉัน: /
Tyguy7

3
@ Tyguy7 จากข้อมูลจำเพาะ: "สำหรับอาร์เรย์หรือสตริงดัชนีอยู่ในช่วงถ้า 0 <= ต่ำ <= สูง <= len (a) มิฉะนั้นจะอยู่นอกช่วง" บางทีในกรณีของคุณสูง <ต่ำ; ในกรณีนี้คุณจะได้รับข้อผิดพลาด ( golang.org/ref/spec#Slice_expressions )
mlg

2
ประสิทธิภาพของสิ่งนี้เป็นอย่างไร ฉันหวังอย่างจริงจังว่าจะไม่สร้างชิ้นใหม่ภายใต้ประทุน ..
joonas.fi

7
@ Tyguy7 ฉันคิดว่าคุณพยายามที่จะลบองค์ประกอบของชิ้นภายในวง ดังนั้นคุณต้องระวังดัชนี
Nikolay Bystritskiy

42

มีสองตัวเลือก:

เป็น: คุณสนใจเกี่ยวกับการรักษาคำสั่งอาร์เรย์:

a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]

B: คุณไม่สนใจเกี่ยวกับการรักษาลำดับ (นี่อาจจะเร็วกว่า):

a[i] = a[len(a)-1] // Replace it with the last one. CAREFUL only works if you have enough elements.
a = a[:len(a)-1]   // Chop off the last one.

ดูลิงก์เพื่อดูความเกี่ยวข้องของหน่วยความจำรั่วถ้าอาเรย์ของคุณเป็นพอยน์เตอร์

https://github.com/golang/go/wiki/SliceTricks


นี่เป็นเรื่องที่น่าสนใจ แต่ไม่ตอบคำถามจริงๆ
ไบรอัน

นี่เป็นสิ่งที่ดี แต่คงจะดีกว่านี้หากสามารถลบองค์ประกอบเดียวในอาร์เรย์ได้
Naguib Ihab

2
เพียงหัวขึ้นพยายามที่จะใช้คนแรกจาก b (แทนที่ด้วยองค์ประกอบสุดท้าย) ไม่ชัดเจนถ้าคุณกำลังพยายามที่จะลบองค์ประกอบสุดท้ายในชิ้น lol
ไซเรน

13

แทนที่จะคิดของดัชนีในที่[a:]- [:b]- และ[a:b]-notations เป็นดัชนีองค์ประกอบคิดว่าพวกเขาเป็นดัชนีของช่องว่างรอบ ๆ และระหว่างองค์ประกอบที่เริ่มต้นด้วยการจัดทำดัชนีช่องว่างก่อนองค์ประกอบการจัดทำดัชนี00

ป้อนคำอธิบายรูปภาพที่นี่

มองไปที่เพียงตัวเลขสีฟ้าก็ง่ายมากที่จะเห็นสิ่งที่เกิดขึ้น: [0:3]ล้อมรอบทุกอย่าง[3:3]ว่างเปล่าและจะให้ผลผลิต[1:2] {"B"}จากนั้นก็[a:]เป็นเพียงรุ่นสั้น[a:len(arrayOrSlice)], [:b]รุ่นสั้น[0:b]และรุ่นสั้น[:] [0:len(arrayOrSlice)]โดยปกติจะใช้เพื่อเปลี่ยนอาร์เรย์ให้เป็นชิ้นเมื่อจำเป็น


1
สิ่งนี้ช่วยอธิบายได้ว่าทำไมคำตอบคือ "ไม่" สำหรับความคิดเห็นของพวกเขาในคำตอบของเดฟซึ่งหมายถึง [i + 1:] แม้ว่าจะอ้างถึงองค์ประกอบ ith: play.golang.org/p/E0lQ3jPcjX5
Nick P

5

... เป็นไวยากรณ์สำหรับข้อโต้แย้ง Variadic

ฉันคิดว่ามันถูกนำมาใช้โดย complier ใช้ slice ( []Type)เช่นเดียวกับฟังก์ชั่นผนวก:

func append(slice []Type, elems ...Type) []Type

เมื่อคุณใช้ "elems" ใน "append" ที่จริงแล้วมันคือ slice ([] type) ดังนั้น " a = append(a[:0], a[1:]...)" หมายถึง " a = append(a[0:0], a[1:])"

a[0:0] เป็นชิ้นที่ไม่มีอะไร

a[1:] คือ "Hello2 Hello3"

นี่คือวิธีการทำงาน


2
a[0:0]ไม่ได้nilเป็นเพียงชิ้นเดียวที่มีความยาว 0 a[0:0]จะเพียงเป็นnilถ้าเป็นa nil
icza

5

ฉันได้รับข้อผิดพลาดในการทำดัชนีด้วยคำตอบที่ได้รับการยอมรับ เหตุผล: เมื่อช่วงเริ่มต้นมันจะไม่ซ้ำค่าหนึ่งโดยหนึ่งมันจะวนตามดัชนี หากคุณแก้ไขชิ้นในขณะที่อยู่ในช่วงมันจะทำให้เกิดปัญหา

คำตอบเก่า:

chars := []string{"a", "a", "b"}

for i, v := range chars {
    fmt.Printf("%+v, %d, %s\n", chars, i, v)
    if v == "a" {
        chars = append(chars[:i], chars[i+1:]...)
    }
}
fmt.Printf("%+v", chars)

คาดว่า:

[a a b], 0, a
[a b], 0, a
[b], 0, b
Result: [b]

จริง:

// Autual
[a a b], 0, a
[a b], 1, b
[a b], 2, b
Result: [a b]

วิธีที่ถูกต้อง (โซลูชัน):

chars := []string{"a", "a", "b"}

for i := 0; i < len(chars); i++ {
    if chars[i] == "a" {
        chars = append(chars[:i], chars[i+1:]...)
        i-- // form the remove item index to start iterate next item
    }
}

fmt.Printf("%+v", chars)

ที่มา: https://dinolai.com/notes/golang/golang-delete-slice-item-in-range-problem.html


3

ใน wiki ของ golang มันแสดงให้เห็นถึงเทคนิคบางอย่างสำหรับชิ้นรวมถึงลบองค์ประกอบจากชิ้น

ลิงก์: ป้อนคำอธิบายลิงก์ที่นี่

ตัวอย่างเช่น a คือส่วนที่คุณต้องการลบองค์ประกอบหมายเลข i

a = append(a[:i], a[i+1:]...)

หรือ

a = a[:i+copy(a[i:], a[i+1:])]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.