Swift – Funções

1 de novembro de 2016   |   by administrador
// Swift - Funções

/*
Definição de Função
Como em qualquer outra linguagem, funções são blocos de código (nomeados), com uma finalidade específica
Uma função tem um nome, pode ter parâmetros (argumentos) e também pode ter um valor de retorno (Em Swift, pode ter mais de um valor de retorno)

BOAS PRÁTICAS:
1) Uma função deve ter uma única finalidade
2) O nome da função deve dar uma ideia da sua finalidade
3) Evite codificar funções com "milhares de linhas de código"
4) Procure dar nomes em inglês ou em português, mas siga um padrão

Sintaxe na Swift:
func <nome função> (<nome parâmetro1>: <tipo parâmetro>, <nome parâmetro2>: <tipo parâmetro>, ...) -> <tipo do retorno> {
// Corpo da função
...
}
*/

// 1 - Função simples - sem parâmetros e sem valor de retorno
func sayHello1() {
print("Olá Alunos! Sejam bem-vindos!")
}

// Chama a função simples
sayHello1()

// 2 - Função com parâmetro, mas ainda sem valor de retorno
func sayHello2(nomePessoa: String) {
print("Olá, \(nomePessoa)")
}

// Chama a função passando o parâmetro
sayHello2(nomePessoa: "Jones")
sayHello2(nomePessoa: "Edivaldo")

// 3 - Sintaxe completa - com parâmetros e valor de retorno
func sayHello3(nomePessoa: String) -> String {
let cumprimento = "Olá, " + nomePessoa + "!"
return cumprimento
}

// Chama a função completa
let resposta = sayHello3(nomePessoa: "Jones")
print("\(resposta). Você é o nosso novo aluno")

// 4 - Funções com múltiplos parâmetros
func sayHello4(personName: String, alreadyGreeted: Bool) -> String {

if alreadyGreeted {
return "Olá, " + personName + ", novamente"
} else {
return "Olá " + personName
}
}

// Testando a função com múltiplos parâmetros
var retorno = sayHello4(personName: "José", alreadyGreeted: false)
retorno = sayHello4(personName: "José", alreadyGreeted: true)

// NOVIDADE SWIFT
// Função com múltiplos valores de retorno
func minMax(array: [Int]) -> (min: Int, max: Int) {

// A finalidade aqui é pesquisar, respectivamente, os valores nínimo e máximo de um array de inteiros
var currentMin = array[0]
var currentMax = array[0]

for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}

// Retorna os dois parâmetros num único retorno
return (currentMin, currentMax)
}

// Como chamar essa função?
let valores = minMax(array: [8, -6, 2, 109, 3, 71])
print("O menor valor é \(valores.min) e o maior valor é \(valores.max)")

// Apenas para tipificar o tipo de retorno
print(type(of: valores))

// Labels de Argumento e Nomes de Parâmetros
// Cada parâmetro pode ter um label de argumento seguido de um nome de parâmetro
// O label é utilizado na chamada da função
// O intuito é documentar melhor as chamadas

// Declaração
func funcao(LabelArgumento NomeParametro: Int) {

}

// Exemplo SEM e COM parâmetros nomeados

// Para receber o retorno das funções
var retornoJoin: String

// 1 - Forma normal (sem parâmetro nomeado)
func join(s1: String, s2: String, joiner: String) -> String {
return s1 + joiner + s2
}

// Chamando a função normal
retornoJoin = join(s1: "Hello", s2: "World", joiner: ", ")

// 2 - Com labels de argumentos e nomes dos parâmetros
func joinPE(string s1: String, toString s2: String, withJoiner joiner: String) -> String {
return s1 + joiner + s2
}

retornoJoin = joinPE(string: "Hello", toString: "World", withJoiner: ", ")

// Omitindo labels de argumentos
// Caso vcoê não queira especificar um label de argumento, ele pode ser omitido através da escrita de um caracter sublinhado (_)

// Declaração
func someFunction(_ primeiroParametro: Int, segundoParametro: Int) {
print("Valor do primeiro parâmetro: \(primeiroParametro)")
print("Valor do segundo parâmetro: \(segundoParametro)")
}

// Chamando a função, com label omitido
someFunction(1, segundoParametro: 2)

// Mais um recurso interessante: parâmetros com valor default (pré-definidos)

// Função com múltiplos parâmetros e parâmetro default
func login(name: String, withLastName: String? = "Savi", isEnable: Bool) -> String {

// Uma pitadinha de guard
// A ideia é executar a função apenas se o parâmetro isEnable for true
guard isEnable else {
return "A função só será executada se o parâmetro isEnable for true"
}

// Com código seguro
// Visando garantir que o parâmetro contenha um valor válido
// Podemos usar o valor passado na chamada ou o valor default
if let lastName = withLastName {
return "Bem-vindo de volta \(name) \(lastName)"
}

// Se o parâmetro não for especificado, utilizaremos o valor default
return "Bem-vindo \(name)"

}

// As formas de chamar essa função
// 1 - Omitindo o parâmetro default
var info = login(name: "Carlos", isEnable: true)

// 2 - Declare um valor default (válido)
info = login(name: "Pedro", withLastName: "Farias", isEnable: true)

// 3 - Declarando o valor default (inválido)
info = login(name: "João", withLastName: nil, isEnable: true)

// 4 - Parando no guardião da função - guard
info = login(name: "Carlos", isEnable: false)
info = login(name: "Carlos", withLastName: "Carvalho", isEnable: false)

// Função com parâmetros variadic
// Onde o número de parâmetros é variável
// Restrições:
// - Se a função tiver mais parâmetros, o variadic deve ser o último
// - Só pode haver 1 variadic por função
func mediaAritmetica(numeros: Double...) -> Double {
var total: Double = 0
for numero in numeros {
total += numero
}
return total / Double(numeros.count)
}

// Usando a variadic

// Para receber o retorno do cálculo
var media: Double?

// Exemplos de chamada
media = mediaAritmetica(numeros: 1, 2, 3, 4, 5)
media = mediaAritmetica(numeros: 10, 20)
let num = 10.0
media = mediaAritmetica(numeros: num, 20, 30)

// Função com parâmetros in-out
// Na função tradicional, ocorre uma cópia dos valores passados como parâmetros. Os valores originais não são alterados pelo processamento da função
// E se eu quiser que a função atualize as veriáveis que estou passando como parâmetros?
// Utilizando parâmetros in-out, é passado à função os enderedeços de memória das variáveis, e não uma cópia. Desta forma, a função consegue altere os valores originais.

// 1 - A forma tradicional
// A finalidade aqui é trocar (inverter) os valores de 2 inteiros
func troca2Inteiros(a: Int, b: Int) {
let temporaryA = a
// Não consguimos ter acesso às variáveis originais, pois elas foram passadas como cópia e são do tipo let
// As instruçÕes baixo geram um erro de compilação
// a = b
// b = temporaryA
}

// O parâmetro in-out resolve essa limitação
func trocaDoisInteiros(a: inout Int, b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}

// Chamada da função
var umInt = 3
var outroInt = 107

// Ao chamar uma função com parâmetros inout, utilizaremo ponteiros, ou seja, passaremos não a cópia mas o endereço de memória das variáveis
trocaDoisInteiros(a: &umInt, b: &outroInt)
print(umInt)
print(outroInt)
// Os valores originais foram trocados
// Aqui uma das raríssimas situações na linguagem Swift, onde utilizamos ponteiros

// Uma função pode ser um tipo de dados, na Swift
func soma2Ints(a: Int, b: Int) -> Int {
return a + b
}

func multiplica2Ints(a: Int, b: Int) -> Int {
return a * b
}

// Para receber o resultado do cálculo
var calculo: Int?

// Consguimos então, definir uma variável do tiupo função?
var mathFunction: (Int, Int) -> Int = soma2Ints

// E como usar isso?
calculo = mathFunction(2, 3)

// Posso variar as condições
mathFunction = multiplica2Ints
calculo = mathFunction(2, 3)

// A inferência também funciona
let anotherMathFunction = soma2Ints

// Funções aninhadas
func retornaQuinze() -> Int {
var y = 10
// Função aninhada
func add() {
// A função interna (aninhada) tem acesso às propriedades da função externa
y += 5
}
// A função externa consegue chamar a função interna (aninhada)
add()
return y
}

// Testar a função ainhada
let retornoFunc: Int?

// Para o mundo exterior, apenas a função extermna é visível
retornoFunc = retornaQuinze()
// No entanto, a função interna não é acessível
// A linha está comnentada pois gera um erro de compilação
//retornoFunc = add()