2010-03-24 9 views
7

Considere una situación en la que necesite llamar rutinas sucesivas y detenerse tan pronto como se devuelva un valor que podría evaluarse como positivo (verdadero, objeto, 1, str (1)).¿Se considera mala forma ejecutar una función dentro de una instrucción condicional?

Es muy tentador para hacer esto:

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

return fruit; 

me gusta, pero esto no es un estilo muy recurrente en lo que puede considerarse el código de producción profesional. Uno de ellos es probable que en lugar de código más elaborado como:

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

return fruit; 

De acuerdo con el dogma de las estructuras básicas, es la forma anterior aceptable? ¿Lo recomendarías?

Editar:

pido perdón a aquellos que asumieron que estas funciones estaban destinados a ser fábricas o constructores. No lo son, solo son marcadores de posición. La pregunta es más sobre sintaxis que "factorizar". Estas funciones también podrían ser lambda.

+1

Depende del idioma. – SLaks

Respuesta

9

Si desea una sintaxis sucinta, varios idiomas permitir el uso de la "lógica o" para este propósito (C# proporciona explícitamente un operador de coalescencia, debido a los nulos no son Falsy).

Python:

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

C#:

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

Creo que hay planes para versiones futuras de PHP para hacer que la segunda parte de las declaraciones ternarias de estilo C sea opcional, lo que le permite usar '?:' En PHP al igual que C# 's '??'. –

+0

la implementación de python se ajustaría a javascript. el operador coalescente sería una buena adición a muchos idiomas. gracias por esto. –

1

El problema, según lo veo, no es la estructura, sino las reglas de manejo. ¿Por qué getOrange viene antes de getApple, etc.?

usted es probablemente más propensos a ver algo más impulsado por los datos:

enum FruitEnum 
{ 
    Orange, Apple, Mango, Banana 
} 

y por separado,

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(); 
} 

Dicho esto, para simplificar el código, se puede utilizar un operador se unen:

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

Al igual que la enumeración, ya que se ajusta a la visión del mundo existente de OP. La fábrica de fruta ... Buen Señor. Parece un montón de problemas para una bolsa de fruta. Pero entiendo por qué lo haces de esa manera. –

+2

Estás asumiendo mucho acerca de los constructos de lenguaje disponibles que el OP no pone en su pregunta. – tvanfosson

+0

también, no hay razón para suponer que su lenguaje, o cualquier otro, tiene el operador de fusión. Solo sé de C#, y definitivamente no es C#, lo más probable es que sea PHP. C# no permitirá la asignación dentro de un si condicional. – Tesserex

3

En un lenguaje fuertemente tipado que no equivale a 0/nulo a falso y no 0/no nulo a tr Debo decir que probablemente sea seguro, pero marginalmente menos legible en el caso general, donde los nombres de los métodos y el número de parámetros pueden ser más grandes. Personalmente lo evitaría, excepto en ciertos modismos estándar, en los casos en que 0/null equivale a falso y no 0/no nulo a verdadero simplemente por el peligro potencial de confundir la asignación con la comprobación de igualdad al leer el código. Algunas expresiones idiomáticas en lenguajes de tipo débilmente, como C, son tan penetrante que no tiene sentido para evitarlos, .e.g,

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

puedo pensar en dos alternativas.

La primera es la única permitida en lenguas como la suya (PHP?), Donde = individuales en un condicional está bien.

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

Aclara que estás haciendo una comparación y que el single = no es un error.

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

Al igual que su segundo ejemplo, pero se deshace de la fea anidación adicional.

0

Para contestar a su pregunta directamente: es por lo general mala forma de tener efectos secundarios en sentencia condicional.

Como solución, puede almacenar los constructores de frutas en una matriz y encontrar el primer constructor, que devuelve un valor no nulo (pseudocódigo):

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

es como un operador nulo se unen, pero más generalizado . En C#, lo que probablemente utilizar LINQ de la siguiente manera:

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

Al menos de esta manera se puede pasar sus constructores por ahí como una clase de fábrica, en lugar de que sean difíciles de codificación con el operador nulo se unen.

1

en C o C++, se podría escribir:

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

La razón para evitar tanto este como su primera versión no es "el dogma de las estructuras básicas", es que la asignación por sí solo en una condición se confuso. No todos los idiomas lo admiten, por un lado. Por otro, es fácilmente malinterpretado como ==, o el lector puede no estar seguro de si realmente lo quiso decir, o tal vez tenía la intención de ==. Agregar != 0 a cada condición se vuelve bastante denso y prolijo.

GCC tiene una extensión para permitir:

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

Lo mismo puede lograrse a menudo con || o or (pero no en C o C++).

Otra posibilidad es:

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

Esto va aún mejor en un lenguaje donde se puede break de un bloque básico, que se puede con last en Perl, ya que se puede prescindir de la do/while(false). Pero probablemente solo a los programadores de montaje realmente les gustará.

0

No creo que nadie haya mencionado aún que la primera versión puede ser un poco difícil de solucionar en un depurador. La diferencia en legibilidad es discutible, pero en general, evito asignaciones y llamadas a funciones en condicionales para facilitar el rastreo a través de la ejecución, cuando sea necesario (incluso si nunca necesito depurarlo, alguien más puede necesitar modificar el código posteriormente)

Cuestiones relacionadas