Melewati array ke fungsi dengan jumlah variabel args di Swift


151

Dalam Bahasa Pemrograman Swift , dikatakan:

Fungsi juga dapat mengambil sejumlah variabel argumen, mengumpulkannya ke dalam array.

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

Ketika saya memanggil fungsi tersebut dengan daftar angka yang dipisah koma (`sumOf (1, 2, 3, 4), mereka tersedia sebagai larik di dalam fungsi.

Pertanyaan: bagaimana jika saya sudah memiliki array angka yang ingin saya sampaikan ke fungsi ini?

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

Ini gagal dengan kesalahan kompiler, "Tidak dapat menemukan kelebihan untuk '__ konversi' yang menerima argumen yang disediakan". Apakah ada cara untuk mengubah array yang ada menjadi daftar elemen yang bisa saya sampaikan ke fungsi variadic?


1
Mengapa tidak mengkonfigurasi fungsi untuk mengambil array integer saja?
Mick MacCallum

27
Tentu, tetapi saya mungkin bukan penulis fungsi dan mungkin tidak dapat (atau ingin) mengubahnya.
Ole Begemann

Jawaban:


97

Pukulan belum dalam bahasa , seperti dikonfirmasi oleh devs. Solusi untuk saat ini adalah dengan menggunakan kelebihan atau menunggu jika Anda tidak dapat menambahkan kelebihan.


3
Apakah Splatting dalam bahasa belum? Saya mencoba meneleponsumOf(...numbers)
Noitidart

Sangat mengecewakan! Saya memukul ini bahkan dengan sesuatu yang sederhana seperti mencoba mendelegasikan panggilan log saya sendiri ke print!
Mark A. Donohoe

65

Ini pekerjaan yang saya temukan. Saya tahu itu tidak persis apa yang Anda inginkan, tetapi tampaknya berhasil.

Langkah 1: Nyatakan fungsi yang Anda inginkan dengan array alih-alih argumen variadik:

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

Langkah 2: Panggil ini dari dalam fungsi variadic Anda:

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

Langkah 3: Panggil Either Way:

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

Kelihatannya aneh, tetapi itu berhasil dalam pengujian saya. Beri tahu saya jika ini menyebabkan masalah yang tidak terduga bagi siapa pun. Swift tampaknya dapat memisahkan perbedaan antara dua panggilan dengan nama fungsi yang sama.

Selain itu, dengan metode ini jika Apple memperbarui bahasa seperti jawaban @ manojid, Anda hanya perlu memperbarui fungsi-fungsi ini. Jika tidak, Anda harus melalui dan melakukan banyak penggantian nama.


Terima kasih, saya suka solusinya. Saya masih akan memberikan jawaban "benar" untuk manojlds karena menemukan tautan ke konfirmasi resmi bahwa fitur tersebut belum tersedia. Saya harap Anda mengerti.
Ole Begemann

Anda mungkin mengikuti Panduan dan fungsi Anda (angka: [Int]) -> Int sebenarnya menghitung rata-rata
gfelisberto

18

Anda dapat menggunakan fungsi ini:

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

Bagus! Ini juga berfungsi dengan beberapa parameter (bernama); mis: func sumOf(foo numbers: Int..., bar: Bool) -> Int {};membutuhkantypealias Function = (foo: [Int], bar: Bool) -> Int;
ThomasR

Bagus! Selamatkan hidupku.
JerryZhou

Bagaimana saya bisa melakukan hal serupa dengan AnyObject ...? stackoverflow.com/questions/42016358/... Terima kasih.
JerryZhou

Ini ide yang sangat buruk. Sama sekali tidak ada jaminan bahwa ini akan berhasil.
idmean

1
@MattMc Tentu. Sejauh yang saya tahu tidak ada yang akan memungkinkan Anda untuk hanya melemparkan satu jenis panggilan ke yang lain menggunakan unsafeBitCast. Ini mungkin berfungsi hari ini, tetapi kecuali jika referensi mengatakannya, versi selanjutnya dari kompiler bebas untuk melakukan apa pun di sini (kesalahan kompiler / crash / mengeksekusi kode secara acak ...). Lihat peringatan serius yang terlihat di unsafeBitCast .
idmean

15

Anda dapat menggunakan fungsi pembantu seperti:

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

12
Asalkan ada versi untuk array. Saya pikir premis dari pertanyaan adalah bahwa fungsi asli diambil Int...dan tidak dapat (dengan mudah) diubah?

2
@delnan: Benar. Tampaknya bagi saya bahwa harus ada cara untuk melewatkan array ke fungsi variadic, mengingat bahwa argumen variadic diubah menjadi array pula.
Ole Begemann

1
Bahasa yang menerima jumlah variabel argumen dalam fungsi, seperti Skema, memiliki applyprosedur. Saya mengumpulkan beberapa orang menyebutnya 'splatting'.
GoZoner

1
Apa yang sumArraydirujuk di sini?
Rob

2

Saya tahu respons ini tidak menjawab pertanyaan persis Anda, tetapi saya merasa perlu untuk diperhatikan. Saya juga mulai bermain dengan Swift dan segera mengalami pertanyaan serupa. Jawaban Manojlds lebih baik untuk pertanyaan Anda, saya setuju, tapi sekali lagi, solusi lain yang saya temukan. Kebetulan aku juga menyukai Logan.

Dalam kasus saya, saya hanya ingin melewatkan array:

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])

Hanya ingin berbagi, kalau-kalau ada orang yang berpikir seperti saya. Sebagian besar waktu saya lebih suka melewati array seperti ini, tetapi saya belum memikirkan "Swiftly". :)


1

Saya melakukan ini (Wrapper + Identity Mapping):

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

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

0

Cepat 5

Ini adalah pendekatan dengan @dynamicCallablefitur yang memungkinkan untuk menghindari kelebihan muatan atau unsafeBitCasttetapi Anda harus membuat structpanggilan khusus:

@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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.