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.