ส่งผ่านอาร์เรย์ไปยังฟังก์ชันที่มีจำนวนของตัวแปรใน Swift


151

ในภาษาโปรแกรม Swiftมันพูดว่า:

ฟังก์ชั่นยังสามารถนำจำนวนอาร์กิวเมนต์ที่หลากหลายมารวมกันเป็นอาร์เรย์

  func sumOf(numbers: Int...) -> Int {
      ...
  }

เมื่อฉันเรียกฟังก์ชั่นดังกล่าวพร้อมกับรายการตัวเลขคั่นด้วยเครื่องหมายจุลภาค (`sumOf (1, 2, 3, 4) พวกมันถูกทำให้เป็นอาร์เรย์ภายในฟังก์ชัน

คำถาม: ถ้าฉันมีตัวเลขที่ฉันต้องการส่งผ่านไปยังฟังก์ชั่นนี้แล้ว?

let numbers = [1, 2, 3, 4]
sumOf(numbers)

สิ่งนี้ล้มเหลวด้วยข้อผิดพลาดของคอมไพเลอร์“ ไม่พบโอเวอร์โหลดสำหรับ '__conversion' ที่ยอมรับอาร์กิวเมนต์ที่ให้มา” มีวิธีเปลี่ยนอาร์เรย์ที่มีอยู่ให้เป็นรายการองค์ประกอบที่ฉันสามารถส่งไปยังฟังก์ชัน Variadic ได้หรือไม่


1
ทำไมไม่เพียงกำหนดค่าฟังก์ชั่นให้ใช้อาร์เรย์จำนวนเต็มแทน
Mick MacCallum

27
แน่นอน แต่ฉันอาจไม่ได้เป็นผู้เขียนฟังก์ชั่นและอาจไม่สามารถเปลี่ยน (หรือต้องการ) ได้
Ole Begemann

คำตอบ:


97

การสาดน้ำยังไม่ได้อยู่ในภาษาตามที่ได้รับการยืนยันโดย devs วิธีแก้ปัญหาในตอนนี้คือการใช้งานเกินพิกัดหรือรอถ้าคุณไม่สามารถเพิ่มเกินพิกัดได้


3
Splatting เป็นภาษาหรือยัง ฉันพยายามโทรหาsumOf(...numbers)
Noitidart

น่าผิดหวังมาก! ฉันกดปุ่มนี้แม้จะมีอะไรที่เรียบง่ายแค่พยายามมอบหมายบันทึกการโทรของฉันเองprint!
Mark A. Donohoe

65

นี่คืองานที่ฉันพบ ฉันรู้ว่ามันไม่ได้เป็นอย่างที่คุณต้องการ แต่ดูเหมือนว่าจะทำงาน

ขั้นตอนที่ 1: ประกาศฟังก์ชั่นที่คุณต้องการด้วยอาเรย์แทนอาร์กิวเม้นต์แบบแปรผัน:

func sumOf(numbers: [Int]) -> Int {
    var total = 0
    for i in numbers {
        total += i
    }
    return total
}

ขั้นตอนที่ 2: เรียกสิ่งนี้จากภายในฟังก์ชัน Variadic ของคุณ:

func sumOf(numbers: Int...) -> Int {
    return sumOf(numbers)
}

ขั้นตอนที่ 3: โทรทางใดก็ได้:

var variadicSum = sumOf(1, 2, 3, 4, 5)
var arraySum = sumOf([1, 2, 3, 4, 5])

ดูเหมือนว่าจะแปลก แต่ก็ใช้งานได้ในการทดสอบของฉัน แจ้งให้เราทราบหากสิ่งนี้ทำให้เกิดปัญหาที่ไม่คาดฝันสำหรับทุกคน Swift ดูเหมือนจะสามารถแยกความแตกต่างระหว่างการโทรสองครั้งด้วยชื่อฟังก์ชันเดียวกันได้

ด้วยวิธีนี้หาก Apple อัปเดตภาษาตามคำตอบของ @ manojid คุณจะต้องอัพเดตฟังก์ชั่นเหล่านี้เท่านั้น มิฉะนั้นคุณจะต้องผ่านและทำการเปลี่ยนชื่อเป็นจำนวนมาก


ขอบคุณฉันชอบวิธีแก้ปัญหา ฉันจะยังคงให้รางวัลคำตอบ "ถูกต้อง" กับ manojlds สำหรับการค้นหาลิงก์ไปยังการยืนยันอย่างเป็นทางการว่ายังไม่มีฟีเจอร์นี้ ฉันหวังว่าคุณเข้าใจ.
Ole Begemann

คุณอาจปฏิบัติตามคำแนะนำและ func sumOf ของคุณ (ตัวเลข: [Int]) -> Int คำนวณค่าเฉลี่ยจริง ๆ
gfelisberto

18

คุณสามารถใช้ฟังก์ชัน:

typealias Function = [Int] -> Int
let sumOfArray = unsafeBitCast(sumOf, Function.self)
sumOfArray([1, 2, 3])

ที่ดี! ใช้ได้กับพารามิเตอร์หลายตัว (ตั้งชื่อ) ด้วย; เช่น: func sumOf(foo numbers: Int..., bar: Bool) -> Int {};ต้องการtypealias Function = (foo: [Int], bar: Bool) -> Int;
ThomasR

ที่ดี! ช่วยชีวิตฉันไว้
JerryZhou

ฉันจะทำสิ่งที่คล้ายกันกับ AnyObject ... ได้อย่างไร stackoverflow.com/questions/42016358/… ขอบคุณ
JerryZhou

นี่เป็นความคิดที่แย่มาก ไม่มีการรับประกันเป็นศูนย์อย่างแน่นอนว่าสิ่งนี้จะได้ผล
idmean

1
@MattMc แน่นอน เท่าที่ผมสามารถบอกได้ว่ามีอะไรที่จะอนุญาตให้คุณโยนเพียงประเภทหนึ่งไปยังอีก callable unsafeBitCastใช้ สิ่งนี้อาจใช้งานได้ในทุกวันนี้ แต่หากไม่มีการอ้างอิงที่กล่าวเช่นนั้นคอมไพเลอร์รุ่นต่อไปมีอิสระที่จะทำสิ่งใดที่นี่อย่างแท้จริง มีลักษณะที่เตือนร้ายแรงมองในunsafeBitCast
idmean

15

คุณสามารถใช้ฟังก์ชันตัวช่วยได้เช่น:

func sumOf (numbers : [Int])  -> Int { return numbers.reduce(0, combine: +) }
func sumOf (numbers : Int...) -> Int { return sumOf (numbers) }

12
ให้มีรุ่นสำหรับอาร์เรย์ ฉันคิดว่าหลักฐานของคำถามคือฟังก์ชั่นดั้งเดิมใช้Int...และไม่สามารถเปลี่ยนได้ง่าย (ง่าย)?

2
@delnan: ถูกต้อง สำหรับฉันดูเหมือนว่าควรมีวิธีการส่งผ่านอาร์เรย์ไปยังฟังก์ชัน Variadic เนื่องจากอาร์กิวเมนต์ Variadic นั้นได้เปลี่ยนเป็นอาร์เรย์แล้ว
Ole Begemann

1
ภาษาที่ยอมรับจำนวนตัวแปรของข้อโต้แย้งในฟังก์ชั่นเช่น Scheme มีapplyขั้นตอน ฉันรวบรวมบางคนเรียกว่า 'แดง'
GoZoner

1
มีsumArrayการอ้างอิงอะไรที่นี่?
Rob

2

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

ในกรณีของฉันฉันต้องการผ่านอาร์เรย์:

func sumOf(numbers: Array<Int>) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}

var someNums = [8,7,2,9,12]
sumOf(someNums)
sumOf([10, 15, 20])

แค่อยากจะแบ่งปันในกรณีที่คนอื่นคิดเหมือนฉัน เวลาส่วนใหญ่ฉันต้องการผ่านอาร์เรย์เช่นนี้ แต่ฉันไม่คิดว่า "รวดเร็ว" เลย :)


1

ฉันทำสิ่งนี้ (Wrapper + การจับคู่ข้อมูลเฉพาะตัว):

func addBarButtonItems(types: REWEBarButtonItemType...) {
    addBarButtonItems(types: types.map { $0 })
}

func addBarButtonItems(types: [REWEBarButtonItemType]) {
    // actual implementation
}

0

สวิฟท์ 5

นี่เป็นวิธีการที่มี@dynamicCallableคุณสมบัติที่อนุญาตให้หลีกเลี่ยงการโอเวอร์โหลดหรือunsafeBitCastแต่คุณควรทำการstructโทรโดยเฉพาะ:

@dynamicCallable
struct SumOf {
    func dynamicallyCall(withArguments args: [Int]) -> Int {
        return args.reduce(0, +)
    }
}

let sum = SumOf()

// Use a dynamic method call.
sum(1, 2, 3) // 6

// Call the underlying method directly.
sum.dynamicallyCall(withArguments: [1, 2, 3]) // 6
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.