Passando um array para uma função com número variável de argumentos no Swift


151

Na linguagem de programação Swift , diz:

As funções também podem receber um número variável de argumentos, coletando-os em uma matriz.

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

Quando eu chamo essa função com uma lista de números separados por vírgula (`sumOf (1, 2, 3, 4)), eles são disponibilizados como uma matriz dentro da função.

Pergunta: e se eu já tiver uma matriz de números que deseje passar para esta função?

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

Isso falha com um erro do compilador, "Não foi possível encontrar uma sobrecarga para '__conversion' que aceita os argumentos fornecidos". Existe uma maneira de transformar uma matriz existente em uma lista de elementos que eu posso passar para uma função variável?


1
Por que não apenas configurar a função para usar uma matriz inteira?
— Mick MacCallum

27
Claro, mas talvez eu não seja o autor da função e não consiga (ou queira) alterá-la.
— precisa

Respostas:


97

Splatting ainda não está no idioma , conforme confirmado pelos desenvolvedores. A solução alternativa por enquanto é usar uma sobrecarga ou aguardar se você não puder adicionar sobrecargas.


3
O Splatting já está no idioma? Estou tentando ligarsumOf(...numbers)
— Noitidart 15/05/19

Tão decepcionante! Eu acertei isso mesmo com algo tão simples quanto tentar delegar minhas próprias chamadas de log print!
— Mark A. Donohoe

65

Aqui está uma solução que eu encontrei. Eu sei que não é exatamente o que você quer, mas parece estar funcionando.

Etapa 1: Declare a função que você gostaria com uma matriz em vez de argumentos variados:

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

Etapa 2: chame isso de dentro da sua função variável:

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

Etapa 3: Ligue de qualquer maneira:

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

Parece estranho, mas está funcionando nos meus testes. Deixe-me saber se isso causa problemas imprevistos para alguém. Swift parece capaz de separar a diferença entre as duas chamadas com o mesmo nome de função.

Além disso, com esse método, se a Apple atualizar o idioma como sugere a resposta do @ manojid, você só precisará atualizar essas funções. Caso contrário, você terá que passar por várias renomeações.


Obrigado, eu gosto da solução alternativa. Ainda atribuirei a resposta "correta" aos manojlds por encontrar o link para a confirmação oficial de que o recurso ainda não está disponível. Espero que entenda.
— 3105 Ole Begemann

Você provavelmente estão seguindo o Guia e sua func SumOf (números: [Int]) -> Int é realmente o cálculo da média
— gfelisberto

18

Você pode converter a função:

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

Ótimo! Isso funciona com vários parâmetros (nomeados) também; por exemplo: func sumOf(foo numbers: Int..., bar: Bool) -> Int {};exigetypealias Function = (foo: [Int], bar: Bool) -> Int;
— ThomasR 11/16/16

Ótimo! Salve minha vida.
— precisa saber é o seguinte

Como posso fazer coisas semelhantes com o AnyObject ...? stackoverflow.com/questions/42016358/… Obrigado.
— 21417 JerryZhou

Esta é uma péssima ideia. Não há absolutamente nenhuma garantia de que isso funcionará.
— Idmean

1
@MattMc Sure. Até onde eu sei, não há nada que permita que você simplesmente converta um tipo de chamada para outro usando unsafeBitCast. Isso pode funcionar hoje, mas, a menos que uma referência o diga, a próxima versão do compilador é livre para literalmente fazer qualquer coisa aqui (erro do compilador / falha / código de execução aleatória ...). Veja o aviso sério no unsafeBitCast .
— idmean 4/03

15

Você pode usar uma função auxiliar como tal:

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

12
Desde que haja uma versão para matrizes. Eu pensei que a premissa da pergunta é que a função original leva Int...e não pode (facilmente) ser alterada?

2
@ delnan: Correto. Parece-me que deveria haver uma maneira de passar um array para uma função variadica, dado que os argumentos variadicos são transformados em um array de qualquer maneira.
— precisa

1
Idiomas que aceitam número variável de argumentos em funções, como Scheme, têm um applyprocedimento. Eu acho que algumas pessoas chamam isso de 'splatting'.
— GoZoner

1
O que é sumArrayreferenciado aqui?
— Rob

2

Sei que esta resposta não responde à sua pergunta exata, mas acho que vale a pena notar. Eu também estava começando a brincar com Swift e imediatamente me deparei com uma pergunta semelhante. A resposta da Manojld é melhor para sua pergunta, concordo, mas, novamente, outra solução alternativa que me veio à cabeça. Por acaso também gosto mais do Logan.

No meu caso, eu só queria passar uma 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])

Só queria compartilhar, caso alguém mais estivesse pensando como eu. Na maioria das vezes eu preferiria passar o array assim, mas ainda não acho o "Swiftly". :)


1

Eu fiz isso (Wrapper + Mapeamento de identidade):

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

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

0

Swift 5

Esta é uma abordagem com @dynamicCallablerecurso que permite evitar sobrecargas ou que unsafeBitCastvocê deve especificar um específico structpara chamar:

@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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.