web-dev-qa-db-fra.com

Est-il toujours préférable d'écrire une fonction pour tout ce qui doit être répété deux fois?

Moi-même, j'ai hâte d'écrire une fonction quand j'ai besoin de faire plus de deux fois. Mais quand il s'agit de choses qui n'apparaissent que deux fois, c'est un peu plus délicat.

Pour le code qui nécessite plus de deux lignes, j'écrirai une fonction. Mais face à des choses comme:

print "Hi, Tom"
print "Hi, Mary"

J'hésite à écrire:

def greeting(name):
    print "Hi, " + name

greeting('Tom')
greeting('Mary')

Le second semble trop, non?


Mais que faire si nous avons:

for name in vip_list:
    print name
for name in guest_list:
    print name

Et voici l'alternative:

def print_name(name_list):
    for name in name_list:
        print name

print_name(vip_list)
print_name(guest_list)

Les choses deviennent délicates, non? C'est difficile de décider maintenant.

Quelle est votre opinion à ce sujet?

132
Zen

Bien que ce soit un facteur pour décider de séparer une fonction, le nombre de fois que quelque chose est répété ne devrait pas être le seul facteur. Il est souvent logique de créer une fonction pour quelque chose qui n'est exécuté que ne fois. En règle générale, vous souhaitez diviser une fonction lorsque:

  • Il simplifie chaque couche d'abstraction individuelle.
  • Vous avez de bons noms significatifs pour les fonctions de séparation, vous n'avez donc généralement pas besoin de sauter entre les couches d'abstraction pour comprendre ce qui se passe.

Vos exemples ne répondent pas à ces critères. Vous passez d'un one-liner à un one-liner, et les noms ne vous rapportent vraiment rien en termes de clarté. Cela dit, les fonctions aussi simples sont rares en dehors des tutoriels et des travaux scolaires. La plupart des programmeurs ont tendance à trop se tromper dans l'autre sens.

201
Karl Bielefeldt

Uniquement si la duplication est intentionnelle plutôt que accidentelle .

Ou, autrement dit:

Seulement si vous ( vous attendez à ce qu'ils évoluent ensemble à l'avenir.


Voici pourquoi:

Parfois, deux morceaux de code ( se produisent pour devenir identiques même s'ils n'ont rien à voir l'un avec l'autre). Dans ce cas, vous ( devez résister à l'envie de les combiner, car la prochaine fois que quelqu'un effectuera la maintenance de l'un d'eux, personne ne s'attendra pas à ce que les modifications se propagent à un appelant qui n'existait pas auparavant, et cette fonction peut se casser en conséquence. Par conséquent, vous ne devez factoriser le code que lorsque cela a du sens, pas chaque fois qu'il semble réduire la taille du code.

Règle générale:

Si le code ne renvoie que de nouvelles données et ne modifie pas les données existantes ou n'a pas d'autres effets secondaires, il est très probable qu'il soit sûr de factoriser en tant que fonction distincte. (Je ne peux imaginer aucun scénario dans lequel cela provoquerait une rupture sans changer complètement la sémantique prévue de la fonction, auquel cas le nom ou la signature de la fonction devrait également changer, et vous devrez de toute façon être prudent dans ce cas. )

104
user541686

Non, ce n'est pas toujours une meilleure pratique.

Toutes autres choses étant égales, le code linéaire, ligne par ligne, est plus facile à lire que de sauter les appels de fonction. Un appel de fonction non trivial prend toujours des paramètres, vous devez donc trier tout cela et effectuer des sauts contextuels mentaux d'un appel de fonction à un appel de fonction. Privilégiez toujours une meilleure clarté du code, sauf si vous avez une bonne raison d'être obscur (comme obtenir les améliorations de performances nécessaires).

Alors pourquoi le code est-il refactorisé dans des méthodes distinctes? Pour améliorer la modularité. Pour collecter fonctionnalité significative derrière une méthode individuelle et lui donner un nom significatif. Si vous n'accomplissez pas cela, vous n'avez pas besoin de ces méthodes distinctes.

60
Robert Harvey

Dans votre exemple particulier, créer une fonction peut sembler excessif; au lieu de cela, je poserais la question: est-il possible que cette salutation particulière change à l'avenir? Comment?

Les fonctions ne sont pas simplement utilisées pour conclure des fonctionnalités et faciliter la réutilisation, mais aussi pour faciliter la modification. Si les exigences changent, le code copié-collé devra être recherché et modifié manuellement, tandis qu'avec une fonction, le code ne doit être modifié qu'une seule fois.

Votre exemple en bénéficierait non pas via une fonction - puisque la logique est quasiment inexistante - mais peut-être un GREET_OPENING constant. Cette constante pourrait ensuite être chargée à partir d'un fichier afin que votre programme soit facilement adaptable à différentes langues, par exemple. Notez qu'il s'agit d'une solution brute, un programme plus complexe aurait probablement besoin d'un moyen plus raffiné pour suivre i18n, mais encore une fois cela dépend des exigences par rapport au travail à faire.

Il s'agit finalement d'exigences possibles et de planifier les choses à l'avance pour faciliter le travail de votre futur.

25
Svalorzen

Personnellement, j'ai adopté la règle des trois - que je justifierai en appelant YAGNI . Si je dois faire quelque chose une fois, j'écrirai ce code, deux fois je pourrai simplement copier/coller (oui, je viens d'admettre de copier/coller!) Parce que je n'en aurai plus besoin, mais si je dois faire de même encore une fois, je vais refactoriser et extraire ce morceau dans sa propre méthode, je me suis démontré que je en aurais besoin.

Je suis d'accord avec ce que Karl Bielefeldt et Robert Harvey ont dit et mon interprétation de ce qu'ils disent est que la règle primordiale est la lisibilité. Si cela facilite la lecture de mon code, envisagez de créer une fonction, gardez à l'esprit des choses comme DRY et SLAP . Si je peux éviter d'avoir un niveau d'abstraction variable dans ma fonction, je trouve cela plus facile à gérer dans ma tête, donc ne pas sauter entre les fonctions (si je ne peux pas comprendre ce que fait la fonction simplement en lisant son nom) signifie moins de commutateurs dans mon processus mentaux.
De même, ne pas avoir à changer de contexte entre les fonctions et le code en ligne tel que print "Hi, Tom" Fonctionne pour moi, dans ce cas, je peut-être extraire une fonction PrintNames() ssi le reste de ma fonction globale était principalement des appels de fonction.

22
Simon Martin

C'est rarement clair, vous devez donc peser les options:

  • Date limite (correction de la gravure de la salle des serveurs dès que possible)
  • Lisibilité du code (peut influencer le choix de toute façon)
  • Niveau d'abstraction de la logique partagée (lié à ce qui précède)
  • Exigence de réutilisation (c'est-à-dire que la même logique exacte est importante ou tout simplement pratique en ce moment)
  • Difficulté de partage (voici où python brille, voir ci-dessous)

En C++, j'utilise généralement la règle des trois (c'est-à-dire la troisième fois que j'ai besoin de la même chose, je la refactorise en une pièce correctement partageable), mais avec l'expérience, il est plus facile de faire ce choix au départ car vous en savez plus sur la portée, logiciel et domaine avec lesquels vous travaillez.

Cependant, en Python il est assez léger de réutiliser, plutôt que de répéter, la logique. Plus encore que dans beaucoup d'autres langages (au moins historiquement), IMO.

Envisagez donc de réutiliser la logique localement, par exemple en créant une liste à partir d'arguments locaux:

def foo():
    for name_list in (vip_list, guest_list): # can be list of tuples, for many args
        for name in name_list:
            print name

Vous pouvez utiliser un tuple de tuples et le diviser directement en boucle for, si vous avez besoin de plusieurs arguments:

def foo2():
    for header, name_list in (('vips': vip_list), ('people': guest_list)): 
        print header + ": "
        for name in name_list:
            print name

ou créer une fonction locale (peut-être que votre deuxième exemple est celui-ci), ce qui rend la logique de réutilisation explicite mais montre également clairement que print_name () n'est pas utilisé en dehors de la fonction:

def foo():
    def print_name(name_list):
        for name in name_list:
            print name

    print_name(vip_list)
    print_name(guest_list)

Les fonctions sont préférables, en particulier lorsque vous devez abandonner la logique au milieu (c'est-à-dire utiliser le retour), car les interruptions ou les exceptions peuvent encombrer les choses inutilement.

L'une ou l'autre est supérieure à la répétition de la même logique, IMO, et aussi moins encombrante que la déclaration d'une fonction globale/classe qui n'est utilisée que par un seul appelant (bien que deux fois).

13
Macke

Toutes les bonnes pratiques ont une raison particulière à laquelle vous pouvez vous référer pour répondre à une telle question. Vous devez toujours éviter les ondulations, lorsqu'un changement futur potentiel implique également des modifications d'autres composants.

Donc, dans votre exemple: si vous supposez que vous avez un message d'accueil standard et que vous voulez vous assurer qu'il est le même pour tous, écrivez

def std_greeting(name):
    print "Hi, " + name

for name in ["Tom", "Mary"]:
    std_greeting(name)   # even the function call should be written only once

Sinon, vous devrez faire attention et modifier le message d'accueil standard à deux endroits, si cela se produit.

Si, cependant, le "Salut" est le même juste par accident et que le changement de l'une des salutations n'entraîne pas nécessairement des changements pour l'autre, alors gardez-les séparés. Par conséquent, gardez-les séparés s'il y a des raisons logiques de croire que le changement suivant est plus probable:

print "Hi, Tom"
print "Hello, Mary"

Pour votre deuxième partie, décider combien de "package" en fonctions dépend probablement de deux facteurs:

  • garder les blocs petits pour la lisibilité
  • garder les blocs suffisamment grands pour que les changements ne se produisent que dans quelques blocs. Pas besoin de trop regrouper car il est difficile de suivre le modèle d'appel.

Idéalement, lorsqu'un changement se produit, vous penserez "Je dois changer la plupart des blocs suivants" et non "Je dois changer le code quelque part à l'intérieur d'un grand bloc".

9
Gerenuk

Je pense que vous devriez avoir une raison de créer une procédure. Il existe plusieurs raisons de créer une procédure. Ceux que je pense sont les plus importants sont:

  1. abstraction
  2. modularité
  3. navigation par code
  4. mettre à jour la cohérence
  5. récursivité
  6. essai

Abstraction

Une procédure peut être utilisée pour extraire un algorithme général des détails d'étapes particulières de l'algorithme. Si je peux trouver une abstraction suffisamment cohérente, cela m'aide à structurer ma façon de penser ce que fait l'algorithme. Par exemple, je peux concevoir un algorithme qui fonctionne sur des listes sans avoir à considérer nécessairement la représentation de la liste.

Modularité

Les procédures peuvent être utilisées pour organiser le code en modules. Les modules peuvent souvent être traités séparément. Par exemple, construit et testé séparément.

Un module bien conçu capture généralement une unité de fonctionnalité cohérente et significative. Par conséquent, ils peuvent souvent appartenir à différentes équipes, être complètement remplacés par une alternative ou réutilisés dans un contexte différent.

Navigation dans le code

Dans les grands systèmes, trouver le code associé à un comportement système particulier peut être difficile. L'organisation hiérarchique du code au sein des bibliothèques, modules et procédures peut aider à relever ce défi. Surtout si vous essayez a) d'organiser des fonctionnalités sous des noms significatifs et prévisibles b) de placer les procédures relatives à des fonctionnalités similaires l'une à côté de l'autre.

Mettre à jour la cohérence

Dans les grands systèmes, il peut être difficile de trouver tout le code qui doit être modifié pour obtenir un changement de comportement particulier. L'utilisation de procédures pour organiser les fonctionnalités du programme peut faciliter cela. En particulier, si chaque élément de fonctionnalité de votre programme n'apparaît qu'une seule fois et dans un groupe cohérent de procédures, il est moins probable (selon mon expérience) que vous manquiez un endroit que vous auriez dû mettre à jour ou effectuer une mise à jour incohérente.

Notez que vous devez organiser les procédures en fonction de la fonctionnalité et des abstractions du programme. Pas basé sur le fait que deux bits de code se trouvent être identiques pour le moment.

Récursivité

L'utilisation de la récursivité nécessite la création de procédures

Essai

Vous pouvez généralement tester les procédures indépendamment les unes des autres. Il est plus difficile de tester différentes parties du corps d'une procédure de manière indépendante, car vous devez généralement exécuter la première partie de la procédure avant la seconde. Cette observation s'applique également souvent à d'autres approches pour spécifier/vérifier le comportement des programmes.

Conclusion

Beaucoup de ces points concernent la compréhensibilité du programme. Vous pourriez dire que les procédures sont un moyen de créer un nouveau langage, spécifique à votre domaine, avec lequel organiser, écrire et lire sur les problèmes et les processus de votre domaine.

5
flamingpenguin

L'aspect le plus important des constantes et fonctions nommées n'est pas tant qu'elles réduisent la quantité de frappe, mais plutôt qu'elles "attachent" les différents endroits où elles sont utilisées. En ce qui concerne votre exemple, envisagez quatre scénarios:

  1. Il est nécessaire de modifier les deux salutations de la même manière, [par ex. à Bonjour, Tom et Bonjour, Mary].

  2. Il est nécessaire de changer un message d'accueil mais de laisser l'autre tel quel [par ex. Hi, Tom et Guten Tag, Mary].

  3. Il est nécessaire de changer les deux salutations différemment [par exemple Hello, Tom et Howdy, Mary].

  4. Aucun des deux messages d'accueil n'a besoin d'être changé.

S'il n'est jamais nécessaire de modifier l'un ou l'autre des messages d'accueil, peu importe l'approche à adopter. L'utilisation d'une fonction partagée aura pour effet naturel que la modification d'un message d'accueil les modifiera de la même manière. Si toutes les salutations devaient changer de la même manière, ce serait une bonne chose. S'ils ne le font pas, cependant, le message d'accueil de chaque personne devra être codé ou spécifié séparément; tout travail effectué en les faisant utiliser une fonction ou une spécification commune devra être annulé (et il aurait été préférable de ne pas le faire en premier lieu).

Certes, il n'est pas toujours possible de prédire l'avenir, mais si l'on a des raisons de croire qu'il est plus probable que les deux salutations devront changer ensemble, ce serait un facteur en faveur de l'utilisation d'un code commun (ou d'une constante nommée) ; si l'on a des raisons de croire qu'il est plus probable que l'un ou les deux doivent changer pour qu'ils diffèrent, ce serait un facteur contre l'utilisation d'un code commun.

5
supercat

Aucun des cas que vous donnez ne semble devoir être refactorisé.

Dans les deux cas, vous effectuez une opération qui s'exprime clairement et succinctement et il n'y a aucun risque de modification incohérente.

Je vous recommande de toujours chercher des moyens d'isoler le code où:

  1. Il existe une tâche identifiable et significative que vous pourriez séparer, et

  2. Soit

    une. la tâche est complexe à exprimer (en tenant compte des fonctions à votre disposition) o

    b. la tâche est effectuée plusieurs fois et vous avez besoin d'un moyen de vous assurer qu'elle est modifiée de la même manière aux deux endroits,

Si la condition 1 n'est pas remplie, vous ne trouverez pas la bonne interface pour le code: si vous essayez de le forcer, vous vous retrouverez avec des charges de paramètres, plusieurs choses que vous souhaitez retourner, beaucoup de logique contextuelle, et sera très probablement incapable de trouver un bon nom. Essayez de documenter l'interface à laquelle vous pensez en premier, c'est un bon moyen de tester l'hypothèse que c'est le bon regroupement de fonctionnalités et vient avec le bonus supplémentaire que lorsque vous écrivez votre code, il est déjà spécifié et documenté!

2a est possible sans 2b: j'ai parfois retiré du code du flux même quand je sais qu'il n'est utilisé qu'une seule fois, simplement parce que le déplacer ailleurs et le remplacer par une seule ligne signifie que le contexte dans lequel il est appelé est soudainement beaucoup plus lisible (surtout s'il s'agit d'un concept simple que le langage rend difficile à mettre en œuvre). Il est également clair lors de la lecture de la fonction extraite où la logique commence et se termine et ce qu'elle fait.

2b est possible sans 2a: l'astuce est d'avoir une idée du type de changement le plus probable. Est-il plus probable que la politique de votre entreprise passe de "Bonjour" partout à "Bonjour" tout le temps ou que votre message d'accueil change de ton si vous envoyez une demande de paiement ou des excuses pour une interruption de service? Parfois, cela peut être dû au fait que vous utilisez une bibliothèque externe dont vous n'êtes pas sûr et que vous souhaitez pouvoir l'échanger rapidement pour une autre: l'implémentation peut changer même si la fonctionnalité ne change pas.

Le plus souvent, cependant, vous aurez un mélange de 2a et 2b. Et vous devrez utiliser votre jugement, en tenant compte de la valeur commerciale du code, de la fréquence à laquelle il est utilisé, qu'il soit bien documenté et compris, de l'état de votre suite de tests, etc.

Si vous remarquez le même bit de logique utilisé plus d'une fois, vous devriez certainement en profiter pour envisager une refactorisation. Si vous commencez de nouvelles instructions avec plus de n niveaux d'indentation dans la plupart des langues, c'est un autre déclencheur légèrement moins important (choisissez une valeur de n avec laquelle vous êtes à l'aise avec votre langue: Python peut être 6, par exemple).

Tant que vous réfléchissez à ces choses et que vous éliminez les gros monstres à code spaghetti, tout devrait bien se passer - ne passez pas trop de temps à vous soucier de la finesse de votre refactoring sans avoir le temps pour des choses comme les tests ou la documentation élaborés. S'il le faut, le temps nous le dira.

4
user52889

En général, je suis d'accord avec Robert Harvey, mais je voulais ajouter un cas pour diviser une fonctionnalité en fonctions. Pour améliorer la lisibilité. Prenons un cas:

def doIt(smth,smthElse)
    for x in getDataFromSomething(smth,smthElse):
        if not check(x,smth):
            continue
        process(x,smthElse)
        store(x) 

Même si ces appels de fonction ne sont utilisés nulle part ailleurs, il y a un gain significatif de lisibilité si les 3 fonctions sont considérablement longues et ont des boucles imbriquées, etc.

3
UldisK

J'en suis venu à croire quelque chose au sujet de la refactorisation que je n'ai pas vu mentionné ici, je sais qu'il y a déjà beaucoup de réponses ici, mais je pense que c'est nouveau.

J'ai été un refactoriseur impitoyable et un fervent partisan de DRY depuis avant que les termes ne surviennent. C'est principalement parce que j'ai du mal à garder une grande base de code dans ma tête et en partie parce que j'aime DRY et je n'aime rien au codage C&P, en fait c'est douloureux et terriblement lent pour moi.

Le fait est qu'insister sur DRY m'a donné beaucoup de pratique dans certaines techniques que je vois rarement utiliser. Beaucoup de gens insistent pour que Java is difficile ou impossible de rendre SEC, mais vraiment ils n'essaient tout simplement pas.

Un exemple d'il y a longtemps qui est quelque peu similaire à votre exemple. Les gens ont tendance à penser Java La création d'une interface graphique est difficile. Bien sûr, c'est si vous codez comme ceci:

Menu m=new Menu("File");
MenuItem save=new MenuItem("Save")
save.addAction(saveAction); // I forget the syntax, but you get the idea
m.add(save);
MenuItem load=new MenuItem("Load")
load.addAction(loadAction)

Quiconque pense que c'est fou est tout à fait raison, mais ce n'est pas la faute de Java - le code ne devrait jamais être écrit de cette façon. Ces appels de méthode sont des fonctions destinées à être enveloppées dans d'autres systèmes. Si vous ne trouvez pas un tel système, construisez-le!

Vous ne pouvez évidemment pas coder comme ça, vous devez donc prendre du recul et regarder le problème, que fait ce code répété? Il s'agit de spécifier certaines chaînes et leur relation (un arbre) et de joindre les feuilles de cet arbre aux actions. Donc, ce que vous voulez vraiment, c'est dire:

class Menu {
    @MenuItem("File|Load")
    public void fileLoad(){...}
    @MenuItem("File|Save")
    public void fileSave(){...}
    @MenuItem("Edit|Copy")
    public void editCopy(){...}...

Une fois que vous avez défini votre relation d'une manière succincte et descriptive, vous écrivez une méthode pour y faire face - dans ce cas, vous parcourez les méthodes de la classe passée et créez un arbre, puis utilisez-le pour construire vos menus. et Actions et (évidemment) afficher un menu. Vous n'aurez pas de duplication, et votre méthode est réutilisable ... et probablement plus facile à écrire qu'un grand nombre de menus ne l'aurait été, et si vous aimez réellement la programmation, vous avez eu BEAUCOUP plus de plaisir. Ce n'est pas difficile - la méthode dont vous avez besoin pour écrire est probablement moins de lignes que la création de votre menu à la main ne le serait!

La chose est, pour bien faire cela, vous devez beaucoup pratiquer. Vous devez vous familiariser avec l'analyse exacte des informations uniques contenues dans les parties répétées, extraire ces informations et trouver comment les exprimer correctement. Apprendre à utiliser des outils comme l'analyse de chaînes et les annotations aide beaucoup. Il est également très important d'apprendre à être très clair sur les rapports d'erreurs et la documentation.

Vous obtenez une pratique "gratuite" simplement en codant bien - il y a de fortes chances qu'une fois que vous serez bon, vous constaterez que le codage de quelque chose DRY (y compris l'écriture d'un outil réutilisable) est plus rapide que la copie et coller et toutes les erreurs, les erreurs en double et les changements difficiles que ce type de codage provoque.

Je ne pense pas que je serais en mesure d'apprécier mon travail si je ne pratiquais pas DRY techniques et outils de construction autant que je le pouvais. Si je devais prendre une d'avoir à faire du copier-coller de programmation, je le prendrais.

Mes points sont donc:

  • Le copier-coller coûte plus de temps, sauf si vous ne savez pas bien refactoriser.
  • Vous apprenez à bien refacturer en le faisant - en insistant sur DRY même dans les cas les plus difficiles et les plus triviaux.
  • Vous êtes programmeur, si vous avez besoin d'un petit outil pour rendre votre code SEC, compilez-le.
2
Bill K

Il n'y a pas de règle stricte à appliquer, cela dépendra de la fonction réelle qui serait utilisée. Pour une simple impression de nom, je n'utiliserais pas de fonction, mais s'il s'agit d'une somme mathématique, ce serait une autre histoire. Vous créeriez alors une fonction même si elle n'est appelée que deux fois, afin de garantir que la somme mathématique est toujours la même si jamais elle est modifiée. Dans un autre exemple, si vous effectuez une forme de validation, vous utiliseriez une fonction, donc dans votre exemple, si vous deviez vérifier que le nom dépasse 5 caractères, vous utiliseriez une fonction pour vous assurer que les mêmes validations sont toujours effectuées.

Je pense donc que vous avez répondu à votre propre question lorsque vous avez déclaré: "Pour les codes qui nécessitent plus de deux lignes, je dois écrire un func". En général, vous utiliseriez une fonction, mais vous devez également utiliser votre propre logique pour déterminer s'il existe un type de valeur ajoutée provenant de l'utilisation d'une fonction.

2
W.Walford

Généralement, c'est une bonne idée de diviser quelque chose en une fonction qui améliore la lisibilité de votre code, ou, si la partie souvent répétée est en quelque sorte au cœur du système. Dans l'exemple ci-dessus, si vous aviez besoin d'accueillir vos utilisateurs en fonction des paramètres régionaux, il serait logique d'avoir une fonction d'accueil plus distincte.

1
user90766

En corollaire au commentaire de @ DocBrown à @RobertHarvey sur l'utilisation de fonctions pour créer des abstractions: si vous ne pouvez pas trouver un nom de fonction suffisamment informatif qui n'est pas significativement plus concis ou plus clair que le code derrière, vous n'abstrayez pas beaucoup . En l'absence de toute autre bonne raison d'en faire une fonction, il n'est pas nécessaire de le faire.

De plus, un nom de fonction capture rarement sa sémantique complète, vous devrez donc peut-être aller vérifier sa définition, s'il n'est pas assez courant pour être familier. En particulier dans un langage autre que fonctionnel, vous devrez peut-être savoir s'il a des effets secondaires, quelles erreurs peuvent survenir et comment elles sont traitées, sa complexité temporelle, s'il alloue et/ou libère des ressources, et s'il s'agit de threads. sûr.

Bien sûr, nous ne considérons par définition que des fonctions simples, mais cela va dans les deux sens - dans de tels cas, l'inline n'est pas susceptible d'ajouter de la complexité. De plus, le lecteur ne se rendra probablement pas compte qu'il s'agit d'une fonction simple tant qu'il n'aura pas regardé. Même avec un IDE qui vous relie à la définition, le saut visuel est un obstacle à la compréhension.

De manière générale, la factorisation récursive du code dans davantage de fonctions plus petites vous amènera finalement au point où les fonctions n'auront plus de signification indépendante, car au fur et à mesure que les fragments de code dont elles sont faites deviennent plus petits, ces fragments obtiennent plus de leur signification du contexte créé par le code environnant. Par analogie, pensez-y comme une image: si vous zoomez trop près, vous ne pouvez pas voir ce que vous regardez.

1
sdenham