Kodumaro :: Funções e métodos em Julia

Publicado em 19 de Dezembro, 2017
As sombras da programação.
Julia Lang

Uma ideia bacana em Julia é a relação entre funções e métodos.

Por exemplo, inicie o prompt da linguagem:

sh> julia
_
_ _ _(_)_ | A fresh approach to technical computing
(_) | (_) (_) | Documentation: https://docs.julialang.org
_ _ _| |_ __ _ | Type "?help" for help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 0.6.2 (2017-12-13 18:08 UTC)
_/ |\__'_|_|_|\__'_| | Oaaicial http://julialang.org/ release
|__/ | x86_64-pc-linux-gnu

julia>

Agora digite:

julia> methods(+)
# 180 methods for generic function "+":

Isso é seguido por cento e oitenta descrições de métodos. Vamos tomar dois métodos:

+(x::Bool, y::Bool) in Base at bool.jl:89
+(x::T, y::T) where T<:Number in Base at promotion.jl:335

Isso significa que, quando chamamos a função + com dois booleanos:

julia> true + true
2

Será executado o método +(x::Bool, y::Bool).

Quando for chamado com dois valores numéricos:

julia > 3 + 4
7

Será executado o método +(x::T, y::T) where T<:Number.

Sempre que você escreve uma “função” em Julia, na verdade você está escrevendo um método que será chamado para aquela função, dados os tipos de parâmetro corretos.

Se não houver um método para atender àquela assinatura específica, será levantado um erro de método (MethodError). Por exemplo:

julia > :a + :b
ERROR: MethodError: no method matching +(::Symbol, ::Symbol)
Closest candidates are:
+(::Any, ::Any, ::Any, ::Any...) at operators.jl:424

Essa abordagem é ao mesmo tempo coerente com sobrecarga de funções em programação funcional com tipos e com passagem de mensagens em orientação a objetos.

Odd words

Como exemplo de métodos diferentes para mesma função, vamos implementar uma brincadeira chamada odd words: dada uma frase, atribuindo um índice a cada palavra a partir de zero, o objetivo é inverter as palavras com índice ímpar.

Para isso vamos criar um módulo que pode se chamar oddwords.jl:

#!/usr/bin/env julia

module OddWords

    #=
     = O código vai aqui
     =#

end

A inicialização do módulo vai passar os parâmetros da linha de comando (ARGS), aplicar a função oddwords e exibir na tela:

__init__() = ARGS |> oddwords |> println
vamos definir o primeiro método, que receberá um array de strings:
oddwords(phrase::Array{String}) = oddwords(phrase, :even, "")

Esse primeiro método repassa a outro método, indicando que o primeiro índice (zero) é par e passando como acumulador uma string vazia.

O método seguinte, que tratará essa chamada, testará se a frase está vazia, caso contrário, inverterá (ou não) a palavra atual segundo a rodada:

oddwords(phrase::Array{String}, round::Symbol, acc::String) =
    isempty(phrase) ? strip(acc) : odd_even(phrase, Val{round}, acc)

Observação:

O tipo Val é um recurso importante em Julia para permitir despacho de métodos baseado em valor em vez de tipo.

O primeiro método da função odd_even tratará índice par, passando a primeira palavra (os índices de Julia começam em um) da frase para o acumulador, e chamando oddwords novamente, agora com o índice ímpar:

odd_even(phrase::Array{String}, ::Type{Val{:even}}, acc::String) =
    oddwords(phrase[2:end], :odd, acc * " " * phrase[1])

O próximo método tratará índice ímpar, fazendo o mesmo que o anterior, mas invertendo a palavra atual:

odd_env(phrase::Array{String}, ::Type{Val{:odd}}, acc::String) =
    oddwords(phrase[2:end], :even, acc * " " * phrase[1][end:-1:1])

Já é suficiente:

sh> chmod +x oddwords.jl
sh> ./oddwords.jl esse teste verifica se as palavras serão invertidas
esse etset verifica es as sarvalap serão saditrevni

Sucesso!

O método correto é chamado de acordo com os tipos dos parâmetros:

julia> include("oddwords.jl")

OddWords

julia> import OddWords

julia> methods(OddWords.oddwords)
# 2 methods for generic function "oddwords":
oddwords(phrase::Array{String,N} where N) in OddWords at ./oddwords.jl:7
oddwords(phrase::Array{String,N} where N, round::Symbol, acc::String) in OddWords at ./oddwords.jl:9

julia> methods(OddWords.odd_even)
# 2 methods for generic function "odd_even":
odd_even(phrase::Array{String,N} where N, ::Type{Val{:even}}, acc::String) in OddWords at ./oddwords.jl:12
odd_even(phrase::Array{String,N} where N, ::Type{Val{:odd}}, acc::String) in OddWords at ./oddwords.jl:15

Funcional