Ricerca…


Sintassi

  • f (n) = ...
  • funzione f (n) ... fine
  • n :: Tipo
  • x -> ...
  • f (n) do ... end

Osservazioni

Oltre alle funzioni generiche (che sono le più comuni), esistono anche funzioni integrate. Tali funzioni includono is , isa , typeof , throw e funzioni simili. Le funzioni built-in sono in genere implementate in C anziché in Julia, quindi non possono essere specializzate sui tipi di argomenti per la spedizione.

Piazza un numero

Questa è la sintassi più semplice per definire una funzione:

square(n) = n * n

Per chiamare una funzione, usa parentesi tonde (senza spazi intermedi):

julia> square(10)
100

Le funzioni sono oggetti in Julia e possiamo mostrarle in REPL come con qualsiasi altro oggetto:

julia> square
square (generic function with 1 method)

Tutte le funzioni di Julia sono generiche (altrimenti conosciute come polimorfiche ) per impostazione predefinita. La nostra funzione square funziona altrettanto bene con valori in virgola mobile:

julia> square(2.5)
6.25

... o anche matrici :

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

Funzioni ricorsive

Ricorsione semplice

Usando la ricorsione e l' operatore condizionale ternario , possiamo creare un'implementazione alternativa della funzione factorial integrata:

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

Uso:

julia> myfactorial(10)
3628800

Lavorare con gli alberi

Le funzioni ricorsive sono spesso le più utili su strutture dati, in particolare strutture di dati ad albero. Poiché le espressioni in Julia sono strutture ad albero, la ricorsione può essere molto utile per la metaprogrammazione . Ad esempio, la funzione seguente raccoglie un insieme di tutte le teste utilizzate in un'espressione.

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

Possiamo verificare che la nostra funzione funzioni come previsto:

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

Questa funzione è compatta e utilizza una varietà di tecniche più avanzate, come la reduce funzione di ordine superiore , il tipo di dati Set e le espressioni del generatore.

Introduzione alla spedizione

Possiamo usare la :: sintassi per inviare il tipo di argomento.

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

Uso:

julia> describe(10)
"integer 10"

julia> describe(1.0)
"floating point 1.0"

A differenza di molte lingue, che in genere forniscono una distribuzione multipla statica o una singola spedizione dinamica, Julia ha una distribuzione multipla dinamica completa. Cioè, le funzioni possono essere specializzate per più di un argomento. Ciò è utile quando si definiscono metodi specializzati per operazioni su determinati tipi e metodi di fallback per altri tipi.

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"

Uso:

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"

Argomenti opzionali

Julia consente alle funzioni di prendere argomenti opzionali. Dietro le quinte, questo è implementato come un altro caso speciale di dispacciamento multiplo. Ad esempio, risolviamo il famoso problema di Fizz Buzz . Per impostazione predefinita, lo faremo per i numeri compresi nell'intervallo 1:10 , ma consentiremo un valore diverso se necessario. Permetteremo anche l'uso di frasi diverse per Fizz o 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

Se ispezioniamo fizzbuzz nel REPL, si dice che ci sono quattro metodi. È stato creato un metodo per ciascuna combinazione di argomenti consentita.

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

Possiamo verificare che i nostri valori predefiniti vengano utilizzati quando non vengono forniti parametri:

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

ma che i parametri opzionali sono accettati e rispettati se li forniamo:

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

Invio parametrico

È frequente il caso che una funzione debba essere inviata su tipi parametrici, come Vector{T} o Dict{K,V} , ma i parametri del tipo non sono corretti. Questo caso può essere risolto utilizzando la spedizione parametrica:

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"

Si potrebbe essere tentati di scrivere semplicemente xs::Vector{Number} . Ma questo funziona solo per oggetti il ​​cui tipo è esplicitamente Vector{Number} :

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

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

Ciò è dovuto all'invarianza parametrica : l'oggetto Int[1, 2] non è un Vector{Number} , perché può contenere solo Int s, mentre un Vector{Number} dovrebbe essere in grado di contenere qualsiasi tipo di numero.

Scrivere codice generico

Dispatch è una funzionalità incredibilmente potente, ma spesso è meglio scrivere codice generico che funzioni per tutti i tipi, invece di specializzare il codice per ogni tipo. La scrittura di codice generico evita la duplicazione del codice.

Ad esempio, ecco il codice per calcolare la somma dei quadrati di un vettore di numeri interi:

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

Ma questo codice funziona solo per un vettore di Int s. Non funzionerà su un UnitRange :

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

Non funzionerà su un 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

Dovrebbe essere un modo migliore per scrivere questa funzione sumsq

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

Questo funzionerà sui due casi sopra elencati. Ma ci sono alcune collezioni che potremmo voler sommare i quadrati di quello non sono affatto vettori, in nessun senso. Per esempio,

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

dimostra che non possiamo sommare i quadrati di un pigro iterabile .

Un'implementazione ancora più generica è semplicemente

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

Che funziona in tutti i casi:

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

Questo è il codice Julia più idiomatico e può gestire ogni tipo di situazione. In alcuni altri linguaggi, la rimozione delle annotazioni di tipo può influire sulle prestazioni, ma non è questo il caso in Julia; solo la stabilità del tipo è importante per le prestazioni.

Fattoriale imperativo

Una sintassi di lunga durata è disponibile per la definizione di funzioni multi-linea. Questo può essere utile quando usiamo strutture imperative come i loop. L'espressione nella posizione di coda viene restituita. Ad esempio, la funzione seguente usa un ciclo for per calcolare il fattoriale di qualche intero n :

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

Uso:

julia> myfactorial(10)
3628800

Nelle funzioni più lunghe, è comune vedere la dichiarazione di return utilizzata. Il return affermazione non è necessaria in posizione di coda, ma è ancora talvolta utilizzato per chiarezza. Ad esempio, un altro modo di scrivere la funzione sopra sarebbe

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

che è identico nel comportamento alla funzione di cui sopra.

Funzioni anonime

Sintassi della freccia

Le funzioni anonime possono essere create usando la sintassi -> . Questo è utile per passare funzioni a funzioni di ordine superiore , come la funzione map . La funzione seguente calcola il quadrato di ciascun numero in una matrice A

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

Un esempio di utilizzo di questa funzione:

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

Sintassi multilinea

Le funzioni anonime multilinea possono essere create usando la sintassi della function . Ad esempio, il seguente esempio calcola i fattoriali dei primi n numeri, ma usando una funzione anonima al posto del factorial incorporato.

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

Do la sintassi del blocco

Perché è così comune per passare una funzione anonima come primo argomento a una funzione, c'è un do sintassi del blocco. La sintassi

map(A) do x
    x ^ 2
end

è equivalente a

map(x -> x ^ 2, A)

ma il primo può essere più chiaro in molte situazioni, specialmente se viene eseguita molta computazione nella funzione anonima. do sintassi dei blocchi è particolarmente utile per l' input e l'output dei file per ragioni di gestione delle risorse.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow