ต่อสองชิ้นเข้าด้วยกันใน Go


477

ฉันพยายามที่จะรวมชิ้นและชิ้น[1, 2] [3, 4]ฉันจะทำสิ่งนี้ได้อย่างไรในโกะ?

ฉันเหนื่อย:

append([]int{1,2}, []int{3,4})

แต่ได้:

cannot use []int literal (type []int) as type int in append

อย่างไรก็ตามเอกสารประกอบที่ดูเหมือนจะบ่งบอกว่าเป็นไปได้สิ่งที่ฉันหายไป?

slice = append(slice, anotherSlice...)

คำตอบ:


877

เพิ่มจุดหลังจากชิ้นที่สอง:

//---------------------------vvv
append([]int{1,2}, []int{3,4}...)

นี่เป็นเหมือนกับฟังก์ชัน Variadic อื่น ๆ

func foo(is ...int) {
    for i := 0; i < len(is); i++ {
        fmt.Println(is[i])
    }
}

func main() {
    foo([]int{9,8,7,6,5}...)
}

37
append()ฟังก์ชัน Variadic และ...ให้คุณส่งอาร์กิวเมนต์หลาย ๆ ตัวไปยังฟังก์ชัน Variadic จากชิ้น

11
นี่มันนักแสดงทุกคนหรือเปล่าเมื่อชิ้นส่วนมีขนาดค่อนข้างใหญ่ หรือคอมไพเลอร์ไม่ผ่านองค์ประกอบทั้งหมดเป็นพารามิเตอร์จริง ๆ หรือไม่
คางคก

15
@Toad: มันไม่ได้แพร่กระจายออกไป ในfoo()ตัวอย่างข้างต้นisพารามิเตอร์เก็บสำเนาของชิ้นต้นฉบับซึ่งจะกล่าวว่ามันมีสำเนาของการอ้างอิงน้ำหนักเบาไปยังอาร์เรย์ต้นแบบเดียวกัน len และ cap หากfooฟังก์ชั่นเปลี่ยนแปลงสมาชิกจะเห็นการเปลี่ยนแปลงในต้นฉบับ นี่คือการสาธิต ดังนั้นค่าโสหุ้ยที่แท้จริงเพียงอย่างเดียวก็คือมันจะสร้างชิ้นใหม่ถ้าคุณยังไม่มีอีกแล้วเช่น: foo(1, 2, 3, 4, 5)ซึ่งจะสร้างชิ้นใหม่ที่isจะถือ

2
อา. ถ้าฉันเข้าใจอย่างถูกต้องฟังก์ชั่น Variadic จะถูกนำไปใช้จริงเช่นอาร์เรย์ของพารามิเตอร์ (แทนที่จะเป็นทุกพารามิเตอร์ในสแต็ก)? และเมื่อคุณผ่านส่วนแบ่งมันจะแมปแบบหนึ่งต่อหนึ่งหรือไม่
คางคก

@Toad: ใช่เมื่อคุณใช้...ชิ้นที่มีอยู่มันจะผ่านชิ้นนั้นไป เมื่อคุณผ่านการโต้แย้งแต่ละครั้งมันจะรวบรวมพวกมันเป็นชิ้นใหม่และผ่านมันไป ฉันไม่ได้มีมือแรกความรู้เกี่ยวกับกลศาสตร์แน่นอน แต่ฉันเดาว่านี้: foo(1, 2, 3, 4, 5)และนี่: func foo(is ...int) {เพียง de-น้ำตาลนี้: และนี่:foo([]int{1, 2, 3, 4, 5}) func foo(is []int) {

77

ต่อท้ายและคัดลอกชิ้น

ฟังก์ชัน Variadic จะappendผนวกค่าเป็นศูนย์หรือมากกว่านั้นxให้กับs ประเภทSซึ่งจะต้องเป็นประเภทชิ้นและส่งกลับชิ้นฝานที่เกิดขึ้นเช่นSกัน ค่าxจะถูกส่งผ่านไปยังพารามิเตอร์ประเภท...Tที่Tเป็นประเภทองค์ประกอบSและใช้กฎการส่งผ่านพารามิเตอร์ตามลำดับ เป็นกรณีพิเศษผนวกยังยอมรับมอบหมายอาร์กิวเมนต์แรกจะพิมพ์[]byteด้วยอาร์กิวเมนต์ที่สองของ ประเภทตามมาด้วยstring ...แบบฟอร์มนี้ต่อท้ายไบต์ของสตริง

append(s S, x ...T) S  // T is the element type of S

s0 := []int{0, 0}
s1 := append(s0, 2)        // append a single element     s1 == []int{0, 0, 2}
s2 := append(s1, 3, 5, 7)  // append multiple elements    s2 == []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...)    // append a slice              s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}

กำลังส่งอาร์กิวเมนต์ไปยังพารามิเตอร์ ...

ถ้าfเป็น variadic กับพารามิเตอร์ชนิดสุดท้ายแล้วภายในฟังก์ชันอาร์กิวเมนต์เทียบเท่ากับพารามิเตอร์ของชนิด...T []Tในการเรียกแต่ละครั้งfอาร์กิวเมนต์ที่ส่งไปยังพารามิเตอร์สุดท้ายคือชิ้นส่วนใหม่[]Tที่มีองค์ประกอบต่อเนื่องคืออาร์กิวเมนต์ที่เกิดขึ้นจริงซึ่งทั้งหมดจะต้องสามารถกำหนดให้กับประเภทTได้ ความยาวของชิ้นจึงเป็นจำนวนข้อโต้แย้งที่ผูกไว้กับพารามิเตอร์สุดท้ายและอาจแตกต่างกันสำหรับแต่ละไซต์โทร

คำตอบสำหรับคำถามของคุณเป็นตัวอย่างs3 := append(s2, s0...)ในการเขียนโปรแกรมภาษาไปจำเพาะ ตัวอย่างเช่น,

s := append([]int{1, 2}, []int{3, 4}...)

6
หมายเหตุ: การใช้งานโดยทั่วไปของการผนวก (slice1, slice2 ... ) ดูเหมือนว่าค่อนข้างอันตรายสำหรับฉัน ถ้า slice1 เป็นส่วนหนึ่งของอาร์เรย์ที่มีขนาดใหญ่กว่าค่าของอาร์เรย์นั้นจะถูกเขียนทับโดย slice2 (มันทำให้ฉันประจบประแจงว่าสิ่งนี้ดูเหมือนจะไม่เป็นข้อกังวลร่วมกันใช่ไหม?)
Hugo

7
@Hugo หากคุณ "ส่ง" ส่วนแบ่งของอาร์เรย์คุณจะรู้ว่าส่วน "เจ้าของ" จะสามารถดู / เขียนทับส่วนต่าง ๆ ของอาร์เรย์ที่อยู่นอกเหนือความยาวปัจจุบันของชิ้นส่วน ถ้าคุณไม่อยากให้เรื่องนี้คุณอาจใช้การแสดงออกชิ้นเต็ม (ในรูปแบบของa[low : high : max]) ซึ่งยังระบุสูงสุดความจุ ตัวอย่างชิ้นa[0:2:4]จะมีความสามารถ4และไม่สามารถ resliced ​​เพื่อรวมองค์ประกอบนอกเหนือจากนั้นแม้ว่าอาร์เรย์สำรองมีองค์ประกอบหนึ่งพันหลังจากนั้น
icza

30

ไม่มีอะไรเทียบกับคำตอบอื่น ๆ แต่ฉันพบคำอธิบายสั้น ๆ ในเอกสารที่เข้าใจได้ง่ายกว่าตัวอย่างในพวกเขา:

ต่อท้าย func

func append(slice []Type, elems ...Type) []Typeฟังก์ชันในตัวต่อท้ายผนวกองค์ประกอบเข้ากับส่วนท้ายของส่วน หากมีความจุเพียงพอปลายทางจะถูก resliced ​​เพื่อรองรับองค์ประกอบใหม่ ถ้าไม่เช่นนั้นจะมีการจัดสรรอาร์เรย์ใหม่ ผนวกส่งคืนชิ้นที่อัปเดต ดังนั้นจึงจำเป็นต้องจัดเก็บผลลัพธ์ของการผนวกบ่อยครั้งในตัวแปรที่เก็บชิ้นส่วนเอง:

slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)

เป็นกรณีพิเศษมันถูกต้องตามกฎหมายที่จะผนวกสตริงไปยังชิ้นไบต์เช่นนี้

slice = append([]byte("hello "), "world"...)

1
ขอบคุณ! มีค่าสำหรับฉัน!
Korjavin Ivan

23

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

ซึ่งหมายความว่าหากปลายทางถูกสร้างขึ้นโดยการแบ่งอาร์เรย์หรือส่วนที่ใหญ่กว่าซึ่งมีองค์ประกอบเพิ่มเติมเกินกว่าความยาวของชิ้นส่วนผลลัพธ์พวกเขาอาจถูกเขียนทับ

ในการสาธิตดูตัวอย่างนี้:

a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)

x, y := a[:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))

x = append(x, y...)
fmt.Printf("x: %v\n", x)

fmt.Printf("a: %v\n", a)

เอาท์พุท (ลองไปที่สนามเด็กเล่น Go ):

a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 10
x: [1 2 3 4]
a: [1 2 3 4 0 0 0 0 0 0]

เราได้สร้าง "สนับสนุน" อาร์เรย์ที่มีความยาวa 10จากนั้นเราก็สร้างxชิ้นปลายทางโดยหั่นนี้aอาร์เรย์ชิ้นจะถูกสร้างขึ้นโดยใช้ตัวอักษรคอมโพสิตy []int{3, 4}ตอนนี้เมื่อเราผนวกyเพื่อxผลที่ได้คือความคาดหวัง[1 2 3 4]แต่สิ่งที่อาจจะน่าแปลกใจคืออาร์เรย์สนับสนุนaยังมีการเปลี่ยนแปลงเนื่องจากความจุของxมีที่10ซึ่งเพียงพอที่จะผนวกyกับมันจึงxเป็น resliced ซึ่งจะใช้เหมือนกันaอาร์เรย์การสนับสนุนและappend()จะคัดลอกองค์ประกอบของyไปที่นั่น

หากคุณต้องการหลีกเลี่ยงปัญหานี้คุณอาจใช้นิพจน์ส่วนเต็มที่มีแบบฟอร์ม

a[low : high : max]

max - lowซึ่งสร้างชิ้นและยังควบคุมกำลังการผลิตชิ้นส่งผลโดยการตั้งค่าให้

ดูตัวอย่างที่แก้ไข (ความแตกต่างเพียงอย่างเดียวคือเราสร้างxสิ่งนี้x = a[:2:2]:

a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)

x, y := a[:2:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))

x = append(x, y...)
fmt.Printf("x: %v\n", x)

fmt.Printf("a: %v\n", a)

เอาท์พุท (ลองไปที่สนามเด็กเล่นไป )

a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 2
x: [1 2 3 4]
a: [1 2 0 0 0 0 0 0 0 0]

อย่างที่คุณเห็นเราได้รับxผลลัพธ์เดียวกันแต่อาเรย์สำรองaไม่เปลี่ยนแปลงเนื่องจากความสามารถในการxเป็น "เท่านั้น" 2(ด้วยการแสดงออกเต็มส่วนa[:2:2]) ดังนั้นจะทำอย่างไรผนวก, อาร์เรย์สนับสนุนใหม่มีการจัดสรรที่สามารถเก็บองค์ประกอบของทั้งสองxและซึ่งแตกต่างจากya


2
มันมีประโยชน์มากสำหรับปัญหาที่ฉันเผชิญ ขอบคุณ
Aidy

9

ฉันต้องการเน้น @icza คำตอบและทำให้มันง่ายขึ้นเล็กน้อยเพราะเป็นแนวคิดที่สำคัญ ผมถือว่าผู้อ่านที่มีความคุ้นเคยกับชิ้น

c := append(a, b...)

นี่คือคำตอบที่ถูกต้องสำหรับคำถาม แต่ถ้าคุณต้องการใช้ชิ้น 'a' และ 'c' ในภายหลังในรหัสในบริบทที่แตกต่างกันนี่ไม่ใช่วิธีที่ปลอดภัยในการเชื่อมต่อชิ้นส่วนต่างๆ

ในการอธิบายให้อ่านนิพจน์ไม่ได้ในรูปของชิ้น แต่ในแง่ของอาร์เรย์ต้นแบบ:

"ใช้อาเรย์ (อ้างอิง) ของ 'a' และต่อท้ายองค์ประกอบจากอาเรย์ 'b' ลงไปหากอาเรย์ 'a' มีความจุเพียงพอที่จะรวมองค์ประกอบทั้งหมดจาก 'b' - อาเรย์พื้นฐานของ 'c' จะไม่เป็นอาเรย์ใหม่ จริง ๆ แล้วมันจะเป็นอาร์เรย์ 'a' โดยทั่วไป slice 'a' จะแสดงองค์ประกอบ len (a) ของอาร์เรย์ต้นแบบ 'a' และ slice 'c' จะแสดง len (c) ของอาร์เรย์ 'a'

ผนวก () ไม่จำเป็นต้องสร้างอาร์เรย์ใหม่! สิ่งนี้สามารถนำไปสู่ผลลัพธ์ที่ไม่คาดคิด ดูตัวอย่างไปสนามเด็กเล่น

ใช้ฟังก์ชัน make () ทุกครั้งถ้าคุณต้องการตรวจสอบให้แน่ใจว่ามีการจัดสรรอาร์เรย์ใหม่สำหรับส่วนแบ่ง ตัวอย่างเช่นที่นี่มีตัวเลือกที่น่าเกลียดน้อย แต่มีประสิทธิภาพเพียงพอสำหรับงาน

la := len(a)
c := make([]int, la, la + len(b))
_ = copy(c, a)
c = append(c, b...)

la := len(a)
c := make([]int, la + len(b))
_ = copy(c, a)
_ = copy(c[la:], b)

ขอบคุณที่ชี้ไปยังผลข้างเคียงเหล่านี้ ตรงกันข้ามกับ szenario ที่ดัดแปลงมาอย่างน่าอัศจรรย์นี้ play.golang.org/p/9FKo5idLBj4แม้ว่าจะให้ความจุเกิน แต่ก็ควรคำนึงถึงผลข้างเคียงที่น่าสงสัยเหล่านี้ต่อปรีชาน่าเชื่อถือ
olippuner

5

ผนวก () ฟังก์ชั่นและผู้ประกอบการแพร่กระจาย

สามารถต่อสองชิ้นเข้าด้วยกันโดยใช้appendวิธีการในไลบรารี golang มาตรฐาน ซึ่งคล้ายกับการvariadicใช้งานฟังก์ชั่น ดังนั้นเราต้องใช้...

package main

import (
    "fmt"
)

func main() {
    x := []int{1, 2, 3}
    y := []int{4, 5, 6}
    z := append([]int{}, append(x, y...)...)
    fmt.Println(z)
}

ผลลัพธ์ของรหัสข้างต้นคือ: [1 2 3 4 5 6]


2

append([]int{1,2}, []int{3,4}...)จะทำงาน. การส่งอาร์กิวเมนต์ไปยัง...พารามิเตอร์

ถ้าfเป็น variadic กับพารามิเตอร์สุดท้ายpของประเภท...Tแล้วภายในfประเภทของเทียบเท่ากับประเภทp[]T

หากfถูกเรียกด้วยไม่มีข้อโต้แย้งที่เกิดขึ้นจริงสำหรับpค่าที่ส่งผ่านไปยังมีpnil

มิฉะนั้นค่าที่ส่งเป็นชิ้นใหม่ชนิดกับอาร์เรย์พื้นฐานใหม่ที่มีองค์ประกอบต่อเนื่องเป็นข้อโต้แย้งที่เกิดขึ้นจริงซึ่งทั้งหมดจะต้องมีการมอบหมาย[]T Tความยาวและความสามารถของชิ้นจึงเป็นจำนวนข้อโต้แย้งที่ถูกผูกไว้pและอาจแตกต่างกันสำหรับแต่ละไซต์ที่โทร

รับฟังก์ชั่นและการโทร

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.