Curso
Quer se aprofundar em Scala? Faça o curso Introduction to Scala da DataCamp.
Scala é uma linguagem de programação funcional que trata funções como valores de primeira classe e também possui métodos, com semelhanças e diferenças entre eles. Tanto funções quanto métodos são blocos de código reutilizáveis usados para centralizar lógicas repetidas e, assim, permitir chamadas que executam tarefas específicas. Eles também facilitam a depuração e a manutenção do código.
Porém, funções são objetos inicializados em variáveis, enquanto métodos começam com a palavra-chave 'def', seguida do nome do método, lista de parâmetros e corpo do método com o valor de retorno.
Declaração e definição de métodos
A sintaxe para declarar e definir métodos em Scala é a seguinte:
def 'method_name' ('parameters':'return_type_parameters') : ('return_type_of_method') = {
'method_body'
return 'value'
}
Um método em Scala é composto pelas partes abaixo:
- 'def': palavra-chave usada para declarar métodos.
- 'method_name': é o nome do método, normalmente em lower camel case.
- 'parameters': são os parâmetros do método; pode não haver nenhum, um ou vários, separados por vírgula quando houver mais de um.
- 'return_type_of_parameters': deve corresponder ao tipo de dados da 'parameters_list' e é obrigatório.
- 'return_type_of_method': é opcional; por padrão, retorna 'Unit', mas um valor pode ser retornado usando a palavra-chave 'return'.
- Símbolo de atribuição ('='): é opcional; se usado, indica que haverá valor de retorno; se omitido, o método não retorna nada.
- 'method_body': bloco de código entre chaves '{}' que contém a lógica, tarefa ou operações necessárias.
- return: palavra-chave usada para retornar valores e encerrar a execução naquele ponto; em Scala, é raramente utilizada.
Chamada de método
A sintaxe para chamar um método em Scala é:
method_name(arguments)
A chamada é feita passando os argumentos para 'method_name', que é o nome do método correspondente que você deseja executar.
Método com argumentos nomeados
Métodos com argumentos nomeados permitem associar explicitamente cada argumento ao parâmetro correspondente durante a chamada. Veja abaixo um exemplo de passagem de argumentos, com a declaração/definição da função e a chamada em ação:
object calculateResult {
def funSub(x:Int, y:Int) : Int =
{
var diff:Int = 0
diff = x - y
// return value
return diff
}
def main(args: Array[String]) {
// Function call
println("Difference of the value is: " + funSub(8,6));
println("Difference of the value is " + funSub(y=6,x=8));
}
}
O programa acima gera a saída:
Difference of the value is: 2
Difference of the value is: 2
No exemplo, temos o objeto 'calculateResult' e, dentro dele, o método 'funSub' com os parâmetros x e y, ambos do tipo 'Int'. O tipo de retorno do método também é 'Int'. Em seguida, há uma atribuição para o valor de retorno. As chaves indicam o início do corpo do método, onde a variável 'diff' é inicializada com 0. A chamada 'funSub(8,6)' feita em 'main' associa 8 a 'x' e 6 a 'y'; a subtração é realizada, o valor de 'diff' é retornado e impresso. Da mesma forma, 'funSub(x=8,y=6)' associa 8 a 'x' e 6 a 'y' via nomes (a ordem não importa); a operação é a mesma e o resultado é impresso.
Valores padrão de parâmetros
Você pode definir valores padrão para parâmetros de métodos, inicializando-os na assinatura. Assim, é possível omitir argumentos na chamada.
object calculateResult {
def funSub(x:Int=9, y:Int=6) : Int =
{
var diff:Int = 0
diff = x - y
// return value
return diff
}
def main(args: Array[String]) {
// Function call
print( "The final value is: " + funSub() );
}
}
O programa acima gera a saída:
The final value is: 3
No código, o objeto 'calculateResult' contém o método 'funSub' com parâmetros x e y, ambos do tipo 'Int', e tipo de retorno 'Int'. A atribuição final indica o valor retornado. O corpo do método inicia com as chaves, e 'diff' é inicializada em 0. Em 'main', a chamada 'funSub()' usa os valores padrão x = 9 e y = 6; a operação é realizada, 'diff' é retornado e impresso.
Argumentos de comprimento variável
Argumentos de comprimento variável permitem passar uma quantidade qualquer de valores, conforme desejado pelo usuário/cliente. O último parâmetro do método é declarado usando '*' para indicar repetição.
object variableArgument {
def main(args: Array[String]) {
printAll("Scala", "is", "great")
}
def printAll(strings: String*) {
var i : Int = 0;
for( value <- strings ){
println(value);
i = i + 1;
}
}
}
O programa acima gera a saída:
Scala
is
great
Note que o objeto 'variableArgument' define o método 'printAll' com o argumento de comprimento variável 'String*'. Na chamada do método, é possível passar uma lista de strings. A lista recebida é percorrida e seus valores são impressos na função principal.
Função recursiva
Recursão é uma técnica comum em programação funcional: o problema é resolvido ao dividi-lo repetidamente em subproblemas menores, semelhantes ao original. Resolvem-se os menores e suas soluções são combinadas para chegar ao resultado final. Por isso, é conhecido como abordagem de dividir e conquistar (ou diminuir e conquistar), deixando o código mais limpo e elegante. Ao escrever uma função recursiva, dois pontos são essenciais:
- Caso base: pode haver mais de um caso base; se não for bem definido, a recursão pode se tornar infinita. O caso base precisa ter uma solução direta, sem recursão.
- Caso recursivo: onde as chamadas recursivas são feitas. O ponto-chave é que, a cada chamada, o problema diminui de tamanho, dividindo-se em subproblemas até chegar ao caso base. Ao final, as soluções são combinadas para resolver o problema principal (abordagem divide and conquer).
O fatorial de um número qualquer é n! e pode ser expresso assim:
Também podemos escrever (n-1).(n-2)...3.2.1 como (n-1)!. Logo, o fatorial de n é n.(n-1)!
Veja abaixo a solução recursiva do problema.
object factorialDemo {
def main(args: Array[String]) {
for (i <- 1 to 5)
println( "Factorial of " + i + ": = " + factorial(i) )
}
def factorial(n: Int): Int = {
if (n <= 1)
return 1
else
return n * factorial(n - 1)
}
}
O programa acima gera a saída:
Factorial of 1: = 1
Factorial of 2: = 2
Factorial of 3: = 6
Factorial of 4: = 24
Factorial of 5: = 120
Temos o objeto 'factorialDemo' com a função 'main' que executa um loop de 1 a 5 e chama 'factorial' a cada iteração. A função recursiva faz chamadas sucessivas até atingir o caso base (n menor ou igual a 1), quando então retorna o resultado, que é impresso.
Função anônima
Funções anônimas são definições leves de função sem nome, conhecidas em Scala como function literals.
Sintaxe com exemplos de função anônima:
- Primeira sintaxe e exemplo:
Sintaxe:
('first_variable':'data_type', 'second_variable':'data_type') => "certain_expression"
Exemplo:
(var1:String:var2:String) => var1 + var2
OU
- Segunda sintaxe e exemplo:
Sintaxe:
(_:'data_type')operator(_'data_type')
Exemplo:
(_:String)+(_:String)
Na primeira sintaxe, a expressão após "=>" é avaliada para um valor específico, enquanto a lista de variáveis antes de "=>" é usada para avaliar a expressão.
Na segunda, usamos placeholders: cada sublinhado aceita um valor como um curinga apenas uma vez, e então o operador é aplicado entre eles.
Veja um exemplo de função anônima:
object anonymousDemo
{
def main(args: Array[String])
{
var function1 = (var1:Int, var2:Int) => var1 + var2
var function2 = (_:Int) + (_:Int)
// function call
println(function1(5, 5))
println(function2(7, 3))
}
}
Initializing Scala interpreter ...
Spark Web UI available at http://DESKTOP-03TH7J0:4040
SparkContext available as 'sc' (version = 2.4.3, master = local[*], app id = local-1566986265958)
SparkSession available as 'spark'
<console>:10: error: unbound placeholder parameter
var function2 = {_:Int} + {_:Int}
^
<console>:10: error: unbound placeholder parameter
var function2 = {_:Int} + {_:Int}
^
O programa acima gera a saída:
10
10
No objeto 'anonymousDemo', a função 'main' define duas funções anônimas. Elas têm sintaxes diferentes, mas produzem o mesmo resultado. 'function1' usa parâmetros nomeados ao ser chamada (passamos 5 e 5), resultando em 10. 'function2' usa placeholders, recebe 7 e 3, aceita quaisquer inteiros válidos, soma e imprime o resultado.
Conclusão
Parabéns! Você concluiu a leitura deste tutorial.
Há muitos tópicos avançados em Scala, como higher-order functions, funções aninhadas, currying, entre outros.
Referências:
TOUR OF SCALA BASICS
Scala | Functions – Basics
Scala - Functions
Anonymous Functions in Scala
Confira o curso Introduction to Scala da DataCamp.
