Fu

Funções em Julia

2 exercícios

Sobre Funções

O conceito Basics apresentou duas formas de definir uma função.

De forma mais geral, a forma com várias linhas:

function muladd(x, y, z)
    x * y + z
end

A forma de "atribuição", ou "de linha única", para definições curtas:

muladd(x, y, z) = x * y + z

Numa terceira forma, ainda mais curta, é possível criar uma função pequena, de uso único, sem nome:

julia> map(x -> 2x, 1:3)
4-element Vector{Int64}:
 2
 4
 6

julia> map((x, y) -> x * y, 1:3, 4:6)
3-element Vector{Int64}:
  4
 10
 18

Neste caso, x -> 2x é uma "função anónima". Isto equivale ao que outras linguagens chamam de "função lambda".

Repara que vários argumentos precisam de parênteses, como em (x, y) -> x * y.

As funções anónimas são comuns no código Julia, sobretudo quando combinadas com funções de ordem superior como map() e filter().

Argumentos das funções

Até agora, no programa, só vimos funções que têm um número exato de argumentos e que exigem que as chamadas à função forneçam todos eles, pela ordem correta. Isso seria limitativo e inconveniente, por isso existem várias outras opções.

Argumentos opcionais

Como muitas linguagens, o Julia permite que as definições de funções forneçam valores por omissão para argumentos individuais.

Depois, a chamada à função pode fornecer um valor para esse argumento, ou omiti-lo e usar o valor por omissão.

julia> f(x, y=10) = x * y
f (generic function with 2 methods)

julia> f(2, 3)
6

julia> f(2)
20

Todos os argumentos sem valores por omissão têm de vir antes de qualquer argumento com valores por omissão, o que significa que f(x=2, y) seria inválido.

Argumentos de palavra-chave

Todos os exemplos até agora usam positional arguments, em que os valores fornecidos numa chamada à função têm de corresponder à ordem dos argumentos correspondentes na definição da função.

Como muitas linguagens, o Julia também permite keyword arguments. As chamadas à função têm de indicar o nome do argumento, mas depois podem especificar-se vários argumentos de palavra-chave por qualquer ordem.

Uma característica distintiva do Julia é que os argumentos de palavra-chave (caso existam) na definição da função têm de ser precedidos por um ponto e vírgula ; para os separar de eventuais argumentos posicionais. Uma chamada à função pode usar ; ou , entre o último argumento posicional e o primeiro argumento de palavra-chave.

julia> b(x; y) = x + y
b (generic function with 1 method)

julia> b(2, y=3)
5

# keyword is required when calling
julia> b(2, 3)
ERROR: MethodError: no method matching b(::Int64, ::Int64)
The function `b` exists, but no method is defined for this combination of argument types.

Os valores por omissão podem ser especificados opcionalmente, exatamente como nos argumentos posicionais.

É comum acabarmos com uma sintaxe como myarg=myarg numa chamada à função, quando uma variável com o mesmo nome do parâmetro já foi calculada previamente. Nesta situação, é permitida uma sintaxe abreviada:

julia> width = 4.0
4.0

julia> height = √ width
2.0

julia> area(; width, height) = width * height
area (generic function with 1 method)

# repetition
julia> area(; width=width, height=height)
8.0

# shorthand form
julia> area(; width, height)
8.0

Splat e slurp

Estes são os nomes habituais de um aspeto útil da sintaxe do Julia, caso tenhas curiosidade. Ambos se referem ao operador ....

Splat

O splatting usa-se nas chamadas às funções, para expandir coleções em valores individuais exigidos pela função.

Talvez seja mais fácil mostrar do que explicar:

julia> fxyz(x, y, z) = x * y * z
fxyz (generic function with 1 method)

julia> xyz = [2, 3, 4]
3-element Vector{Int64}:
 2
 3
 4

# Using the vector directly in a function call is invalid
julia> fxyz(xyz)
ERROR: MethodError: no method matching fxyz(::Vector{Int64})
The function `fxyz` exists, but no method is defined for this combination of argument types.

# splatting converts the vector to 3 numbers, used as positional arguments
julia> fxyz(xyz...)
24

Algumas "chamadas a funções" estão escondidas por açúcar sintático, por isso o splatting também pode ser usado de formas menos óbvias.

Por exemplo, a atribuição múltipla usa internamente uma função construtora de tuplos:

julia> first, rest... = [1, 2, 3, 4]
4-element Vector{Int64}:
 1
 2
 3
 4

julia> first
1

julia> rest
3-element Vector{Int64}:
 2
 3
 4

Os argumentos de palavra-chave também podem ser fornecidos por splatting, normalmente com um named tuple. Um Dict também funciona, mas as chaves têm de ser símbolos (as strings não funcionam aqui).

# function with 3 keyword arguments
julia> fabc(; a, b, c) = a + b + c
fabc (generic function with 1 method)

# named tuple
julia> abc_nt = (a=2, b=3, c=4)
(a = 2, b = 3, c = 4)

# there are no positional arguments, so need to use ; before kw argument
julia> fabc(;abc_nt...)
9

# Dict
julia> abc_dict = Dict(:a=>2, :b=>3, :c=>4)
Dict{Symbol, Int64} with 3 entries:
  :a => 2
  :b => 3
  :c => 4

julia> fabc(;abc_dict...)
9
Slurp

O slurping usa-se na definição da função, para empacotar um número arbitrário de valores individuais numa coleção.

julia> f_more(i, j, more...) = i + j + sum(more)
f_more (generic function with 1 method)

julia> f_more(1, 3, 5, 7, 9, 11)
36

O nome do argumento recolhido pelo slurp (neste caso, more) não tem importância. O tipo desta variável é escolhido pelo compilador, mas, para argumentos posicionais, é provavelmente tuple ou algo semelhante.

Os argumentos de palavra-chave também podem ser recolhidos por slurp, dando um Dict (ou algo semelhante).

julia> f_kwslurp(x, y; switches...) = :mult in keys(switches) ? x * y : x + y
f_kwslurp (generic function with 1 method)

julia> f_kwslurp(5, 6; mult=true)
30

julia> f_kwslurp(5, 6)
11

Pode usar-se qualquer argumento de palavra-chave na chamada. Cabe à definição da função decidir a que palavras-chave responder e quais ignorar.

Editar via GitHub A ligação abre numa nova janela ou separador

Aprende Funções

A prática está bloqueada

Desbloqueia mais 1 exercício para praticares Funções