Recherche…


Syntaxe

  • MyType immuable champ; champ; fin
  • tapez MyType; champ; champ; fin

Remarques

Les types sont la clé de la performance de Julia. Une idée importante pour la performance est la stabilité des types , qui se produit lorsque le type renvoyé par une fonction dépend uniquement des types, et non des valeurs, de ses arguments.

Envoi sur types

Sur Julia, vous pouvez définir plusieurs méthodes pour chaque fonction. Supposons que nous définissions trois méthodes de la même fonction:

foo(x) = 1
foo(x::Number) = 2
foo(x::Int) = 3

Lors du choix de la méthode à utiliser (appelée dispatch ), Julia choisit la méthode plus spécifique correspondant aux types d'arguments:

julia> foo('one')
1

julia> foo(1.0)
2

julia> foo(1)
3

Cela facilite le polymorphisme . Par exemple, nous pouvons facilement créer une liste chaînée en définissant deux types immuables, nommés Nil et Cons . Ces noms sont traditionnellement utilisés pour décrire une liste vide et une liste non vide, respectivement.

abstract LinkedList
immutable Nil <: LinkedList end
immutable Cons <: LinkedList
    first
    rest::LinkedList
end

Nous représenterons la liste vide par Nil() et par toute autre liste par Cons(first, rest) , où first est le premier élément de la liste liée et le rest est la liste chaînée composée de tous les éléments restants. Par exemple, la liste [1, 2, 3] sera représentée comme

julia> Cons(1, Cons(2, Cons(3, Nil())))
Cons(1,Cons(2,Cons(3,Nil())))

La liste est-elle vide?

Supposons que nous voulions étendre la fonction isempty la bibliothèque standard, qui fonctionne sur différentes collections:

julia> methods(isempty)
# 29 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394
...

Nous pouvons simplement utiliser la syntaxe de répartition de la fonction et définir deux méthodes supplémentaires d' isempty . Comme cette fonction provient du module Base , nous devons la qualifier de Base.isempty afin de l'étendre.

Base.isempty(::Nil) = true
Base.isempty(::Cons) = false

Ici, nous n'avons pas du tout besoin des valeurs d'argument pour déterminer si la liste est vide. Le seul type suffit pour calculer cette information. Julia nous permet d'omettre les noms des arguments, en gardant uniquement leur annotation de type, si nous n'avons pas besoin d'utiliser leurs valeurs.

Nous pouvons tester que nos méthodes isempty fonctionnent:

julia> using Base.Test

julia> @test isempty(Nil())
Test Passed
  Expression: isempty(Nil())

julia> @test !isempty(Cons(1, Cons(2, Cons(3, Nil()))))
Test Passed
  Expression: !(isempty(Cons(1,Cons(2,Cons(3,Nil())))))

et en effet le nombre de méthodes pour isempty a augmenté de 2 :

julia> methods(isempty)
# 31 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394

De toute évidence, déterminer si une liste chaînée est vide ou non est un exemple trivial. Mais cela mène à quelque chose de plus intéressant:

Combien de temps dure la liste?

La fonction length de la bibliothèque standard nous donne la longueur d'une collection ou de certaines itérations . Il existe plusieurs façons de mettre en œuvre une length pour une liste chaînée. En particulier, en utilisant un while boucle est probablement le plus rapide et le plus efficace mémoire de Julia. Mais l' optimisation prématurée doit être évitée, alors supposons que notre liste chaînée ne soit pas efficace. Quelle est la manière la plus simple d'écrire une fonction de length ?

Base.length(::Nil) = 0
Base.length(xs::Cons) = 1 + length(xs.rest)

La première définition est simple: une liste vide a une longueur de 0 . La deuxième définition est également facile à lire: pour compter la longueur d'une liste, nous comptons le premier élément, puis comptons la longueur du reste de la liste. Nous pouvons tester cette méthode de la même manière que nous avons testé isempty :

julia> @test length(Nil()) == 0
Test Passed
  Expression: length(Nil()) == 0
   Evaluated: 0 == 0

julia> @test length(Cons(1, Cons(2, Cons(3, Nil())))) == 3
Test Passed
  Expression: length(Cons(1,Cons(2,Cons(3,Nil())))) == 3
   Evaluated: 3 == 3

Prochaines étapes

Cet exemple de jouet est loin d'implémenter toutes les fonctionnalités souhaitées dans une liste chaînée. Il manque, par exemple, l'interface d'itération. Cependant, il illustre comment la répartition peut être utilisée pour écrire un code court et clair.

Types immuables

Le type composite le plus simple est un type immuable. Les instances de types immuables, comme les tuples , sont des valeurs. Leurs champs ne peuvent pas être modifiés après leur création. À bien des égards, un type immuable est comme un Tuple avec des noms pour le type lui-même et pour chaque champ.

Types singleton

Les types composites, par définition, contiennent un certain nombre de types plus simples. En Julia, ce nombre peut être zéro; c'est-à-dire qu'un type immuable est autorisé à ne contenir aucun champ. Ceci est comparable au tuple vide () .

Pourquoi cela pourrait-il être utile? Ces types immuables sont connus sous le nom de "types singleton", car une seule d'entre eux pourrait exister. Les valeurs de ces types sont appelées "valeurs singleton". La bibliothèque standard Base contient de nombreux types singleton. Voici une brève liste:

  • Void , le type de nothing . Nous pouvons vérifier que Void.instance (qui est une syntaxe spéciale pour récupérer la valeur singleton d'un type singleton) n'est en effet nothing .
  • Tout type de média, tel que MIME"text/plain" , est un type singleton avec une seule instance, MIME("text/plain") .
  • Les types Irrational{:π} , Irrational{:e} , Irrational{:φ} et similaires sont des types singleton, et leurs instances singleton sont les valeurs irrationnelles π = 3.1415926535897... , etc.
  • Les traits de taille de l'itérateur Base.HasLength , Base.HasShape , Base.IsInfinite et Base.SizeUnknown sont tous des types singleton.
0.5.0
  • Dans la version 0.5 et les versions ultérieures, chaque fonction est une instance singleton d'un type singleton! Comme toute autre valeur de singleton, nous pouvons récupérer la fonction sin , par exemple, de typeof(sin).instance .

Comme ils ne contiennent rien, les types singleton sont incroyablement légers et ils peuvent souvent être optimisés par le compilateur pour ne pas avoir de surcharge d'exécution. Ainsi, ils sont parfaits pour les traits, les valeurs d'étiquettes spéciales et pour des choses comme les fonctions que l'on aimerait se spécialiser.

Pour définir un type de singleton,

julia> immutable MySingleton end

Pour définir une impression personnalisée pour le type singleton,

julia> Base.show(io::IO, ::MySingleton) = print(io, "sing")

Pour accéder à l'instance singleton,

julia> MySingleton.instance
MySingleton()

Souvent, on assigne ceci à une constante:

julia> const sing = MySingleton.instance
MySingleton()

Types d'emballage

Si les types immuables du champ zéro sont intéressants et utiles, alors les types immuables à un champ sont peut-être encore plus utiles. De tels types sont communément appelés "types de wrappers" car ils encapsulent certaines données sous-jacentes, fournissant une interface alternative auxdites données. Un exemple de type de wrapper dans Base est String . Nous allons définir un type similaire à String , nommé MyString . Ce type sera soutenu par un vecteur ( tableau à une dimension) d'octets ( UInt8 ).

Tout d'abord, la définition de type elle-même et certains éléments personnalisés:

immutable MyString <: AbstractString
    data::Vector{UInt8}
end

function Base.show(io::IO, s::MyString)
    print(io, "MyString: ")
    write(io, s.data)
    return
end

Maintenant, notre type MyString est prêt à être utilisé! Nous pouvons lui fournir des données UTF-8 brutes, et cela s’affiche comme nous l’aimons pour:

julia> MyString([0x48,0x65,0x6c,0x6c,0x6f,0x2c,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21])
MyString: Hello, World!

Evidemment, ce type de chaîne nécessite beaucoup de travail avant de devenir aussi utilisable que le type Base.String .

Véritables types composites

Le plus souvent, de nombreux types immuables contiennent plus d'un champ. Un exemple est le type de bibliothèque standard Rational{T} , qui contient deux champs: un champ num pour le numérateur et un champ den pour le dénominateur. Il est assez simple d'émuler ce type de conception:

immutable MyRational{T}
    num::T
    den::T
    MyRational(n, d) = (g = gcd(n, d); new(n÷g, d÷g))
end
MyRational{T}(n::T, d::T) = MyRational{T}(n, d)

Nous avons implémenté avec succès un constructeur qui simplifie nos nombres rationnels:

julia> MyRational(10, 6)
MyRational{Int64}(5,3)


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow