C'est la façon la plus populaire (il me semble) de vérifier si une valeur est dans un tableau:
for (int x : array)
{
if (x == value)
return true;
}
return false;
Cependant, dans un livre que j'ai lu il y a de nombreuses années, probablement, Wirth ou Dijkstra, il a été dit que ce style est meilleur (par rapport à une boucle while avec une sortie à l'intérieur):
int i = 0;
while (i < array.length && array[i] != value)
i++;
return i < array.length;
De cette façon, la condition de sortie supplémentaire devient une partie explicite de l'invariant de la boucle, il n'y a pas de conditions cachées et sort à l'intérieur de la boucle, tout est plus évident et plus d'une manière de programmation structurée. J'ai généralement préféré ce dernier modèle chaque fois que possible et for
j'ai utilisé la boucle pour parcourir uniquement de a
à b
.
Et pourtant je ne peux pas dire que la première version soit moins claire. C'est peut-être encore plus clair et plus facile à comprendre, du moins pour les très débutants. Je me pose donc toujours la question de savoir laquelle est la meilleure?
Peut-être que quelqu'un peut donner une bonne justification en faveur d'une des méthodes?
Mise à jour: Il ne s'agit pas de points de retour de fonctions multiples, de lambdas ou de trouver un élément dans un tableau en soi. Il s'agit de savoir comment écrire des boucles avec des invariants plus complexes qu'une seule inégalité.
Mise à jour: OK, je vois l'intérêt des personnes qui répondent et commentent: j'ai mélangé la boucle foreach ici, qui elle-même est déjà beaucoup plus claire et lisible qu'une boucle while. Je n'aurais pas dû faire ça. Mais c'est aussi une question intéressante, alors laissons-la telle quelle: foreach-loop et une condition supplémentaire à l'intérieur, ou une boucle while avec un invariant de boucle explicite et une post-condition après. Il semble que la boucle foreach avec une condition et une sortie / pause soit gagnante. Je vais créer une question supplémentaire sans la boucle foreach (pour une liste chaînée).
la source
collection.contains(foo)
Réponses:
Je pense que pour les boucles simples, comme celles-ci, la première syntaxe standard est beaucoup plus claire. Certaines personnes considèrent que les retours multiples sont source de confusion ou d'une odeur de code, mais pour un morceau de code aussi petit, je ne pense pas que ce soit un vrai problème.
Cela devient un peu plus discutable pour les boucles plus complexes. Si le contenu de la boucle ne peut pas tenir sur votre écran et a plusieurs retours dans la boucle, il y a un argument à faire que les multiples points de sortie peuvent rendre le code plus difficile à maintenir. Par exemple, si vous deviez vous assurer qu'une méthode de maintenance d'état était exécutée avant de quitter la fonction, il serait facile de manquer de l'ajouter à l'une des instructions de retour et vous provoqueriez un bogue. Si toutes les conditions de fin peuvent être vérifiées dans une boucle while, vous n'avez qu'un seul point de sortie et pouvez ajouter ce code après.
Cela dit, avec des boucles en particulier, il est bon d'essayer de mettre autant de logique que possible dans des méthodes distinctes. Cela évite de nombreux cas où la deuxième méthode aurait des avantages. Les boucles Lean avec une logique clairement séparée auront plus d'importance que celui de ces styles que vous utilisez. De plus, si la plupart du code de base de votre application utilise un seul style, vous devez vous en tenir à ce style.
la source
C'est facile.
Rien n'est plus important que la clarté pour le lecteur. La première variante que j'ai trouvée incroyablement simple et claire.
La deuxième version «améliorée», j'ai dû lire plusieurs fois et m'assurer que toutes les conditions de bord étaient bonnes.
Il y a ZERO DOUBT qui est un meilleur style de codage (le premier est bien meilleur).
Maintenant - ce qui est CLAIR pour les gens peut varier d'une personne à l'autre. Je ne suis pas sûr qu'il existe des normes objectives pour cela (bien que publier sur un forum comme celui-ci et obtenir des contributions de diverses personnes peut aider).
Dans ce cas particulier, cependant, je peux vous dire pourquoi le premier algorithme est plus clair: je sais à quoi ressemble l'itération C ++ sur une syntaxe de conteneur. Je l'ai intériorisé. Quelqu'un UNFAMILIAR (sa nouvelle syntaxe) avec cette syntaxe pourrait préférer la deuxième variante.
Mais une fois que vous connaissez et comprenez cette nouvelle syntaxe, c'est un concept de base que vous pouvez simplement utiliser. Avec l'approche de l'itération de boucle (deuxième), vous devez soigneusement vérifier que l'utilisateur vérifie CORRECTEMENT toutes les conditions de bord pour boucler sur l'ensemble du tableau (par exemple, moins que au lieu de inférieur ou égal, même indice utilisé pour le test et pour l'indexation, etc.).
la source
longerLength = true
, puisreturn longerLength
.length
. Si elle était effectivement déclarée comme un tableau et non comme un pointeur, ils pourraient utilisersizeof
, ou si c'était unstd::array
, la fonction membre correcte estsize()
, il n'y a pas delength
propriété.sizeof
serait en octets ... Le plus générique puisque C ++ 17 l'eststd::size()
.Pas assez. La variable
i
existe ici en dehors de la boucle while et fait donc partie de la portée externe, tandis que (jeu de mots voulu)x
de lafor
boucle -lo n'existe que dans la portée de la boucle. La portée est un moyen très important d'introduire une structure dans la programmation.la source
Les deux boucles ont une sémantique différente:
La première boucle répond simplement à une simple question oui / non: "Le tableau contient-il l'objet que je recherche?" Il le fait de la manière la plus brève possible.
La deuxième boucle répond à la question: "Si le tableau contient l'objet que je recherche, quel est l'index de la première correspondance?" Encore une fois, il le fait de la manière la plus brève possible.
Étant donné que la réponse à la deuxième question fournit strictement plus d'informations que la réponse à la première, vous pouvez choisir de répondre à la deuxième question, puis dériver la réponse de la première question. C'est ce que fait la ligne
return i < array.length;
, de toute façon.Je pense qu'il est généralement préférable d' utiliser simplement l'outil qui correspond à l'objectif, sauf si vous pouvez réutiliser un outil déjà existant et plus flexible. C'est à dire:
bool
variable et une pause est également très bien. (Évite la deuxièmereturn
instruction, la réponse est disponible dans une variable au lieu d'un retour de fonction.)std::find
est très bien (réutilisation du code!).bool
ne l'est pas.la source
Je proposerai une troisième option:
Il existe de nombreuses raisons différentes pour itérer sur un tableau: vérifiez si une valeur spécifique existe, transformez le tableau en un autre tableau, calculez une valeur agrégée, filtrez certaines valeurs hors du tableau ... Si vous utilisez une boucle simple pour, ce n'est pas clair en un coup d'œil spécifiquement comment la boucle for est utilisée. Cependant, la plupart des langages modernes ont des API riches sur leurs structures de données de tableau qui rendent ces différentes intentions très explicites.
Comparez la transformation d'un tableau en un autre avec une boucle for:
et en utilisant une
map
fonction de style JavaScript :Ou sommer un tableau:
contre:
Combien de temps vous faut-il pour comprendre ce que cela fait?
contre
Dans les trois cas, bien que la boucle for soit certainement lisible, vous devez passer quelques instants pour comprendre comment la boucle for est utilisée et vérifier que tous les compteurs et les conditions de sortie sont corrects. Les fonctions modernes de style lambda rendent les objectifs des boucles extrêmement explicites, et vous savez avec certitude que les fonctions API appelées sont implémentées correctement.
La plupart des langages modernes, y compris JavaScript , Ruby , C # et Java , utilisent ce style d'interaction fonctionnelle avec des tableaux et des collections similaires.
En général, bien que je ne pense pas que l'utilisation de boucles soit nécessairement erronée, et c'est une question de goût personnel, je me suis retrouvé fortement en faveur de l'utilisation de ce style de travail avec des tableaux. Ceci est spécifiquement dû à la clarté accrue dans la détermination de ce que fait chaque boucle. Si votre langue possède des fonctionnalités ou des outils similaires dans ses bibliothèques standard, je vous suggère également d'envisager d'adopter ce style!
la source
array.find
pose la question, car nous devons ensuite discuter de la meilleure façon de mettre en œuvrearray.find
. À moins que vous n'utilisiez du matériel avec unefind
opération intégrée , nous devons y écrire une boucle.find
dans leurs bibliothèques standard. Sans aucun doute, ces bibliothèques implémententfind
et leurs proches utilisent des boucles for, mais c'est ce que fait une bonne fonction: elle abstrait les détails techniques loin du consommateur de la fonction, permettant au programmeur de ne pas avoir besoin de penser à ces détails. Ainsi, même s'ilfind
est probablement implémenté avec une boucle for, il permet toujours de rendre le code plus lisible, et comme il est souvent dans la bibliothèque standard, son utilisation n'ajoute aucun frais ou risque significatif.Tout se résume précisément à ce que l'on entend par «mieux». Pour les programmeurs pratiques, cela signifie généralement efficace - c'est-à-dire que dans ce cas, sortir directement de la boucle évite une comparaison supplémentaire et retourner une constante booléenne évite une comparaison en double; cela économise des cycles. Dijkstra est plus soucieux de rendre le code plus facile à prouver . [Il m'a semblé que l'éducation CS en Europe prend la «preuve de l'exactitude du code» beaucoup plus au sérieux que l'éducation CS aux États-Unis, où les forces économiques ont tendance à dominer les pratiques de codage]
la source