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.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow