Julia Language
funktioner
Sök…
Syntax
- f (n) = ...
- funktion f (n) ... slut
- n :: Typ
- x -> ...
- f (n) gör ... slut
Anmärkningar
Förutom generiska funktioner (som är vanligast) finns det också inbyggda funktioner. Sådana funktioner inkluderar is
, isa
, typeof
, throw
, och liknande funktioner. Inbyggda funktioner implementeras vanligtvis i C istället för Julia, så de kan inte specialiseras på argumenttyper för sändning.
Ruta ett nummer
Detta är den enklaste syntaxen för att definiera en funktion:
square(n) = n * n
För att ringa en funktion använder du runda parenteser (utan mellanrum):
julia> square(10)
100
Funktioner är objekt i Julia, och vi kan visa dem i REPL som med alla andra objekt:
julia> square
square (generic function with 1 method)
Som standard är alla Julia-funktioner generiska (annars känd som polymorf ). Vår square
funktion fungerar lika bra med värden på flytande punkter:
julia> square(2.5)
6.25
... eller till och med matriser :
julia> square([2 4
2 1])
2×2 Array{Int64,2}:
12 12
6 9
Rekursiva funktioner
Enkel rekursion
Med rekursion och den ternära villkorade operatören kan vi skapa en alternativ implementering av den inbyggda factorial
:
myfactorial(n) = n == 0 ? 1 : n * myfactorial(n - 1)
Användande:
julia> myfactorial(10)
3628800
Arbeta med träd
Rekursiva funktioner är ofta mest användbara för datastrukturer, särskilt träddatastrukturer. Eftersom uttryck i Julia är trädstrukturer kan rekursion vara ganska användbar för metaprogrammering . Exempelvis samlar nedanstående funktion en uppsättning av alla huvuden som används i ett uttryck.
heads(ex::Expr) = reduce(∪, Set((ex.head,)), (heads(a) for a in ex.args))
heads(::Any) = Set{Symbol}()
Vi kan kontrollera att vår funktion fungerar som avsedd:
julia> heads(:(7 + 4x > 1 > A[0]))
Set(Symbol[:comparison,:ref,:call])
Denna funktion är kompakt och använder en mängd mer avancerade tekniker, såsom funktionen för att reduce
högre ordning , Set
datatyp och generatoruttryck.
Introduktion till avsändning
Vi kan använda ::
syntax för att leverans på typen av argument.
describe(n::Integer) = "integer $n"
describe(n::AbstractFloat) = "floating point $n"
Användande:
julia> describe(10)
"integer 10"
julia> describe(1.0)
"floating point 1.0"
Till skillnad från många språk, som vanligtvis tillhandahåller antingen statisk flera sändningar eller dynamisk enstaka sändning, har Julia full dynamisk flera sändningar. Det vill säga, funktioner kan specialiseras för mer än ett argument. Detta är praktiskt när man definierar specialiserade metoder för operationer på vissa typer och fallbackmetoder för andra typer.
describe(n::Integer, m::Integer) = "integers n=$n and m=$m"
describe(n, m::Integer) = "only m=$m is an integer"
describe(n::Integer, m) = "only n=$n is an integer"
Användande:
julia> describe(10, 'x')
"only n=10 is an integer"
julia> describe('x', 10)
"only m=10 is an integer"
julia> describe(10, 10)
"integers n=10 and m=10"
Valfritt argument
Julia tillåter funktioner att ta valfria argument. Bakom kulisserna implementeras detta som ett annat speciellt fall med flera sändningar. Låt oss till exempel lösa det populära Fizz Buzz-problemet . Som standard gör vi det för siffror i intervallet 1:10
, men vi tillåter ett annat värde om det behövs. Vi tillåter också att olika fraser används för Fizz
eller Buzz
.
function fizzbuzz(xs=1:10, fizz="Fizz", buzz="Buzz")
for i in xs
if i % 15 == 0
println(fizz, buzz)
elseif i % 3 == 0
println(fizz)
elseif i % 5 == 0
println(buzz)
else
println(i)
end
end
end
Om vi inspekterar fizzbuzz
i REPL säger det att det finns fyra metoder. En metod skapades för varje tillåten kombination av argument.
julia> fizzbuzz
fizzbuzz (generic function with 4 methods)
julia> methods(fizzbuzz)
# 4 methods for generic function "fizzbuzz":
fizzbuzz() at REPL[96]:2
fizzbuzz(xs) at REPL[96]:2
fizzbuzz(xs, fizz) at REPL[96]:2
fizzbuzz(xs, fizz, buzz) at REPL[96]:2
Vi kan verifiera att våra standardvärden används när inga parametrar tillhandahålls:
julia> fizzbuzz()
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
men att de valfria parametrarna accepteras och respekteras om vi tillhandahåller dem:
julia> fizzbuzz(5:8, "fuzz", "bizz")
bizz
fuzz
7
8
Parametrisk utsändning
Det är ofta så att en funktion ska skickas på parametriska typer, till exempel Vector{T}
eller Dict{K,V}
, men typparametrarna är inte fixade. Detta fall kan hanteras med hjälp av parametrisk sändning:
julia> foo{T<:Number}(xs::Vector{T}) = @show xs .+ 1
foo (generic function with 1 method)
julia> foo(xs::Vector) = @show xs
foo (generic function with 2 methods)
julia> foo([1, 2, 3])
xs .+ 1 = [2,3,4]
3-element Array{Int64,1}:
2
3
4
julia> foo([1.0, 2.0, 3.0])
xs .+ 1 = [2.0,3.0,4.0]
3-element Array{Float64,1}:
2.0
3.0
4.0
julia> foo(["x", "y", "z"])
xs = String["x","y","z"]
3-element Array{String,1}:
"x"
"y"
"z"
Man kan frestas att bara skriva xs::Vector{Number}
. Men detta fungerar bara för objekt vars typ uttryckligen är Vector{Number}
:
julia> isa(Number[1, 2], Vector{Number})
true
julia> isa(Int[1, 2], Vector{Number})
false
Detta beror på parametrisk invarians : objektet Int[1, 2]
är inte en Vector{Number}
, eftersom det bara kan innehålla Int
, medan en Vector{Number}
förväntas kunna innehålla alla slags siffror.
Skriva generisk kod
Dispatch är en otroligt kraftfull funktion, men ofta är det bättre att skriva generisk kod som fungerar för alla typer istället för att specialisera kod för varje typ. Att skriva generisk kod undviker koddubbling.
Här är till exempel kod för att beräkna summan av kvadrater för en heltalsvektor:
function sumsq(v::Vector{Int})
s = 0
for x in v
s += x ^ 2
end
s
end
Men den här koden fungerar bara för en vektor av Int
. Det fungerar inte på en UnitRange
:
julia> sumsq(1:10)
ERROR: MethodError: no method matching sumsq(::UnitRange{Int64})
Closest candidates are:
sumsq(::Array{Int64,1}) at REPL[8]:2
Det fungerar inte på en Vector{Float64}
:
julia> sumsq([1.0, 2.0])
ERROR: MethodError: no method matching sumsq(::Array{Float64,1})
Closest candidates are:
sumsq(::Array{Int64,1}) at REPL[8]:2
Ett bättre sätt att skriva denna sumsq
funktion borde vara
function sumsq(v::AbstractVector)
s = zero(eltype(v))
for x in v
s += x ^ 2
end
s
end
Detta fungerar på de två fallen som anges ovan. Men det finns några samlingar som vi kanske vill summera kvadraterna för som inte är vektorer alls, på något sätt. Till exempel,
julia> sumsq(take(countfrom(1), 100))
ERROR: MethodError: no method matching sumsq(::Base.Take{Base.Count{Int64}})
Closest candidates are:
sumsq(::Array{Int64,1}) at REPL[8]:2
sumsq(::AbstractArray{T,1}) at REPL[11]:2
visar att vi inte kan summera kvadraterna för en lat iterable .
En ännu mer generisk implementering är helt enkelt
function sumsq(v)
s = zero(eltype(v))
for x in v
s += x ^ 2
end
s
end
Vilket fungerar i alla fall:
julia> sumsq(take(countfrom(1), 100))
338350
Detta är den mest idiomatiska Julia-koden och kan hantera alla möjliga situationer. På vissa andra språk kan borttagande av annoteringar påverka prestandan, men det är inte fallet i Julia; endast typstabilitet är viktig för prestanda.
Imperativ factorial
En syntax med lång form är tillgänglig för att definiera multilinjefunktioner. Detta kan vara användbart när vi använder tvingande strukturer som slingor. Uttrycket i svansläge returneras. Exempelvis använder funktionen nedan en for
loop för att beräkna faktorn för vissa heltal n
:
function myfactorial(n)
fact = one(n)
for m in 1:n
fact *= m
end
fact
end
Användande:
julia> myfactorial(10)
3628800
I längre fungerar, är det vanligt att se return
uttalande används. return
är inte nödvändig i svansläge, men det används fortfarande ibland för tydlighet. Till exempel skulle ett annat sätt att skriva ovanstående funktion vara
function myfactorial(n)
fact = one(n)
for m in 1:n
fact *= m
end
return fact
end
vilket är identiskt i beteende med funktionen ovan.
Anonyma funktioner
Pil syntax
Anonyma funktioner kan skapas med hjälp av ->
syntaxen. Detta är användbart för att släppa igenom funktioner till högre ordningens funktioner , såsom map
Funktionen nedan beräknar kvadratet för varje nummer i en matris A
squareall(A) = map(x -> x ^ 2, A)
Ett exempel på att använda den här funktionen:
julia> squareall(1:10)
10-element Array{Int64,1}:
1
4
9
16
25
36
49
64
81
100
Multiline syntax
Multilina anonyma funktioner kan skapas med function
. Till exempel, följande exempel beräknar faktorial hos de första n
siffror, men med användning av en anonym funktion i stället för den inbyggda factorial
.
julia> map(function (n)
product = one(n)
for i in 1:n
product *= i
end
product
end, 1:10)
10-element Array{Int64,1}:
1
2
6
24
120
720
5040
40320
362880
3628800
Blockera syntax
Eftersom det är så vanligt att vidarebefordra en anonym funktion som det första argumentet till en funktion finns det en do
block-syntax. Syntaxen
map(A) do x
x ^ 2
end
är ekvivalent med
map(x -> x ^ 2, A)
men det förra kan vara tydligare i många situationer, särskilt om mycket beräkning görs i den anonyma funktionen. do
block-syntax är särskilt användbart för filinmatning och -utgång av resurshanteringsskäl.