Zoeken…


Syntaxis

  • f (n) = ...
  • functie f (n) ... einde
  • n :: Type
  • x -> ...
  • f (n) doen ... eindigen

Opmerkingen

Naast generieke functies (die het meest voorkomen), zijn er ook ingebouwde functies. Dergelijke functies omvatten is , isa , typeof , throw en soortgelijke functies. Ingebouwde functies worden doorgaans geïmplementeerd in C in plaats van Julia, dus ze kunnen niet gespecialiseerd zijn in argumenttypen voor verzending.

Vier een getal

Dit is de eenvoudigste syntaxis om een functie te definiëren:

square(n) = n * n

Gebruik ronde haakjes (zonder spaties ertussen) om een functie aan te roepen:

julia> square(10)
100

Functies zijn objecten in Julia en we kunnen ze in de REPL weergeven zoals bij alle andere objecten:

julia> square
square (generic function with 1 method)

Alle Julia-functies zijn standaard generiek (ook bekend als polymorf ). Onze square werkt net zo goed met drijvende-kommawaarden:

julia> square(2.5)
6.25

... of zelfs matrices :

julia> square([2 4
               2 1])
2×2 Array{Int64,2}:
 12  12
  6   9

Recursieve functies

Eenvoudige recursie

Met behulp van recursie en de ternaire voorwaardelijke operator kunnen we een alternatieve implementatie van de ingebouwde factorial creëren:

myfactorial(n) = n == 0 ? 1 : n * myfactorial(n - 1)

Gebruik:

julia> myfactorial(10)
3628800

Werken met bomen

Recursieve functies zijn vaak het handigst op gegevensstructuren, met name boomgegevensstructuren. Omdat expressies in Julia boomstructuren zijn, kan recursie behoorlijk nuttig zijn voor metaprogrammering . De onderstaande functie verzamelt bijvoorbeeld een set van alle koppen die in een expressie worden gebruikt.

heads(ex::Expr) = reduce(∪, Set((ex.head,)), (heads(a) for a in ex.args))
heads(::Any) = Set{Symbol}()

We kunnen controleren of onze functie werkt zoals bedoeld:

julia> heads(:(7 + 4x > 1 > A[0]))
Set(Symbol[:comparison,:ref,:call])

Deze functie is compact en maakt gebruik van verschillende geavanceerdere technieken, zoals de functie voor het reduce hogere orde , het gegevenstype Set en generatoruitdrukkingen.

Inleiding tot verzending

We kunnen de syntaxis :: om het type argument te verzenden.

describe(n::Integer) = "integer $n"
describe(n::AbstractFloat) = "floating point $n"

Gebruik:

julia> describe(10)
"integer 10"

julia> describe(1.0)
"floating point 1.0"

In tegenstelling tot veel talen, die doorgaans een statische meervoudige verzending of een dynamische enkele verzending bieden, heeft Julia een volledige dynamische meervoudige verzending. Dat wil zeggen dat functies voor meer dan één argument kunnen worden gespecialiseerd. Dit is handig bij het definiëren van gespecialiseerde methoden voor bewerkingen op bepaalde typen en fallback-methoden voor andere typen.

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"

Gebruik:

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"

Optionele argumenten

Met Julia kunnen functies optionele argumenten aannemen. Achter de schermen wordt dit geïmplementeerd als een ander speciaal geval van meervoudige verzending. Laten we bijvoorbeeld het populaire Fizz Buzz-probleem oplossen . Standaard doen we dit voor getallen in het bereik 1:10 , maar we staan indien nodig een andere waarde toe. We zullen ook toestaan dat verschillende zinnen worden gebruikt voor Fizz of 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

Als we fizzbuzz in de REPL inspecteren, staat er dat er vier methoden zijn. Er is één methode gemaakt voor elke toegestane combinatie van argumenten.

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

We kunnen controleren of onze standaardwaarden worden gebruikt als er geen parameters zijn opgegeven:

julia> fizzbuzz()
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz

maar dat de optionele parameters worden geaccepteerd en gerespecteerd als we ze verstrekken:

julia> fizzbuzz(5:8, "fuzz", "bizz")
bizz
fuzz
7
8

Parametrische verzending

Het is vaak het geval dat een functie moet worden verzonden op parametrische typen, zoals Vector{T} of Dict{K,V} , maar de typeparameters staan niet vast. Deze zaak kan worden behandeld met behulp van parametrische verzending:

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"

Men kan in de verleiding komen om eenvoudig xs::Vector{Number} . Maar dit werkt alleen voor objecten waarvan het type expliciet Vector{Number} :

julia> isa(Number[1, 2], Vector{Number})
true

julia> isa(Int[1, 2], Vector{Number})
false

Dit komt door parametrische invariantie : het object Int[1, 2] is geen Vector{Number} , omdat het alleen Int s kan bevatten, terwijl van een Vector{Number} verwacht kan worden dat het allerlei soorten getallen kan bevatten.

Generieke code schrijven

Verzending is een ongelooflijk krachtige functie, maar vaak is het beter om generieke code te schrijven die voor alle typen werkt, in plaats van voor elk type code te specialiseren. Het schrijven van generieke code voorkomt codeduplicatie.

Hier is bijvoorbeeld code om de som van vierkanten van een vector van gehele getallen te berekenen:

function sumsq(v::Vector{Int})
    s = 0
    for x in v
        s += x ^ 2
    end
    s
end

Maar deze code werkt alleen voor een vector van Int s. Het werkt niet op een UnitRange :

julia> sumsq(1:10)
ERROR: MethodError: no method matching sumsq(::UnitRange{Int64})
Closest candidates are:
  sumsq(::Array{Int64,1}) at REPL[8]:2

Het werkt niet op een 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

Een betere manier om deze sumsq functie te schrijven zou moeten zijn

function sumsq(v::AbstractVector)
    s = zero(eltype(v))
    for x in v
        s += x ^ 2
    end
    s
end

Dit werkt in de twee hierboven genoemde gevallen. Maar er zijn een aantal collecties waarvan we de vierkanten willen samenvatten en die helemaal geen vectoren zijn. Bijvoorbeeld,

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

laat zien dat we de vierkanten van een luie iterabele niet kunnen optellen .

Een nog meer generieke implementatie is eenvoudig

function sumsq(v)
    s = zero(eltype(v))
    for x in v
        s += x ^ 2
    end
    s
end

Wat in alle gevallen werkt:

julia> sumsq(take(countfrom(1), 100))
338350

Dit is de meest idiomatische Julia-code en kan allerlei situaties aan. In sommige andere talen kan het verwijderen van type-annotaties de prestaties beïnvloeden, maar dat is niet het geval in Julia; alleen typestabiliteit is belangrijk voor de prestaties.

Gebiedende wijs

Een lange syntaxis is beschikbaar voor het definiëren van functies met meerdere regels. Dit kan handig zijn wanneer we imperatieve structuren zoals lussen gebruiken. De uitdrukking in staartpositie wordt teruggegeven. De onderstaande functie gebruikt bijvoorbeeld een for lus om de faculteit van een geheel getal n te berekenen:

function myfactorial(n)
    fact = one(n)
    for m in 1:n
        fact *= m
    end
    fact
end

Gebruik:

julia> myfactorial(10)
3628800

In langere functies is het gebruikelijk om de return te zien. De return is niet noodzakelijk in staartpositie, maar het wordt nog steeds soms gebruikt voor duidelijkheid. Een andere manier om de bovenstaande functie te schrijven zou bijvoorbeeld zijn

function myfactorial(n)
    fact = one(n)
    for m in 1:n
        fact *= m
    end
    return fact
end

die qua gedrag identiek is aan de functie hierboven.

Anonieme functies

Pijl syntaxis

Anonieme functies kunnen worden gemaakt met behulp van de -> syntaxis. Dit is nuttig voor het doorgeven dient om hogere orde functies , zoals de map functie. De onderstaande functie berekent het kwadraat van elk nummer in een array A

squareall(A) = map(x -> x ^ 2, A)

Een voorbeeld van het gebruik van deze functie:

julia> squareall(1:10)
10-element Array{Int64,1}:
   1
   4
   9
  16
  25
  36
  49
  64
  81
 100

Meerlijnige syntaxis

Anonieme functies met meerdere regels kunnen worden gemaakt met behulp van function . In het volgende voorbeeld worden bijvoorbeeld de faculteiten van de eerste n nummers berekend, maar wordt een anonieme functie gebruikt in plaats van de ingebouwde 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

Blokkeer syntaxis

Omdat het zo gebruikelijk is om een anonieme functie door te geven als het eerste argument voor een functie, is er een do block-syntaxis. De syntaxis

map(A) do x
    x ^ 2
end

is gelijk aan

map(x -> x ^ 2, A)

maar de eerste kan in veel situaties duidelijker zijn, vooral als er veel berekeningen worden uitgevoerd in de anonieme functie. do block syntax is vooral handig voor bestandsinvoer en -uitvoer om redenen van bronbeheer.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow