Quelles structures de contrôle alternatives utiles connaissez-vous? [fermé]


12

Une question similaire a été close sur SO.

Parfois, lorsque nous programmons, nous constatons qu'une structure de contrôle particulière nous serait très utile, mais n'est pas directement disponible dans notre langage de programmation.

Selon vous, quelles structures de contrôle alternatives sont un moyen utile d'organiser le calcul?

Le but ici est d'obtenir de nouvelles façons de penser la structuration du code, afin d'améliorer le découpage et le raisonnement.

Vous pouvez créer une syntaxe / sémantique à souhait non disponible actuellement ou citer une structure de contrôle moins connue sur un langage de programmation existant.

Les réponses devraient donner des idées pour un nouveau langage de programmation ou pour améliorer un langage réel.

Considérez cela comme un remue-méninges, alors postez quelque chose que vous pensez être une idée folle, mais cela peut être viable dans certains scénarios.

Il s'agit d'une programmation impérative.


1
Pourquoi cette question doit-elle concerner la "programmation impérative"?
missingfaktor

@missingfaktor: Parce que les structures de contrôle concernent la programmation impérative. Bien sûr, certains problèmes peuvent être résolus avec une approche fonctionnelle ou d'une autre manière, mais d'autres paradigmes ne placent pas les structures de contrôle au centre du développement. Mais de toute façon, vous pouvez être créatif.
Maniero

3
Eh bien, j'allais partager le Pattern Matching avec les gardes, car ce sont plus généraux que les instructions Case et If-Thens, mais s'il s'agit d'une zone sans FP, je suppose que c'est juste plus de Pattern Matching pour le reste d'entre nous!
CodexArcanum

@CodexArcanum: Eh bien, la correspondance de motifs est une construction très similaire aux constructions impératives. En effet, Pattern Matching lui-même est une alternative aux structures de contrôle impératives. Ne soyez pas timide ;-)
Maniero

En regardant toutes les réponses, je suis heureux qu'aucune des propositions n'existe dans la vie réelle (et j'espère ne le sera jamais). Désolé :)
serg

Réponses:


14

OK, c'est une question amusante.

Je voudrais également avoir un général elsepour les boucles while et for, lorsque la condition n'est pas vraie lors du premier test:

while (condition) {
    // process
}
else {
    // condition was never true
}

Cela évite le recalcul maladroit de la condition ou son stockage dans une variable.


Python a cela.
Barry Brown

Hey! Enfin un choix original de Python avec lequel je suis d'accord! ;-)
Macneil

5
Non. La clause else dans les boucles for / while en Python est exécutée si la boucle se termine normalement, ce qui signifie que la boucle n'est pas terminée par une instruction break ou return ou une exception. Dans les boucles for, la classe else est exécutée après que la séquence d'éléments bouclée est épuisée, et dans les boucles while, elle est exécutée après que la condition de la boucle est évaluée à False pour la première fois.
pillmuncher

4
Il y a une demande pour ajouter la while ... elseconstruction de la façon dont Macneil la décrit à PHP. Je pense que c'est une excellente idée, car le "voici les résultats / il n'y a pas de résultats" est un idiome assez courant dans les applications Web.
Dean Harding

1
@SnOrfus: Il veut un bloc de code (distinct du bloc de la boucle) qui s'exécute UNIQUEMENT si la condition while n'est jamais remplie. Do-While exécute le bloc de code de la boucle (pas un bloc séparé) une fois, quel que soit le conditionnel.
Legion

10

Pourquoi ne pas mélanger quelques réponses en une seule?

while (expr) {

    // Executed every iteration, unless first{} is present.
    // May be explicitly called rest{} if you like first{} to come first.

    // Blocks may return results, and consequently be used in expressions.
    return expr;

} first {

    // Executed only on the first iteration.

} pre {

    // Executed before every iteration.

} post {

    // Executed after every iteration.

} catch (oops) {

    // All blocks are implicitly try{}ed if followed by a catch{}.

} finally {

    // Executes after the block completes, regardless of exceptions.

} else {

    // Executed if the loop body or rest{} never executes.

} never {

    // Executes only when a client is present.

} drop (bad, worse), // Explicitly ignore certain exceptions.
  until (expr);      // Here, have a post-body condition, too.

Une syntaxe de construction de contrôle de flux généralisée et extensible dans un langage impératif serait plutôt utile et divertissante. Jusqu'à ce que cela apparaisse, je suppose que je vais simplement utiliser Lisp ou quelque chose.


5
Pas utile, déroutant. Pas divertissant, lent.
Josh K

2
@ Jos K: Ouais, tu as raison. Quoi, tu pensais que je ne serais pas d'accord? Cette réponse se mord la langue pour ne pas rire.
Jon Purdy

Si cela était ironique, cela a certainement réussi!
Josh K

11
+1 m'a fait rire. Nécessite une clause "entre" pour que le code s'exécute entre les itérations.
Barry Brown

1
+1, mais il en a également besoin seventh { ... }.
j_random_hacker

7

Les structures de contrôle en tant que fonctions.

Je veux for, if, else, while, etc. comme des fonctions, et non pas des structures spéciales.

Je veux return, try/exceptet gotoêtre des dérivés de suites.

Bien sûr, cela a moins à voir avec une structure de contrôle particulière et plus à voir avec la façon dont vous voyez les structures de contrôle en général, la méta des structures de contrôle.


2
Les continuations sont un concept lourd pour une langue, et il y a de bonnes raisons de les laisser hors de nombreuses langues.
David Thornley

Je ne suis pas en désaccord; ayant for, if, elseet whileque les fonctions me fait déduis que les paramètres de ces fonctions doivent être exécutées paresseusement afin de se comporter les mêmes que les constructions d' origine. Avoir la capacité de faire une exécution paresseuse serait bien.
Dr.Wily's Apprentice

1
@David: Ce n'est pas parce qu'ils sont là que vous devez les utiliser. Les programmeurs réguliers peuvent utiliser ce à quoi ils sont habitués return, les exceptions, etc. Mais maintenant, le programmeur expert a un outil supplémentaire puissant dans sa boîte à outils si le besoin s'en fait sentir. En outre, les langues à mon humble avis ne devraient pas être "abruties". Les langues devraient avoir la capacité de soutenir une expertise accrue et la croissance des connaissances en matière de CS.
dietbuddha

1
@dietbuddha: (suite) Je ne dis pas que les suites sont mauvaises, je dis qu'elles sont lourdes, et les rendre utilisables a des implications pour la langue, quelque chose comme les exceptions. De plus, avoir des continuations est susceptible de forcer des changements dans la façon dont les gens utilisent le langage, à nouveau comme les exceptions en C ++. Un langage qui prend en charge les continuations sera différent de celui qui ne le fait pas, à la fois dans le bon et le mauvais sens.
David Thornley

1
@ Steve314 - longjmp n'est pas une continuation bien qu'il y ait des similitudes. Les continuations sauvegardent l'état entier (tous les locaux, globaux et la pile). longjmp enregistre un pointeur de pile, donc si vous échappez au-delà de la portée où setjmp a été utilisé, vous obtenez une erreur de segmentation car le cadre n'existe plus. Je pense qu'il y a aussi des limites dans les variables qu'il enregistre.
dietbuddha

6

L'article lié comprend clairement les boucles N + 1/2 de Donald Knuth . Exprimé en C / C ++ / Java:

for (;;) {
  get next element;
  if (at the end) break;
  process the element;
}

Ceci est utile pour lire des lignes ou des caractères d'un fichier, tester si vous avez atteint EOF, puis le traiter. Je suis tellement habitué à voir le motif for(;;)..if(..)break;apparaître qu'il est idiomatique pour moi. (Avant d'avoir lu l'article de Knuth, réimprimé dans le livre Literate Programming , celui-ci était un "wtf?".)

Knuth a suggéré les mots clés loop/while/repeat:

loop:
  S;
while C:
  T;
repeat

Set Tsont les espaces réservés pour une série de zéro ou plusieurs instructions, et Cest une condition booléenne. S'il n'y avait pas de Sdéclaration, ce serait une boucle while, et s'il n'y avait pas de Tdéclaration, ce serait une boucle do.

Cette construction elle-même pourrait être généralisée en autorisant zéro ou plusieurs while Cclauses, ce qui la rend parfaite pour exprimer des boucles infinies, puis des conditions plus rares qui nécessiteraient deux vérifications.

Dans le même article, Knuth a suggéré un mécanisme de signalisation qui serait une version locale des exceptions de lancement / capture (comme alternative à l'utilisation de goto).

Pour moi? Je souhaite que Java supporte l'optimisation des appels, afin que je puisse exprimer n'importe quelle structure de contrôle générale selon les besoins.


Mise à jour: j'ai oublié de mentionner que de nombreux programmeurs C / C ++ / Java contournent celui-ci en utilisant une affectation intégrée dans l'état de while:

while ((c = getc(f)) != -1) {
   T;
}

En utilisant les termes de la construction de Knuth, cela est permis quand Set Cpeut être combiné en une seule expression. Certaines personnes détestent voir l'affectation intégrée ci-dessus, tandis que d'autres détestent voir breakce qui for (;;)précède. Mais quand Set Cne peut pas être combiné, comme quand Sa plusieurs instructions, le for (;;)est la seule alternative sans répéter le code. L'autre alternative est de simplement dupliquer le Scode:

S;
while (C) {
  T;
  S;
}

L' loop/while/repeatalternative de Knuth semble bien meilleure.


Cette boucle tout en répétant ressemble beaucoup à la repeat-untilboucle Pascal .
Mason Wheeler

@Mason: La différence est qu'il y a deux endroits où vous pouvez insérer des déclarations plus votre condition, pas seulement une.
Macneil

Oh, je vois ce qui se passe. Oui, c'est intéressant ...
Mason Wheeler

ANSI Basic avait un do while ... loop until- à la fois la précondition et la postcondition sont facultatives, et je me souviens (vaguement) d'avoir utilisé les deux dans une boucle. Pour votre condition moyenne, il y a l'Ada exit when ...;. Le principal avantage if ... break;est que vous pouvez écrire exit loopname when ...;pour quitter simultanément plusieurs boucles imbriquées (mais pas nécessairement toutes). Probablement légèrement plus visible que cette cassure aussi.
Steve314

Chose amusante. Ada a exactement cette fonctionnalité.
John R. Strohm

6

Le langage BCPL avait une valueofexpression qui pouvait être utilisée pour transformer une séquence d'instructions en une seule expression:

foo(a, b, valueof {some series of statements; resultis v});

some series of statementspeut être n'importe quoi et le tout valueofs'évalue v.

Cela peut être utile en Java lorsque vous devez calculer un argument pour appeler un this()ou super()(ce qui nécessite que rien ne se passe avant). Bien sûr, vous pouvez simplement écrire une méthode distincte, mais cela pourrait être difficile si vous devez passer de nombreuses valeurs locales pour le contexte.

Si vous pouvez utiliser finalpour les variables nécessaires, vous pouvez déjà faire un valueofen Java en utilisant des classes internes anonymes:

foo(a, b, new Object(){String valueof(){
    String v ...; some series of statements; return v;}}.valueof());

1
GCC a une extension pour cela - ({ statement1; statement2; ...; result-expr; }). J'ai vu pareil ailleurs aussi, mais je ne me souviens pas où. Probablement tous copiés depuis BCPL.
Steve314

6
unless(condition) {
  // ...
}

fait la même chose que:

if(!condition) {
  // ...
}

repeat {
  // ...
} until(condition)

fait la même chose que:

do {
  // ...
} while(!condition)

Vous voudrez peut-être aller à Lisp ...
duros

1
Ou Ruby, il a une syntaxe similaire pour unless.
Josh K

2
Semble assez Perlish. Il y a plus d'une façon de le faire.

2
@ Steve314 Vous devez utiliser un style d'accolade incorrect . ;-)
Orbling

1
@Orbling - pas du tout. Tout le monde utilise un style d'accolade incorrect.
Steve314

5

Sur une note différente, je voudrais voir un meilleur support pour les itérateurs dans les langages de programmation. En particulier, lorsque vous souhaitez descendre deux collections par paires :

for (String s, Integer i : stringsSet, integersSet) {
    // use the pair (s, i)
}

Certains langages dynamiques peuvent déjà l'avoir, ou être facilement pris en charge via des bibliothèques et des macros, mais je pense que c'est dans l'esprit de votre question.

Si les deux ensembles ne sont pas de la même taille, cela pourrait déclencher une exception ou vous pourriez avoir un elseaprès la boucle pour signaler qu'il y avait une différence de taille.

Naturellement, vous pouvez généraliser cela pour descendre trois listes ou plus.


Mise à jour: Il serait également utile de faire le produit cartésien entre les itérables:

for (String s, Integer i : stringsSet * integersSet) {
    // use the pair (s, i), each s with each i
}

qui ne serait rien de plus que des boucles imbriquées:

for (String s : stringsSet) {
    for (Integer i : integersSet) {
        // use the pair (s, i), each s with each i
    }
}

Je m'inquiète un peu qu'entre les deux notations que j'ai fournies ici, il y ait une différence O (n) et O (n ^ 2) dans le nombre de paires, avec seulement le changement d'un seul caractère.


2
En Python, il y a zip et zip_longest qui font cela.
pillmuncher

Cool, je sous-estime peut-être Python et je devrais lui donner un second look après de nombreuses années. Cela me rappelle, parfois, vous voulez aussi le produit cartésien, l'équivalent de boucles imbriquées.
Macneil

1
Ces deux cas dans Scala: paste.pocoo.org/show/297429
missingfaktor

1
En référence au produit cartésien: Encore une fois, Python l'a. for a, b, c in itertools.product(iter1, iter2, iter3):vous donne le produit cartésien évalué paresseusement. Qu'est-ce que c'est? Vous voulez aussi des permutations et des combinaisons d'un itérateur donné? itertools.permutations, itertools.combinations.
aaronasterling

1
Perspective Haskell: utilisez "zipWith" pour le premier cas et la compréhension de la liste ou la monade List pour le second. Comme les exemples Python / Scala, mais plus élégant. :)
LennyProgrammers

5

Il existe ce qu'on appelle la "boucle de Dijkstra" (également appelée "boucle gardée de Dijkstra"). Il a été défini dans The Guarded Command Language (GCL) . Vous pouvez trouver des informations sur la syntaxe et la sémantique dans l'article Wikipédia ci-dessus à la section 6 Répétition: faire .

De nos jours, je connais en fait un langage de programmation qui prend directement en charge cette structure de contrôle. Il s'agit d' Oberon-07 (PDF, 70 Ko). Et il prend en charge "Dijkstra's Loop" sous votre forme de déclaration while. Jetez un œil à la section 9.6. Alors que les déclarations dans le PDF ci-dessus.

WHILE m > n DO m := m – n 
ELSIF n > m DO n := n – m 
END

PS Ceci est une copie de ma réponse SO .


On dirait que le vérificateur de modèle Spin a également cette construction, avec une sémantique identique et une syntaxe fondamentalement identique.
j_random_hacker

4

Expressions de style icône avec retour en arrière intégré.

Python bénéficie de nombreux avantages du générateur d'icônes - et en fait un meilleur travail en général, l'OMI. Et en principe, le retour en arrière n'était qu'une sorte de lancer d'exception, mais c'était la simplicité des expressions l'équivalent approximatif de ...

x = (a / b) else c;

pour gérer les cas d'échec comme la division par zéro.

Où Icon est devenu fou - aucun opérateur de comparaison à retour booléen. Les comparaisons ont toujours réussi ou déclenché un retour en arrière, et il y avait un autre problème sémantique dont j'essaie désespérément de me souvenir ... eh bien, disons simplement qu'il est probablement plus réprimé qu'oublié.

J'ai toujours pensé qu'ils devraient avoir une ifexpression sans autre partie - if (condition, success-value)genre de chose, revenir en arrière si la condition retourne fausse - et abandonner les comparaisons étranges.

EDIT Je me souviens - vraiment évident. Une comparaison avec deux arguments réussit ou échoue - elle ne calcule pas de nouvelle valeur à renvoyer. Alors, quand il réussit, que revient- il? Réponse - l'un des arguments. Mais si vous écrivez a > b, quel est l'argument logique pour retourner - aou b? Et si vous écrivez à la b < aplace? Je pense que cela renvoyait toujours le bon argument, ce qui a autant de sens que n'importe quoi, mais cela me semblait toujours le mauvais argument.


4

Ce n'est qu'une idée générale et une syntaxe:

if (cond)
   //do something
else (cond)
   //do something
also (cond)
   //do something
else
   //do something
end

La condition AUSSI est toujours évaluée. ELSE fonctionne comme d'habitude.

Cela fonctionne pour le cas aussi. C'est probablement un bon moyen d'éliminer la déclaration de rupture:

case (exp)
   also (const)
      //do something
   else (const)
      //do something
   also (const)
      //do something
   else
      //do something
end

peut être lu comme:

switch (exp)
   case (const)
      //do something
   case (const)
      //do something
      break
   case (const)
      //do something
   default
      //do something
end

Je ne sais pas si c'est utile ou simple à lire mais c'est un exemple.


3

La continuité du style de passage me vient à l'esprit. Ensuite, bien sûr, vous aimeriez également avoir l' optimisation des appels de queue .


1
Pas un grand fan de cela, et je ne crois pas que ce soit vraiment une "structure de contrôle" mais plutôt un bloc de construction dans les langages fonctionnels et le style de programmation. Node.js semble cependant assez accroché.
Josh K

1
Bien sûr, c'est une structure de contrôle. Ce n'est tout simplement pas celui qui vient avec un mot-clé comme «si» ou «pour», mais comme un modèle pour structurer le flux de contrôle (d'où la structure de contrôle). Il est même utilisé en coulisse par de nombreux compilateurs. Et ce n'est pas non plus limité aux FL. Cependant, vous avez besoin de fonctions comme objets de première classe.
pillmuncher

1
Vous obtenez l'optimisation des appels de queue en C et C ++ ces jours-ci, mais à l'OMI, il manque le point. Le point est qu'il est une optimisation. Dans Scheme, un véritable appel de queue est évident. En C ++ en particulier, beaucoup de choses peuvent signifier que votre appel de queue n'est pas un appel de queue. Et le débordement de pile signifie que votre application est cassée. OMI, il devrait y avoir quelque chose comme une goto return ...;déclaration, rendant l'intention d'appeler la queue explicite, donc si le compilateur ne peut pas le rendre itératif, c'est une erreur.
Steve314

1
@Macneil - à ma connaissance, l'optimisation des appels de queue se fait dans GCC, Clang et Visual C ++. GCC a des conversions plus sophistiquées de la récursivité à l'itération, qui peuvent gérer un certain nombre de cas qui ne sont pas des récursions de queue. Mais beaucoup de choses peuvent mal tourner. Passez un pointeur sur une variable locale dans cet appel de queue et le cadre de pile ne peut pas être éliminé, car la variable doit être maintenue en vie. En C ++, les destructeurs de variables locales se produisent normalement après le retour de l'appel "tail", ce qui signifie qu'il ne s'agit pas du tout d'un appel de queue.
Steve314

1
@Mike - c'est mon point. Si vous utilisez un style de codage récursif, sans l'appel final "optimisation", votre code est potentiellement cassé, car le débordement de pile est une erreur. En C ++, c'est une optimisation - pour que le compilateur fasse des optimisations pour que vous n'ayez pas à le faire, c'est bien, mais vous ne pouvez pas vous y fier. Si vous voulez avoir la liberté d'écrire dans un style récursif, mais ne voulez pas vous soucier des problèmes de profondeur de pile, l'élimination des appels de queue n'est pas une optimisation - c'est un problème de correction. Si la récursivité est le meilleur moyen de coder quelque chose, vous devriez pouvoir le faire - ne pas avoir à vous soucier du débordement de pile.
Steve314

3

Branche de thread transparente, elle a la syntaxe comme une fonction, mais s'exécute dans un thread séparé et ne peut pas accéder aux données qui ne lui ont pas été initialement transmises.

branch foo(data, to, be, processed){
    //code
    return [resulting, data]
}

Lorsqu'une branche est appelée, elle renvoie immédiatement un handle.

handle=foo(here, is, some, data)

La poignée peut être utilisée pour vérifier si la tâche est terminée.

handle.finished() //True if the execution is complete

Si le résultat est demandé avant la fin de l'exécution, le thread principal attendra simplement.

[result, storage]=handle.result()

Cela ne couvrirait pas les scénarios de multithreading les plus avancés, mais fournirait plutôt un moyen facilement accessible de commencer à utiliser plusieurs cœurs.


Jetez un oeil à Cilk, c'est une extension très propre et simple de C: en.wikipedia.org/wiki/Cilk . Je ne sais pas s'il a un handle.finished()test, mais spawnet syncc'est tout ce dont vous avez besoin pour 90% des tâches de programmation parallèle.
j_random_hacker

3
if (cond)
   //do something
else (cond)
   //do something
else (cond)
   //do something
first
   //do something
then
   //do something
else (cond)
   //do something
else
   //do something
end

Les blocs FIRST et THEN s'exécutent si l'une des 3 conditions est évaluée comme vraie. PREMIER bloc s'exécute avant le bloc conditionnel et ALORS s'exécute après l'exécution du bloc conditionnel.

ELSE l'écriture conditionnelle ou finale suivant les instructions FIRST et THEN est indépendante de ces blocs.

Il peut se lire comme suit:

if (cond)
   first()
   //do something
   then()
else (cond)
   first()
   //do something
   then()
else (cond)
   first()
   //do something
   then()
else (cond)
   //do something
else
   //do something
end


function first()
   //do something
return
function then()
   //do something
return

Ces fonctions ne sont qu'un formulaire à lire. Ils ne créeraient pas de portée. Cela ressemble plus à un gosub / return de Basic.

Utilité et lisibilité comme sujet de discussion.


2

Je me retrouve parfois à écrire une boucle qui doit faire quelque chose de différent lors de la première itération. Par exemple, afficher les balises <th> au lieu des balises <td>.

Je gère cette situation avec un drapeau booléen. Quelque chose comme ça:

first = true

while (some_condition)
    if (first)
        do_something
        first = false
    else
        do_something_else

Il semble stupide de vérifier la valeur de firstà chaque itération alors que ce sera faux la plupart du temps.

J'aimerais avoir une option de bouclage pour spécifier un corps de boucle différent lors de la première itération. Il n'y aurait pas besoin d'une variable distincte. Le code compilé n'en aurait pas besoin non plus, car le code généré aurait deux corps, un pour la première itération et un pour le reste.


La question SO a une idée similaire, mais avec un opérateur "then" à utiliser sur les valeurs. De cette façon, vous n'avez probablement pas de code en double. Par exempleprint(out, first "<th>" then "<td>")
Macneil

1
Le meilleur moyen est de démarrer l'itération +1.
Josh K

1
Le cas courant est une boucle avec entre-manipulations, par exemple la liste des éléments avec un séparateur de virgules. Strictement, c'est if (!first) gimme-a-comma ();, mais à peu près la même chose. Une objection que j'aurais, cependant - si vous le concluez correctement, vous vous retrouvez avec des choses comme la méthode de jointure de chaîne Python - quelle que soit la fréquence à laquelle vous avez besoin du modèle de base, la boucle sous-jacente n'a pas besoin d'être réécrite aussi souvent.
Steve314

Comme le dit Josh, bien sûr, retirer le premier élément de la boucle est valide. Dans le cas des séparateurs de virgules, cela signifie du code en double, mais cela peut être un appel de fonction en double. Personnellement, je préfère l'inefficacité du if (!first), mais les micro-optimiseurs peuvent soulever des objections de prédiction de branche.
Steve314

1
@Barry Brown: Dérouler la 1ère itération de la boucle peut ou non être plus rapide que de vérifier un drapeau booléen. Un prédicteur de branche décent prédit au pire les 2 premières itérations, je prédis :) Je préférerais utiliser if (!first)et laisser un compilateur d'optimisation décider si le corps de la boucle est suffisamment petit pour que le déroulement et la suppression firstsoient une victoire nette.
j_random_hacker

1

[copié de ma propre réponse sur stackoverflow]


ignoring - Pour ignorer les exceptions se produisant dans un certain bloc de code.

try {
  foo()
} catch {
  case ex: SomeException => /* ignore */
  case ex: SomeOtherException => /* ignore */
}

Avec une construction de contrôle ignorant, vous pouvez l'écrire de manière plus concise et plus lisible sous la forme:

ignoring(classOf[SomeException], classOf[SomeOtherException]) {
  foo()
}

[Scala fournit cela (et de nombreuses autres constructions de contrôle de gestion des exceptions) dans sa bibliothèque standard, dans le package util.control. ]


4
Les exceptions ne doivent pas être ignorées.
Josh K

1
Sauf que, dans le contexte, une exception peut ne pas être une erreur.
Steve314

1
@Josh, @Steve: Il y a des cas où vous voudriez ignorer les exceptions. Voir ce fil pour certains de ces cas.
missingfaktor

Une exception est une alerte indiquant que quelque chose ne va pas. Un try..catchbloc vide est une chose; cela vous oblige à reconnaître l'erreur et à l'ignorer délibérément; tout en jetant des morceaux de code sous un ignorer global peut entraîner des problèmes lorsque ces exceptions sont levées, ainsi que de mauvaises habitudes de programmation.
Josh K

@Josh - Je viens de supprimer deux commentaires parce que je ne pensais pas directement - mais les espoirs sont grands pour celui-ci. Si cette déclaration est globale, je suis probablement d'accord - mais elle ressemble à une structure de blocs pour moi. IOW c'est comme un trybloc, sauf qu'il répertorie les exceptions qu'il intercepte et ignore à l'avant, au lieu de plus tard. Cela pourrait même être un avantage de lisibilité - par exemple, faire savoir au lecteur qu'un fichier manquant n'est pas une erreur avant même de lire l'appel ouvert.
Steve314

1

Au lieu de:

switch(myEnum) {
  case MyEnum.Val1: do1(); ...
  case MyEnum.Val2: do2(); ...
....

Faites-le en Python, ou maintenant aussi en C #:

action = val2func[myEnum]
action()

Scala a beaucoup de nouvelles fonctionnalités.

Enfin, des langages tels que Clojure peuvent être étendus pour fournir des fonctionnalités supplémentaires.


1
C peut le faire aussi. Et Pascal. Un probablement toutes ces vieilles langues des années 70/80/90. Un tableau de fonctions devient un tableau de pointeurs de fonction, mais ce n'est pas grave. Là où cela devient plus facile, c'est quand vous avez des fonctions anonymes - vous savez, comme en Lisp, ML, ...
Steve314

Steve314 - correction - ceci est un exemple de dictionnaire qui mappe n'importe quelle valeur à une fonction. C'est là que les choses deviennent légèrement plus propres que la plupart des choses des années 70/80/90.
Job

1

J'ai deux idées.

Souvent, je trouve que je me répète par catchblocs. Cela peut être quelque peu aidé par l'extraction de méthodes, mais cela peut entraîner un encombrement inutile si les méthodes sont très courtes ou autrement dignes d'une méthode. Donc, ce serait bien d'imbriquer des catchblocs:

try {
    // Save something
} catch (Exception e) {
    // Something we do for all Exceptions
    catch (ProcessingException e) {
        // Something we do for all Processing exceptions
        catch (DBExcpetion e) {
            // DBExceptions are a subclass of ProcessingException
        }
        catch (BusinessRuleException e) {
            // BusinessRuleExceptions are also a subclass of ProcessingException
        }
    }
    // Something we do after specific sub class Exceptions
 }

Dans la programmation Web, je me retrouve souvent à faire quelque chose comme ça (ce n'est pas un vrai exemple, alors n'analysez pas les cas fictifs):

Account a = getSavedAccount();
if (a == null) {
    a = getAccountFromSessionId();
}
if (a == null) {
    a = getAccountFromCookieId();
}
if (a == null) {
    a = createNewAccount();
}

En Javascript (enfin, ECMAScript, et peut-être d'autres que je ne connais pas), car n'importe quelle valeur peut être évaluée comme condition, ||peut aider.

var a = getAFromLocation1() || getAFromLocation2() || default;

J'aime vraiment à quoi cela ressemble, et je souhaite que plus de langues, en particulier certaines côté serveur, soient prises en charge. (PHP peut évaluer n'importe quoi en tant que condition, mais convertit toute l'expression conditionnelle en un booléen au lieu de conserver la valeur. Je ne sais pas pour Python ou Ruby.) Cela pourrait devenir compliqué après environ trois cas, mais si vous en avez plus de trois cas, vous pourriez également avoir une mauvaise conception de logiciel.


Python fait quelque chose comme votre || évaluation, mais quand il a finalement obtenu son expression conditionnelle x if c else ysyntaxe, une raison majeure qui est arrivé était parce que beaucoup d'expressions utilisant cette sémantique pour ||et &&était bogué subtilement. IIRC un cas commun était une valeur (telle que zéro) qui était valide pour l'application en cours de traitement false, étant ainsi rejetée de sorte qu'une solution de remplacement non valide était utilisée à la place. Cependant - voir ma réponse WRT le langage de programmation Icon, qui peut avoir des expressions comme get1() else get2() else default.
Steve314

1

Le commutateur généralisé a dit ci-dessus:

 switch(x){
  predicate1:
     dosomething();
  predicate2:
     dosomethingelse();
 }

À Haskell:

  switch' :: a -> [(a -> Bool, b)] -> b
  switch' a [] = undefined
  switch' a (f,b):xs = if f a
                     then b
                      else switch' a xs

0

En C #, je voudrais utiliser des switch () { ... }expressions simples mais extensibles:

switch (value)
{
  // string-based operators:
  case begins "Maria": // to catch Maria Carey
    break;
  case ends "Washington": // to catch George Washington
    break;
  case like "ph": // to catch Phil, Phillip, Sophie
    break;
  case between "Aaron" and "April": // to catch all names between
    break;

  // use non-static variables in case expression:
  case Dao.GetDefaultBabyName():
    break;

  // continuable cases without breaking
  case "John":
    bonus = 25;
  case "Peter":
    salary = 500;
    break;

  // jumps between cases
  case "Aleron":
    // do something
    break;
  case "Bella":
    // do something
    jump "Aleron";
    break;

}

Etc. La même chose avec des chiffres ou d' autres types (que supports IComparable, IConvertible...)

Cela pourrait rendre mon code plus laconique et lisible.


La chute des cas est un mal connu, et je suis généralement d'accord sans ça. Et les sauts sont trop OBTENUS pour toute programmation sensée. Mais avoir des expressions et des non-statiques dans le cas serait un bel ajout.
CodexArcanum

0

C'est une question amusante, comme l'a dit @Macneil.

Ma structure de contrôle inhabituelle préférée, que j'ai (humble toux) découverte, est l' exécution différentielle .

Il a certaines utilisations. Pour moi, l'écrasante utilisation est dans la programmation des interfaces utilisateur, ce qui est un exemple du problème plus général de la maintenance des données redondantes en correspondance. D'une part, il y a des données d'application, et d'autre part, il y a des contrôles d'interface utilisateur, qui doivent être maintenus en accord. Cela ressemble à "contraignant" mais il y a en fait beaucoup plus.

Habituellement, je l'implémente par des macros en C ou C ++. En C #, je dois le faire en développant des instructions à la main. C'est une douleur, mais ça marche.

Une fois que je l'ai implémenté en termes de macros Lisp, puis c'était très propre. Cela n'a pas exigé de prudence de la part du programmeur. J'aurais pu faire la même chose dans n'importe quel autre langage structuré si j'avais pris la peine d'écrire un analyseur complet puis de générer toutes les bonnes choses. C'est un gros projet et je ne l'ai pas fait.


0

Les structures de contrôle "traditionnelles" comme le forcontrôle du travailleur, le gardant soumis aux idéologies corrompues de l'élite capitaliste au pouvoir. C'est pourquoi j'utilise des structures de contrôle alternatives comme à la ph0rplace. C'est comme for, mais plus radical: vous ne vous surprendrez pas à ph0rporter un costume et une cravate, jaillissant de certains BS d'entreprise. ph0rle garde réel, mec.

Combattre le pouvoir!


0

Simplest forboucle-

for(100)
{
    //Will run for 100 times
}


for(i)
{
    //Will run for i times while i must be a positive integer
}


for(i as a)
{
    //Will run for i times while i must be a positive integer
    //and a is the incremental loop variable starting from 0 and 
    //scoped within the loop
}


for(i as a=2)
{
    //Will run for i times while i must be a positive integer
    //and a is the incremental loop variable starting from 2 and 
    //scoped within the loop
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.