Expressions
Expressions multilignes
Les expressions peuvent s'étendre sur plusieurs lignes en utilisant des parenthèses. Les newlines sont ignorés à l'intérieur des parenthèses, permettant de formater le code de manière lisible.
Les parenthèses créent une zone de micro-apesanteur syntaxique où les newlines flottent librement sans jamais toucher le sol du parser. Un espace où les expressions longues se détendent naturellement.
Expressions arithmétiques
# Expression multiligne avec parenthèses
resultat = (
10 + 20 +
30 + 40 +
50
) # 150
# Calcul complexe
moyenne = (
valeur1 + valeur2 + valeur3
+ valeur4 + valeur5
) / 5
# Avec indentation libre
total = (
prix_ht
* quantite
* (1 + tva)
)
Appels de fonction
Les arguments de fonction peuvent être répartis sur plusieurs lignes. Note : le premier argument doit être sur la
même ligne que (, les arguments suivants peuvent être sur des lignes séparées.
# Appel multiligne
resultat = fonction_complexe(argument1,
argument2,
argument3,
argument4)
# Avec arguments nommés
configurer(host="localhost",
port=8080,
debug=True,
timeout=30)
# Mélange positionnel et nommé
creer_serveur("192.168.1.1",
3000,
ssl=True,
workers=4)
Lambdas multilignes
Les paramètres de lambda peuvent aussi s'étendre sur plusieurs lignes. Note : le premier paramètre doit être sur la
même ligne que (.
# Paramètres multiligne
transformer = (x,
y,
z,
scale=1.0) => {
x * scale + y * scale + z * scale
}
# Lambda variadique
combiner = (prefix,
*items) => {
result = list(prefix)
for item in items {
result = result + list(item)
}
result
}
Expressions conditionnelles
Les conditions peuvent être formatées sur plusieurs lignes :
# Condition complexe
if (
age >= 18
and permis == True
and experience > 2
) {
print("Peut conduire")
}
# Expression booléenne longue
valide = (
x > 0
and x < 100
and y > 0
and y < 100
and z > 0
)
Cas d'usage
Sans parenthèses (erreur de parsing) :
# ✗ ERREUR : newline termine l'expression
x = 1 + 2 +
3 + 4 # Parsing error!
Avec parenthèses (valide) :
# ✓ OK : newlines ignorés dans les parenthèses
x = (1 + 2 +
3 + 4) # 10
Note : Les collections (list(), dict(), etc.) et les blocs {} supportent déjà les newlines naturellement sans
nécessiter de parenthèses supplémentaires.
Opérateurs
Opérateurs arithmétiques
# Addition, soustraction, multiplication, division
resultat = 10 + 5 - 2 * 3 / 4
# Division entière et modulo
quotient = 17 // 5 # 3
reste = 17 % 5 # 2
# Exponentiation
puissance = 2 ** 10 # 1024
# Opérateurs unaires
negatif = -42
positif = +42
Opérateurs de comparaison
# Égalité et inégalité
egal = 5 == 5 # True
different = 5 != 3 # True
# Comparaisons
plus_petit = 3 < 5 # True
plus_grand = 10 > 2 # True
inf_egal = 5 <= 5 # True
sup_egal = 10 >= 5 # True
# Comparaisons en chaîne
dans_intervalle = 1 < x < 10
Opérateurs logiques
# AND, OR, NOT
condition1 = True and False # False
condition2 = True or False # True
condition3 = not True # False
# Court-circuit
resultat = x > 0 and y / x > 2
Opérateurs binaires
# AND, OR, XOR binaire
masque = 0xFF & 0x0F # 0x0F
union = 0xF0 | 0x0F # 0xFF
exclusif = 0xFF ^ 0x0F # 0xF0
# Inversion binaire
inverse = ~0xFF
Accès aux attributs et chaînage
Catnip supporte l'accès aux attributs et le chaînage de méthodes :
# Accès à un attribut
valeur = objet.attribut
# Appel de méthode
resultat = objet.methode()
# Appel avec arguments
resultat = objet.methode(arg1, arg2)
# Chaînage
resultat = objet.methode1().methode2().attribut
# Chaînage complexe
valeur = objet.get_data().process(param).to_string()
Indexation et Slicing
Catnip supporte l'indexation et le slicing avec la syntaxe […], compatible avec Python.
Indexation simple
# Accès par index (listes)
nombres = list(10, 20, 30, 40, 50)
premier = nombres[0] # 10
troisieme = nombres[2] # 30
dernier = nombres[4] # 50
# Avec variable ou expression
idx = 3
valeur = nombres[idx] # 40
autre = nombres[1 + 1] # 30
# Dictionnaires
personne = dict(nom="Alice", age=30)
nom = personne["nom"] # "Alice"
age = personne["age"] # 30
# Chaînes de caractères
texte = "bonjour"
premiere_lettre = texte[0] # "b"
Slicing (extraction de sous-séquences)
Le slicing utilise la syntaxe [start:stop:step] où tous les paramètres sont optionnels.
Slicing basique
# start:stop - du start (inclus) au stop (exclu)
nombres = list(10, 20, 30, 40, 50)
sous_liste = nombres[1:4] # [20, 30, 40]
# start: - du start jusqu'à la fin
fin = nombres[2:] # [30, 40, 50]
# :stop - du début jusqu'à stop
debut = nombres[:3] # [10, 20, 30]
# : - copie complète
copie = nombres[:] # [10, 20, 30, 40, 50]
Slicing avec pas (step)
# ::step - tous les step éléments
chiffres = list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
pairs = chiffres[::2] # [0, 2, 4, 6, 8]
impairs = chiffres[1::2] # [1, 3, 5, 7, 9]
# start:stop:step - syntaxe complète
nombres = list(0, 10, 20, 30, 40, 50, 60, 70, 80, 90)
extrait = nombres[1:8:2] # [10, 30, 50, 70]
Indices négatifs
Les indices négatifs comptent depuis la fin (-1 = dernier élément).
liste = list(1, 2, 3, 4, 5, 6, 7, 8, 9)
# Accès négatif
dernier = liste[-1] # 9
avant_dernier = liste[-2] # 8
# Slicing avec indices négatifs
fin = liste[-3:] # [7, 8, 9]
milieu = liste[-5:-2] # [5, 6, 7]
sans_dernier = liste[:-1] # [1, 2, 3, 4, 5, 6, 7, 8]
Inversion et pas négatif
Un pas négatif parcourt la séquence en sens inverse.
nombres = list(1, 2, 3, 4, 5)
# Inversion complète
inverse = nombres[::-1] # [5, 4, 3, 2, 1]
# Un élément sur deux en partant de la fin
extrait = nombres[::-2] # [5, 3, 1]
# Chaînes inversées
texte = "bonjour"
reverse = texte[::-1] # "ruojnob"
Slicing sur chaînes
Le slicing fonctionne également sur les chaînes de caractères.
message = "hello world"
# Extraction
debut = message[0:5] # "hello"
fin = message[6:] # "world"
un_sur_deux = message[::2] # "hlowrd"
# Palindrome check
mot = "kayak"
est_palindrome = mot == mot[::-1] # True
Notation .[:] (fullslice)
La syntaxe .[start:stop:step] permet le slicing avec notation explicite à point, utile après des expressions ou pour
chaîner avec d'autres opérations.
# Équivalent à [:]
data = list(1, 2, 3, 4, 5, 6)
data.[:] # [1, 2, 3, 4, 5, 6]
# Sur expressions
(list(1, 2, 3) + list(4, 5, 6)).[1:5] # [2, 3, 4, 5]
# Chaînage avec broadcast
list(10, 20, 30, 40, 50).[:3].[* 2] # [20, 40, 60]
La notation
.[:]fonctionne exactement comme[:]mais avec la syntaxe membre. Elle s'avère particulièrement pratique quand l'objet source est une expression complexe ou quand on souhaite chaîner plusieurs opérations de manière fluide.
Cas particuliers
# Slice hors bornes (pas d'erreur, liste vide)
liste = list(1, 2, 3)
vide = liste[10:20] # []
aussi_vide = liste[5:] # []
# Step de 0 lève une erreur
# liste[::0] # ValueError!
# Slicing d'une liste imbriquée
matrix = list(list(1, 2, 3), list(4, 5, 6), list(7, 8, 9))
premiere_ligne = matrix[0] # [1, 2, 3]
sous_matrice = matrix[0:2] # [[1, 2, 3], [4, 5, 6]]
Indexation et slicing imbriqués
# Accès dans une liste de listes
matrix = list(list(1, 2, 3), list(4, 5, 6), list(7, 8, 9))
element = matrix[1][2] # 6 (2ème ligne, 3ème colonne)
# Slicing puis indexation
liste = list(10, 20, 30, 40, 50)
sous = liste[1:4] # [20, 30, 40]
element = sous[1] # 30
# Équivalent en une ligne
element = liste[1:4][1] # 30
Exemple pratique : recherche dichotomique
binary_search = (liste, cible, gauche=0, droite=None) => {
if droite == None { droite = len(liste) - 1 }
if gauche > droite {
-1
} else {
milieu = (gauche + droite) // 2
valeur = liste[milieu] # Indexation
if valeur == cible {
milieu
} elif valeur < cible {
binary_search(liste, cible, milieu + 1, droite)
} else {
binary_search(liste, cible, gauche, milieu - 1)
}
}
}
liste_triee = list(1, 3, 5, 7, 9, 11, 13, 15)
index = binary_search(liste_triee, 7) # 3