Pasar una matriz a una función con un número variable de argumentos en Swift


151

En The Swift Programming Language , dice:

Las funciones también pueden tomar un número variable de argumentos, reuniéndolos en una matriz.

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

Cuando llamo a tal función con una lista de números separados por comas (`sumOf (1, 2, 3, 4), están disponibles como una matriz dentro de la función.

Pregunta: ¿qué pasa si ya tengo una serie de números que quiero pasar a esta función?

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

Esto falla con un error del compilador, "No se pudo encontrar una sobrecarga para '__conversión' que acepte los argumentos proporcionados". ¿Hay alguna manera de convertir una matriz existente en una lista de elementos que puedo pasar a una función variadic?


1
¿Por qué no simplemente configurar la función para tomar una matriz entera en su lugar?
Mick MacCallum

27
Claro, pero es posible que no sea el autor de la función y que no pueda (o quiera) cambiarla.
Ole Begemann

Respuestas:


97

Las salpicaduras aún no están en el idioma , como lo confirman los desarrolladores. La solución por ahora es usar una sobrecarga o esperar si no puede agregar sobrecargas.


3
¿Splatting ya está en el idioma? Estoy tratando de llamarsumOf(...numbers)
Noitidart

¡Muy decepcionante! ¡Llegué a esto incluso con algo tan simple como tratar de delegar mis propias llamadas de registro print!
Mark A. Donohoe

65

Aquí hay una solución que encontré. Sé que no es exactamente lo que quieres, pero parece estar funcionando.

Paso 1: declare la función que desea con una matriz en lugar de argumentos variables:

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

Paso 2: llame a esto desde su función variadic:

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

Paso 3: llame de cualquier manera:

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

Parece extraño, pero está funcionando en mis pruebas. Avíseme si esto causa problemas imprevistos para alguien. Swift parece ser capaz de separar la diferencia entre las dos llamadas con el mismo nombre de función.

Además, con este método, si Apple actualiza el idioma como sugiere la respuesta de @ manojid, solo necesitará actualizar estas funciones. De lo contrario, tendrá que pasar y renombrar mucho.


Gracias, me gusta la solución. Todavía otorgaré la respuesta "correcta" a manojlds por encontrar el enlace a la confirmación oficial de que la función aún no está disponible. Espero que entiendas.
Ole Begemann

Probablemente esté siguiendo la Guía y su suma de funciones (números: [Int]) -> Int está calculando el promedio
gfelisberto

18

Puedes lanzar la función:

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

¡Excelente! Esto también funciona con múltiples parámetros (con nombre); por ejemplo: func sumOf(foo numbers: Int..., bar: Bool) -> Int {};requieretypealias Function = (foo: [Int], bar: Bool) -> Int;
ThomasR

¡Excelente! salva mi vida.
JerryZhou

¿Cómo puedo hacer cosas similares con AnyObject ...? stackoverflow.com/questions/42016358/… Gracias.
JerryZhou

Esta es una muy mala idea. No hay absolutamente ninguna garantía de que esto funcione.
idmean el

1
@MattMc Claro. Por lo que puedo decir, no hay nada que te permita simplemente lanzar un tipo invocable a otro usando unsafeBitCast. Esto puede funcionar hoy, pero a menos que una referencia lo indique, la próxima versión del compilador es libre de hacer literalmente cualquier cosa aquí (error del compilador / bloqueo / código de ejecución aleatoria ...). Echa un vistazo a la advertencia de aspecto serio en unsafeBitCast .
idmean

15

Puede usar una función auxiliar como tal:

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

12
Siempre que haya una versión para matrices. Pensé que la premisa de la pregunta es que la función original toma Int...y no se puede (fácilmente) cambiar.

2
@delnan: Correcto. Me parece que debería haber una forma de pasar una matriz a una función variable, dado que los argumentos variables se convierten en una matriz de todos modos.
Ole Begemann

1
Los lenguajes que aceptan un número variable de argumentos en funciones, como Scheme, tienen un applyprocedimiento. Supongo que algunas personas llaman a eso 'salpicaduras'.
GoZoner

1
¿A qué se hace sumArrayreferencia aquí?
Rob

2

Sé que esta respuesta no responde a su pregunta exacta, pero creo que vale la pena señalar. Yo también estaba empezando a jugar con Swift e inmediatamente me encontré con una pregunta similar. La respuesta de Manojld es mejor para su pregunta, estoy de acuerdo, pero nuevamente, otra solución que se me ocurrió. A mí también me gusta más Logan.

En mi caso, solo quería pasar una matriz:

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

Solo quería compartir, en caso de que alguien más estuviera pensando como yo. La mayoría de las veces preferiría pasar la matriz de esta manera, pero todavía no creo que sea "Swiftly". :)


1

Hice esto (Wrapper + Identity Mapping):

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

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

0

Swift 5

Este es un enfoque con @dynamicCallablefunción que permite evitar la sobrecarga o unsafeBitCastdebe realizar una structllamada específica :

@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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.