{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Despacho múltiplo\n", "\n", "Neste notebook, vamos explorar o conceito de [despacho múltiplo](https://en.wikipedia.org/wiki/Multiple_dispatch), que é um recurso chave da linguagem Julia.\n", "\n", "Para entender despacho múltiplo em Julia, vamos começar com um exemplo que nós já vimos.\n", "\n", "Em Julia, podemos declarar funções sem informar o tipo dos argumentos da função.\n", "Por exemplo:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "f (generic function with 1 method)" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(x) = x^2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Então a Julia vai determinar automaticamente sobre quais tipos de argumentos faz sentido aplicar a função, e sobre quais tipos não faz:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "81" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(9)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "ename": "LoadError", "evalue": "MethodError: no method matching ^(::Vector{Int64}, ::Int64)\n\u001b[0mClosest candidates are:\n\u001b[0m ^(\u001b[91m::Union{AbstractChar, AbstractString}\u001b[39m, ::Integer) at strings/basic.jl:730\n\u001b[0m ^(\u001b[91m::Complex{<:AbstractFloat}\u001b[39m, ::Integer) at complex.jl:860\n\u001b[0m ^(\u001b[91m::Complex{<:Integer}\u001b[39m, ::Integer) at complex.jl:862\n\u001b[0m ...", "output_type": "error", "traceback": [ "MethodError: no method matching ^(::Vector{Int64}, ::Int64)\n\u001b[0mClosest candidates are:\n\u001b[0m ^(\u001b[91m::Union{AbstractChar, AbstractString}\u001b[39m, ::Integer) at strings/basic.jl:730\n\u001b[0m ^(\u001b[91m::Complex{<:AbstractFloat}\u001b[39m, ::Integer) at complex.jl:860\n\u001b[0m ^(\u001b[91m::Complex{<:Integer}\u001b[39m, ::Integer) at complex.jl:862\n\u001b[0m ...", "", "Stacktrace:", " [1] literal_pow", " @ ./intfuncs.jl:340 [inlined]", " [2] f(x::Vector{Int64})", " @ Main ./In[1]:1", " [3] top-level scope", " @ In[3]:1", " [4] eval", " @ ./boot.jl:368 [inlined]", " [5] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)", " @ Base ./loading.jl:1428" ] } ], "source": [ "f([1, 2, 3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Especificando os tipos dos argumentos\n", "\n", "Temos a opção de declarar explicitamente quais tipos de argumentos uma função pode receber.\n", "Por exemplo, vamos definir uma função `teste` que só pode receber argumentos do tipo `String`:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "teste (generic function with 1 method)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "teste(x::String, y::String) = println(\"Os argumentos x e y são strings\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos verificar que a função `teste` age sobre strings, mas não age sobre argumentos de outros tipos:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Os argumentos x e y são strings\n" ] } ], "source": [ "teste(\"despacho\", \"múltiplo\")" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "ename": "LoadError", "evalue": "MethodError: no method matching teste(::Int64, ::Int64)", "output_type": "error", "traceback": [ "MethodError: no method matching teste(::Int64, ::Int64)", "", "Stacktrace:", " [1] top-level scope", " @ In[6]:1", " [2] eval", " @ ./boot.jl:368 [inlined]", " [3] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)", " @ Base ./loading.jl:1428" ] } ], "source": [ "teste(2, 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para fazer com que a função `teste` aceite números inteiros, vamos definir `teste` para argumentos do tipo `Int`:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "teste (generic function with 2 methods)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "teste(x::Int, y::Int) = println(\"Os argumentos x e y são números inteiros\")" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Os argumentos x e y são números inteiros\n" ] } ], "source": [ "teste(2, 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Agora `teste` age sobre os inteiros.\n", "Mas observe, a função `teste` continua definida quando `x` e `y` são strings:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Os argumentos x e y são strings\n" ] } ], "source": [ "teste(\"despacho\", \"múltiplo\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quando declaramos\n", "```julia\n", "teste(x::Int, y::Int) = println(\"Os argumentos x e y são números inteiros\"),\n", "```\n", "não removemos ou substituímos\n", "```julia\n", "teste(x::String, y::String),\n", "```\n", "simplesmente adicionamos um método à função genérica `teste`.\n", "\n", "Uma **função genérica** é um conceito abstrato associado a uma operação.\n", "Por exemplo, a função genérica `+` representa o conceito de adição.\n", "Um **método** é uma implementação específica de uma função genérica para um tipo específico de argumento.\n", "Por exemplo, a função `+` possui métodos para números de precisão flutuante, inteiros, matrizes, etc.\n", "Podemos usar `methods` para ver os métodos que a função `teste` possui:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "# 2 methods for generic function teste: