221 lines
8.7 KiB
Markdown
221 lines
8.7 KiB
Markdown
# 22. Collections et itération
|
|
|
|
## 22.1 `Array<T>` — V1 REQUIS — DIRECTION FIGÉE
|
|
|
|
`Array<T>` est une séquence contiguë dont la taille est fixée lors de la création et n'est pas redimensionnable.
|
|
|
|
## 22.2 `StaticArray<T,N>` — V1 REQUIS — DIRECTION FIGÉE
|
|
|
|
`StaticArray<T,N>` est un tableau à taille connue à la compilation.
|
|
|
|
Le mécanisme exact de const generics nécessaire à `N` doit être défini avec les generics.
|
|
|
|
## 22.3 Collection redimensionnable — V1 REQUIS — À FINALISER
|
|
|
|
Un type du type :
|
|
|
|
```text
|
|
Vector<T>
|
|
```
|
|
|
|
est envisagé pour les séquences redimensionnables, mais l'API et le nom final doivent être figés dans le SDK V1.
|
|
|
|
## 22.4 `Iterable<T>` / `Iterator<T>` — V1 REQUIS — FIGÉ
|
|
|
|
Interfaces Core reconnues par `foreach`.
|
|
|
|
Elles ne portent pas le préfixe `Op`, car `foreach` est une construction du langage et non un opérateur symbolique.
|
|
|
|
Indexabilité et itérabilité sont indépendantes.
|
|
|
|
## 22.5 `Range<T>` — V1 REQUIS — FIGÉ EN PRINCIPE
|
|
|
|
`Range<T>` est une vraie valeur Saselang immuable représentant un intervalle ordonné. Il ne représente ni un pas ni une progression d'itération.
|
|
|
|
Les quatre syntaxes de bornes sont :
|
|
|
|
```text
|
|
a..b [a, b] bornes basse et haute incluses
|
|
a>..b (a, b] borne basse exclue, borne haute incluse
|
|
a..<b [a, b) borne basse incluse, borne haute exclue
|
|
a>..<b (a, b) bornes basse et haute exclues
|
|
```
|
|
|
|
Les deux bornes doivent avoir exactement le même type `T`. Saselang ne possède pas de `Range<L,U>` et ne recherche aucun type commun implicite entre deux bornes déjà typées différemment.
|
|
|
|
Un littéral non encore typé peut toutefois être contextualisé normalement :
|
|
|
|
```text
|
|
Range<uint64> ids = 1..100;
|
|
```
|
|
|
|
### 22.5.1 Domaine ordonné
|
|
|
|
`Range<T>` est disponible lorsque les valeurs admissibles utilisées comme bornes appartiennent à un domaine totalement ordonné selon le contrat de comparaison retenu par Saselang.
|
|
|
|
La règle est fondée sur la capacité du type, pas sur une whitelist fermée. Elle peut donc s'appliquer à des entiers, `char`, des enums ou structs explicitement ordonnés, et à des types Core/utilisateur satisfaisant le contrat requis.
|
|
|
|
Les enums n'obtiennent pas automatiquement un ordre à partir de leur ordre de déclaration : ils doivent fournir explicitement la capacité d'ordre total requise.
|
|
|
|
Les floats sont autorisés comme bornes de `Range<floatN>` uniquement pour leur sous-domaine fini :
|
|
|
|
```text
|
|
NaN interdit comme borne
|
|
+Infinity interdit comme borne
|
|
-Infinity interdit comme borne
|
|
valeur finie autorisée
|
|
```
|
|
|
|
`-0.0` et `+0.0` occupent la même position dans l'ordre numérique d'un range, même si leur représentation binaire reste distincte et observable par les mécanismes bas niveau appropriés.
|
|
|
|
`NaN` et les infinities n'appartiennent à aucun `Range<floatN>` défini avec ces règles ; `contains()` retourne donc `false` pour ces valeurs.
|
|
|
|
### 22.5.2 Construction et ranges vides
|
|
|
|
Les bornes sont évaluées de gauche à droite, exactement une fois chacune, avant la construction du range.
|
|
|
|
Une borne statiquement prouvée invalide provoque une erreur de compilation. Une borne calculée dynamiquement mais invalide selon le contrat de `Range<T>` provoque un runtime fault lors de la construction directe. Une API Core explicite retournant `Result` pourra être fournie lorsqu'une validation récupérable est souhaitée.
|
|
|
|
`lower > upper` ne constitue pas une erreur : le résultat est un range vide.
|
|
|
|
Lorsque les bornes sont égales :
|
|
|
|
```text
|
|
a..a contient exactement a
|
|
a>..a vide
|
|
a..<a vide
|
|
a>..<a vide
|
|
```
|
|
|
|
Un range vide est une valeur valide. En revanche, un range statiquement vide utilisé comme pattern de `match` est interdit parce que la branche serait inatteignable.
|
|
|
|
Les ranges ouverts/infinis tels que `..b`, `a..` ou `..` ne font pas partie de V1.
|
|
|
|
### 22.5.3 API fondamentale
|
|
|
|
`Range<T>` doit exposer au minimum les capacités conceptuelles suivantes, dont les noms sont retenus sauf raison ultérieure forte de les changer :
|
|
|
|
```text
|
|
range::lower()
|
|
range::upper()
|
|
range::includesLower()
|
|
range::includesUpper()
|
|
range::isEmpty()
|
|
range::contains(value)
|
|
```
|
|
|
|
Le range est immuable : modifier une borne signifie construire un autre `Range<T>`.
|
|
|
|
Le `match` utilisant un range-pattern emploie la même sémantique d'appartenance sans invoquer un prédicat utilisateur arbitraire.
|
|
|
|
## 22.6 Progression et itération des ranges — V1 REQUIS — FIGÉ EN PRINCIPE
|
|
|
|
L'existence d'un `Range<T>`, son itérabilité et sa progression sont trois concepts distincts :
|
|
|
|
```text
|
|
Range<T> intervalle ordonné
|
|
RangeIterable<T> parcours canonique vers l'avant
|
|
RangeStep<T,Step> parcours avant avec pas explicite
|
|
RangeReverse<T> parcours canonique vers l'arrière
|
|
RangeReverseStep<T,Step> parcours arrière avec pas explicite
|
|
RangeProgression<T,Step> valeur de progression produite
|
|
```
|
|
|
|
Ces noms sont retenus comme noms Core significatifs ; leur namespace exact sera décidé lors de l'organisation générale de Core, probablement dans une famille dédiée aux ranges.
|
|
|
|
Un type n'est pas obligé de fournir toutes ces capacités. Un range peut être valide sans être itérable ; il peut être itérable vers l'avant sans supporter `reverse()` ou `step()`.
|
|
|
|
`foreach` continue à consommer `Iterable<T>` : un `Range<T>` ou une `RangeProgression<T,Step>` n'est utilisable par `foreach` que lorsque les capacités Core concernées fournissent cette itérabilité.
|
|
|
|
### 22.6.1 Progression canonique
|
|
|
|
Lorsqu'un type fournit `RangeIterable<T>`, le parcours direct d'un range utilise sa progression canonique. Celle-ci n'est pas définie génériquement comme un appel caché à `step(1)`.
|
|
|
|
Pour les entiers Core, la progression canonique avance naturellement d'une unité. Pour `char`, elle avance parmi les Unicode scalar values valides et saute les valeurs surrogate `U+D800..U+DFFF`, qui ne sont pas des `char` Saselang valides.
|
|
|
|
Un `Range<floatN>` est un intervalle valide lorsque ses bornes sont finies, mais il n'est pas automatiquement itérable et ne reçoit pas automatiquement une capacité de `step` flottant.
|
|
|
|
### 22.6.2 `step(...)`
|
|
|
|
Le pas ne fait jamais partie du `Range`. La syntaxe de parcours explicite est :
|
|
|
|
```text
|
|
(range)::step(step)
|
|
```
|
|
|
|
Elle produit une progression itérable et ne modifie pas le `Range` d'origine.
|
|
|
|
La compatibilité entre le type des bornes `T` et le type du pas `Step` est définie explicitement par `RangeStep<T,Step>` ou `RangeReverseStep<T,Step>`. Elle n'est pas déduite automatiquement d'une conversion numérique générale.
|
|
|
|
Cela permet notamment de définir des couples adaptés au domaine :
|
|
|
|
```text
|
|
RangeStep<int32,int32>
|
|
RangeStep<int32,uint32>
|
|
RangeStep<char,uint32>
|
|
RangeStep<Date,Duration>
|
|
```
|
|
|
|
et de refuser des couples qui introduiraient une perte ou n'auraient pas de sens. Par exemple, un range `float16` peut recevoir seulement les types de pas explicitement supportés sans imposer qu'un `float32` plus large soit accepté.
|
|
|
|
Le pas exprime une magnitude de progression, pas une direction. Pour les types numériques Core, il doit être strictement supérieur à zéro. Un pas statiquement nul ou négatif est une erreur de compilation ; une valeur dynamique invalide provoque un runtime fault lors de la construction directe de la progression. Une API explicite en `Result` pourra exister pour une validation récupérable.
|
|
|
|
Pour les types utilisateur, la validité du pas est définie par le contrat Range correspondant et n'est pas réduite artificiellement à une comparaison numérique avec zéro.
|
|
|
|
### 22.6.3 `reverse()`
|
|
|
|
La direction inverse est explicite :
|
|
|
|
```text
|
|
(range)::reverse()
|
|
(range)::reverse()::step(step)
|
|
```
|
|
|
|
`reverse()` ne construit pas un range aux bornes inversées. Le `Range` reste identique ; seule la direction de parcours change.
|
|
|
|
La forme canonique compose la direction avant le pas :
|
|
|
|
```text
|
|
range
|
|
[::reverse()]
|
|
[::step(step)]
|
|
```
|
|
|
|
Une progression déjà matérialisée par `step()` n'est pas requise de fournir à son tour `reverse()`. Cela évite plusieurs chaînes d'appels synonymes pour le même parcours.
|
|
|
|
L'inclusion ou l'exclusion des bornes reste une propriété du range et ne change jamais avec la direction.
|
|
|
|
### 22.6.4 Arrêt, overflow et pas non aligné
|
|
|
|
Une progression s'arrête avant de produire une valeur hors du range. Elle ne doit jamais provoquer un overflow uniquement pour constater qu'elle a atteint la fin.
|
|
|
|
Par exemple :
|
|
|
|
```text
|
|
(250u8..255u8)::step(2u8)
|
|
```
|
|
|
|
produit conceptuellement :
|
|
|
|
```text
|
|
250 252 254
|
|
```
|
|
|
|
sans tenter de former `256`.
|
|
|
|
De même :
|
|
|
|
```text
|
|
(1..10)::step(4)
|
|
```
|
|
|
|
produit :
|
|
|
|
```text
|
|
1 5 9
|
|
```
|
|
|
|
La borne finale n'est pas ajoutée artificiellement si elle ne tombe pas naturellement sur la progression.
|
|
|
|
Un range vide produit simplement une progression vide.
|