2010-03-24 4 views
7

Considérons une situation dans laquelle vous devez appeler des routines successives et arrêter dès que l'on renvoie une valeur qui pourrait être évaluée comme positive (vrai, objet, 1, str (1)).Est-il considéré comme incorrect d'exécuter une fonction dans une instruction conditionnelle?

Il est très tentant de le faire:

if (fruit = getOrange()) 
elseif (fruit = getApple()) 
elseif (fruit = getMango()) 
else fruit = new Banana(); 

return fruit; 

Je l'aime, mais ce n'est pas un style très récurrent dans ce qui peut être considéré comme le code de production professionnelle. On risque de voir plutôt un code plus élaboré comme:

fruit = getOrange(); 
if(!fruit){ 
    fruit = getApple(); 
    if(!fruit){ 
     fruit = getMango(); 
     if(!fruit){ 
      fruit = new Banana(); 
     } 
    } 
} 

return fruit; 

Selon le dogme sur les structures de base, est la forme précédente acceptable? Le recommanderiez-vous?

Edit:

Je présente mes excuses à ceux qui ont supposé que ces fonctions étaient censées être des usines ou des constructeurs. Ils ne sont pas, ils sont juste des espaces réservés. La question est plus sur la syntaxe que "l'usine". Ces fonctions pourraient aussi bien être lambda.

+1

Cela dépend de la langue. – SLaks

Répondre

9

Si vous voulez une syntaxe succincte, plusieurs langages permettent d'utiliser le "logique ou" à cet effet (C# fournit explicitement un opérateur de fusion, car les zéros ne sont pas faussés).

Python:

fruit = (getOrange() or 
      getApple() or 
      getMango() or 
      Banana()) 

C#:

fruit = getOrange() ?? 
     getApple() ?? 
     getMango() ?? 
     new Banana(); 
+0

Je crois qu'il y a des projets pour les futures versions de PHP pour rendre optionnelle la deuxième partie des instructions ternaires de style C, ce qui vous permet d'utiliser '?:' En PHP tout comme C# 's '??'. –

+0

l'implémentation de python correspondrait à javascript. l'opérateur de coalescence serait un ajout intéressant à de nombreuses langues. Merci pour cela. –

1

Le problème, comme je le vois, n'est pas la structure, mais les règles de conduite. Pourquoi getOrange est-il disponible avant getApple, etc?

Vous êtes probablement plus susceptibles de voir quelque chose de plus axé sur les données:

enum FruitEnum 
{ 
    Orange, Apple, Mango, Banana 
} 

et séparément,

List<FruitEnum> orderedFruit = getOrderedFruit(); 
int i = 0; 
FruitObj selectedFruit; 
while(selectedFruit == null && i <= orderedFruit.Count) 
{ 
    fruit = FruitFactory.Get(orderedFruit[i++]); 
} 
if(fruit == null) 
{ 
    throw new FruitNotFoundException(); 
} 

Cela dit, pour simplifier votre code, vous pouvez utiliser un opérateur coalesce:

fruit = getOrange() ?? getApple() ?? getMango() ?? new Banana(); 
+0

Comme l'enum, car il correspond à la vue du monde existant de l'OP. L'usine de fruits ... Bon Dieu. On dirait beaucoup de problèmes pour un sac de fruits. Mais je comprends pourquoi vous le faites de cette façon. –

+2

Vous assumez beaucoup sur les constructions de langage disponibles que l'OP ne met pas dans sa question. – tvanfosson

+0

aussi, il n'y a aucune raison de supposer que son langage, ou tout autre, a l'opérateur coalesce. Je ne connais que C#, et ce n'est certainement pas C#, très probablement c'est PHP. C# ne permettra pas l'assignation à l'intérieur d'un if conditionnel. – Tesserex

3

Dans un langage fortement typé qui n'équivaut pas 0/null à false et non-0/non-null à tr Je dirais que c'est sûrement sûr, mais marginalement moins lisible dans le cas général, où les noms de vos méthodes et le nombre de paramètres peuvent être plus grands. Je l'éviterais personnellement, sauf pour certains idiomes standard, dans les cas où 0/null équivaut à false et non-0/non-null à true simplement en raison du risque potentiel de confusion d'affectation avec vérification d'égalité lors de la lecture du code. Quelques idiomes dans les langues faiblement typés, comme C, sont tellement répandu qu'il n'a pas de sens de les éviter, .e.g,

while ((line = getline()) != null) { 
    ... 
} 
4

je peux penser à deux alternatives.

Le premier est seulement permis dans les langages comme le vôtre (PHP?), Où single = dans un conditionnel est ok.

if ((fruit = getOrange()) != null) 
    elseif ((fruit = getApple()) != null) 
    elseif ((fruit = getMango()) != null) 
    else fruit = new Banana(); 

Il est clair que vous faites une comparaison et que le single = n'est pas une erreur.

fruit = getOrange(); 
    if(!fruit) fruit = getApple(); 
    if(!fruit) fruit = getMango(); 
    if(!fruit) fruit = new Banana(); 

Tout comme votre deuxième exemple, mais se débarrasse de l'imbrication supplémentaire laide.

0

Pour répondre à votre question directement: il est généralement mauvaise forme pour avoir des effets secondaires dans la déclaration conditionnelle.

En tant que travail autour, vous pouvez stocker vos constructeurs de fruits dans un tableau et trouver le premier constructeur qui renvoie une valeur non nulle (pseudo-code):

let constructors = [getOrange; getApple; getMango; fun() -> new Banana()] 
foreach constructor in constructors 
    let fruit = constructor() 
    if fruit != null 
     return fruit 

est comme un opérateur nul soudent, mais plus généralisée . En C#, vous auriez probablement utiliser LINQ comme suit:

var fruit = constructors 
    .Select(constructor => constructor()) 
    .Filter(x => x != null) 
    .First(); 

Au moins cette façon, vous pouvez passer vos constructeurs autour comme une classe d'usine, au lieu de les coder en dur avec l'opérateur nul soudent.

1

en C ou C++, vous pouvez écrire:

return (fruit = getOrange()) ? fruit : 
     (fruit = getApple()) ? fruit : 
     (fruit = getMango()) ? fruit : 
     new Banana(); 

La raison d'éviter à la fois ceci et votre première version est pas « dogme sur les structures de base », il est que la cession lui-même dans une condition déroutant. Toutes les langues ne le supportent pas, d'une part. Pour un autre, il est facilement lu comme ==, ou le lecteur pourrait être incertain si vous le pensiez vraiment, ou peut-être ==. Ajouter != 0 à chaque condition devient assez dense et verbeuse.

GCC a une extension pour permettre:

return getOrange() ? : getApple() ? : getMango() ? : new Banana(); 

La même chose peut souvent être réalisé avec || ou or (mais pas en C ou C++).

Une autre possibilité est:

do { 
    fruit = getOrange(); 
    if (fruit) break; 
    fruit = getApple(); 
    if (fruit) break; 
    fruit = getMango(); 
    if (fruit) break; 
    fruit = new Banana(); 
} while (false); 

Cela va encore mieux dans une langue où vous pouvez break d'un bloc de base, que vous pouvez avec last en Perl, puisque vous pouvez passer du do/while(false). Mais probablement, seuls les programmeurs de montage vont l'aimer.

0

Je ne pense pas que tout le monde a mentionné que la première version peut être un peu difficile à franchir dans un débogueur. La différence de lisibilité est discutable, mais en général, j'évite les affectations et les appels de fonction dans les conditions pour faciliter le suivi de l'exécution, si nécessaire (même si je n'ai jamais besoin de le déboguer, quelqu'un d'autre devra peut-être modifier le code).

Questions connexes