¿Qué estructuras de control alternativas útiles conoces? [cerrado]

12

Pregunta similar se cerró en SO.

A veces, cuando estamos programando, encontramos que alguna estructura de control particular sería muy útil para nosotros, pero no está directamente disponible en nuestro lenguaje de programación.

¿Qué estructuras de control alternativas crees que son una forma útil de organizar la computación?

El objetivo aquí es obtener nuevas formas de pensar sobre la estructuración del código, a fin de mejorar la fragmentación y el razonamiento.

Puede crear una sintaxis / semántica deseable que no esté disponible ahora o citar una estructura de control menos conocida en un lenguaje de programación existente.

Las respuestas deben dar ideas para un nuevo lenguaje de programación o mejorar un lenguaje real.

Piense en esto como una lluvia de ideas, así que publique algo que piense que es una idea loca pero que puede ser viable en algún escenario.

Se trata de una programación imperativa.

Maniero
fuente
1
¿Por qué esta pregunta tiene que ser sobre "programación imperativa"?
missingfaktor
@missingfaktor: Porque las estructuras de control se trata de una programación imperativa. Claro, algunos problemas pueden resolverse con un enfoque funcional u otra forma, pero otros paradigmas no ponen las estructuras de control en el centro del desarrollo. Pero de todos modos puedes ser creativo.
Maniero
3
Bueno, iba a compartir patrones de coincidencia con los guardias, ya que son más generales que las declaraciones de casos y If-Thens, pero si esta es una zona sin FP, ¡supongo que es solo más patrones de coincidencia para el resto de nosotros!
CodexArcanum
@CodexArcanum: Bueno, Pattern Matching es una construcción muy similar a las construcciones imperativas. De hecho, Pattern Matching en sí es una alternativa a las estructuras de control imperativas. No seas tímido ;-)
Maniero
Mirar todas las respuestas me alegra de que ninguna de las propuestas exista en la vida real (y con suerte nunca existirá). Lo siento :)
serg

Respuestas:

14

OK, esta es una pregunta divertida.

También me gustaría tener un general elsepara while y para bucles, para cuando la condición no es cierta en la primera prueba:

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

Esto evita el recálculo incómodo de la condición o el almacenamiento en una variable.

Macneil
fuente
Python tiene esto.
Barry Brown el
¡Oye! ¡Finalmente una elección peculiar de Python con la que estoy de acuerdo! ;-)
Macneil
55
No. La cláusula else en Python for / while loops se ejecuta si el ciclo finaliza normalmente, lo que significa que el ciclo no termina a través de una declaración de interrupción o retorno o una excepción. En los bucles for, la clase else se ejecuta después de que se agota la secuencia de elementos que se repite, y en los bucles while se ejecuta después de que la condición del bucle se evalúe como False por primera vez.
pillmuncher
44
Hay una solicitud para agregar la while ... elseconstrucción de la forma en que Macneil la describe a PHP. Creo que es una gran idea, porque "aquí están los resultados / no hubo resultados" es un idioma bastante común en las aplicaciones web.
Dean Harding
1
@SnOrfus: quiere un bloque de código (separado del bloque del bucle) que se ejecuta SOLO si la condición while nunca se cumple. Do-While ejecuta el bloque de código del bucle (no un bloque separado) una vez, independientemente del condicional while.
Legión
10

¿Por qué no mezclar algunas respuestas en una?

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.

Una sintaxis de construcción de control de flujo generalizada y extensible en un lenguaje imperativo sería bastante útil y entretenida. Hasta que aparezca, supongo que solo usaré Lisp o algo así.

Jon Purdy
fuente
55
No es útil, confuso. No entretenido, lento.
Josh K
2
@ Josh K: Sí, tienes razón. ¿Qué, pensaste que no estaría de acuerdo? Esta respuesta es morderse la lengua para no reírse.
Jon Purdy
Si fue por ironía, ¡ciertamente tuvo éxito!
Josh K
11
+1 Me hizo reír. Necesita una cláusula "entre", para que el código se ejecute entre iteraciones.
Barry Brown el
1
+1, pero también necesita un seventh { ... }.
j_random_hacker
7

Estructuras de control como funciones.

Quiero for, if, else, while, etc que son funciones, no estructuras especiales.

Quiero return, try/excepty gotoser derivados de continuaciones.

Por supuesto, esto tiene menos que ver con una estructura de control particular y más con la forma en que ve las estructuras de control en general, las estructuras meta de control.

dietbuddha
fuente
2
Las continuaciones son un concepto pesado para un idioma, y ​​hay buenas razones para dejarlas fuera de muchos idiomas.
David Thornley
No estoy en desacuerdo; teniendo for, if, else, y whilecomo las funciones que me hace inferir que los parámetros a esas funciones deben ser ejecutados con pereza con el fin de comportarse de la misma que las construcciones originales. Tener la capacidad de hacer una ejecución perezosa sería bueno.
Aprendiz del Dr. Wily
1
@David: Solo porque estén allí no significa que tengas que usarlos. Los programadores regulares pueden usar lo que están acostumbrados return, excepciones, etc. Pero ahora el programador experto tiene una poderosa herramienta adicional en su caja de herramientas si surge la necesidad. Además, los idiomas de mi humilde opinión no deberían ser "tontos". Los idiomas deben tener la capacidad de soportar una mayor experiencia y el crecimiento del conocimiento de CS.
dietbuddha
1
@dietbuddha: (cont.) No digo que las continuaciones sean malas, digo que son pesadas, y hacerlas utilizables tiene implicaciones para el lenguaje, algo así como las excepciones. Además, es probable que las continuas forcen cambios en la forma en que las personas usan el lenguaje, nuevamente como excepciones en C ++. Un lenguaje que admita las continuaciones será diferente de uno que no lo haga, tanto en buenos como en malos.
David Thornley
1
@ Steve314 - longjmp no es una continuación, aunque hay similitudes. Las continuaciones guardan todo el estado (todos los locales, globales y la pila). longjmp guarda un puntero de pila, por lo que si escapa más allá del alcance donde se usó setjmp, obtendrá un segfault ya que el marco ya no existe. Creo que también hay algunas limitaciones en las variables que guarda.
dietbuddha
6

El artículo vinculado definitivamente lo hace bien sobre los N + 1/2 Loops de Donald Knuth . Expresado en C / C ++ / Java:

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

Esto es útil para leer líneas o caracteres de un archivo, probar si ha alcanzado EOF y luego procesarlo. Estoy tan acostumbrado a ver for(;;)..if(..)break;aparecer el patrón que es idiomático para mí. (Antes de leer el artículo de Knuth, reimpreso en el libro Literate Programming , este solía ser un "wtf?").

Knuth sugirió las palabras clave loop/while/repeat:

loop:
  S;
while C:
  T;
repeat

Donde Sy Tson marcadores de posición para una serie de cero o más declaraciones, y Ces una condición booleana. Si no hubiera una Sdeclaración, entonces sería un ciclo while, y si no hubiera una Tdeclaración, sería un ciclo do.

Esta construcción en sí misma podría generalizarse permitiendo cero o más while Ccláusulas, haciéndola perfecta para expresar bucles infinitos y luego algunas condiciones más raras que necesitarían dos comprobaciones.

En el mismo artículo, Knuth sugirió un mecanismo de señalización que sería una versión local de lanzar / atrapar excepciones (como alternativa al uso de goto).

¿Para mi? Deseo que Java sea compatible con la optimización de llamadas de cola, para poder expresar cualquier estructura de control general según sea necesario.


Actualización: Olvidé mencionar que muchos programadores de C / C ++ / Java eluden este usando una asignación incrustada en la condición de while:

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

El uso de los términos de constructo de Knuth, esto es admisible cuando Sy Cse pueden combinar en una sola expresión. Algunas personas odian ver la tarea incrustada arriba, mientras que otras odian verla breaken lo for (;;)anterior. Pero cuando Sy Cno se pueden combinar, como cuando Stiene múltiples declaraciones, esta for (;;)es la única alternativa sin repetir el código. La otra alternativa es simplemente duplicar el Scódigo:

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

La loop/while/repeatalternativa de Knuth parece mucho mejor.

Macneil
fuente
Ese ciclo mientras se repite se parece mucho al repeat-untilciclo de Pascal .
Mason Wheeler
@Mason: la diferencia es que hay dos lugares donde puede insertar declaraciones más su condición, no solo una.
Macneil
Oh, ya veo lo que está pasando. Sí, eso es interesante ...
Mason Wheeler
ANSI Basic tenía un do while ... loop until: tanto la precondición como la poscondición son opcionales, y recuerdo (vagamente) usar ambas en un bucle. Para su condición media, está el Ada exit when ...;. La ventaja clave if ... break;es que puede escribir exit loopname when ...;para salir de múltiples (pero no necesariamente) bucles anidados a la vez. Probablemente un poco más visible que ese descanso también.
Steve314
Cosa graciosa. Ada tiene exactamente esa característica.
John R. Strohm
6

El lenguaje BCPL tenía una valueofexpresión que podía usarse para convertir una secuencia de declaraciones en una sola expresión:

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

Donde some series of statementspuede ser cualquier cosa y todo se valueofevalúa v.

Esto podría ser útil en Java para cuando necesite calcular un argumento para llamar a this()o super()(lo que requiere que no suceda nada antes). Por supuesto, podría escribir un método separado, pero eso podría ser un problema si necesita pasar muchos valores locales para el contexto.

Si puede usar las finalvariables que necesita, ya puede hacer un valueofen Java utilizando clases internas anónimas:

foo(a, b, new Object(){String valueof(){
    String v ...; some series of statements; return v;}}.valueof());
Macneil
fuente
1
GCC tiene una extensión para esto - ({ statement1; statement2; ...; result-expr; }). También he visto cosas similares en otros lugares, pero no recuerdo dónde. Probablemente todo copiado de BCPL, sin embargo.
Steve314
6
unless(condition) {
  // ...
}

hace lo mismo que:

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

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

hace lo mismo que:

do {
  // ...
} while(!condition)
missingfaktor
fuente
Es posible que desee ir a Lisp ...
duros
1
O Ruby, tiene una sintaxis similar para unless.
Josh K
2
Parece bastante Perlish. Hay más de una forma de hacerlo.
2
@ Steve314 Debe usar un estilo de llave incorrecto . ;-)
Orbling
1
@Orbling - en absoluto. Todos los demás usan un estilo de llave incorrecto.
Steve314
5

En una nota diferente, me gustaría ver un mejor soporte para iteradores en lenguajes de programación. En particular, para cuando quieres bajar dos colecciones en pares :

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

Es posible que algunos lenguajes dinámicos ya tengan esto, o que sean fácilmente compatibles a través de bibliotecas y macros, pero creo que esto está en el espíritu de su pregunta.

Si los dos conjuntos no son del mismo tamaño, podría generar una excepción o podría tener un elsebucle posterior para indicar que hubo una diferencia en los tamaños.

Naturalmente, podría generalizar esto para bajar tres o más listas.


Actualización: También sería útil hacer el producto cartesiano entre iterables:

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

que no sería más que bucles anidados:

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

Me preocupa un poco que entre las dos notaciones que he proporcionado aquí haya una diferencia de O (n) y O (n ^ 2) en el número de pares, con solo el cambio de un solo carácter.

Macneil
fuente
2
En Python hay zip y zip_longest que hacen esto.
pillmuncher
Genial, tal vez estoy subestimando a Python y debería darle una segunda mirada después de muchos años. Eso me recuerda que a veces también quieres el producto cartesiano, el equivalente de los bucles anidados.
Macneil
1
Ambos casos en Scala: paste.pocoo.org/show/297429
missingfaktor
1
En referencia al producto cartesiano: Nuevamente, Python lo tiene. for a, b, c in itertools.product(iter1, iter2, iter3):le da el producto cartesiano evaluado perezosamente. ¿Que es eso? ¿Quieres permutaciones y combinaciones de un iterador dado también? itertools.permutations, itertools.combinations.
aaronasterling
1
Perspectiva de Haskell: use "zipWith" para el primer caso y la comprensión de la lista o la mónada Lista para el segundo. Como los ejemplos de Python / Scala, pero más elegante. :)
LennyProgrammers
5

Existe el llamado "bucle de Dijkstra" (también llamado "bucle guardado de Dijkstra"). Se definió en The Guarded Command Language (GCL) . Puede encontrar información sobre la sintaxis y la semántica en el artículo de Wikipedia anterior en la sección 6 Repetición: do .

Hoy en día conozco un lenguaje de programación que soporta esta estructura de control directamente. Es Oberon-07 (PDF, 70 KB). Y es compatible con "Dijkstra's Loop" en tu forma de declaración while. Echa un vistazo a la sección 9.6. Mientras que las declaraciones en el PDF anterior.

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

PD: Esta es una copia de mi respuesta SO .

Gato montés
fuente
Parece que el verificador de modelos Spin también tiene esta construcción, con una semántica idéntica y una sintaxis básicamente idéntica.
j_random_hacker
4

Expresiones de estilo de icono con retroceso incorporado.

Python obtiene muchos de los beneficios del generador de íconos, y en general funciona mejor, IMO. Y, en principio, el retroceso fue solo una especie de lanzamiento de excepción, pero fue la simplicidad de las expresiones el equivalente aproximado de ...

x = (a / b) else c;

para manejar casos de falla como la división por cero.

Donde Icon se volvió loco: sin operadores de comparación de retorno booleano. Las comparaciones siempre tuvieron éxito o provocaron un retroceso, y hubo otro problema semántico que ahora estoy tratando desesperadamente de recordar que ... bueno, digamos que probablemente sea más reprimido que olvidado.

Siempre pensé que deberían tener una ifexpresión sin otra parte ( if (condition, success-value)tipo de cosa, retroceder si la condición vuelve falsa) y descartar las extrañas comparaciones.

EDITAR Recuerdo, obviamente obvio. Una comparación con dos argumentos tiene éxito o falla: no calcula un nuevo valor para devolver. Por eso, cuando tiene éxito, lo que hace que vuelva? Respuesta: uno de los argumentos. Pero si escribes a > b, ¿cuál es el argumento lógico para volver, ao b? ¿Y qué pasa si escribes en su b < alugar? Yo creo que siempre se devuelve el argumento de la derecha, que tiene tanto sentido como cualquier cosa, pero todavía por lo general parecía que el argumento mal a mí.

Steve314
fuente
4

Esta es solo una idea general y una sintaxis:

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

TAMBIÉN la condición siempre se evalúa. ELSE funciona como de costumbre.

Funciona para el caso también. Probablemente sea una buena forma de eliminar la declaración de ruptura:

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

se puede leer como:

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

No sé si esto es útil o simple de leer, pero es un ejemplo.

Maniero
fuente
3

Continuar Pasando Estilo viene a la mente. Entonces, por supuesto, también le gustaría tener la Optimización de llamadas de cola .

Pillmuncher
fuente
1
No soy un gran admirador de esto, y no creo que sea realmente una "estructura de control" sino más bien un componente básico en lenguajes funcionales y estilo de programación. Sin embargo, Node.js parece bastante enganchado.
Josh K
1
Por supuesto, es una estructura de control. Simplemente no es uno que viene con una palabra clave como 'if' o 'for', sino como un patrón para estructurar el flujo de control (por lo tanto, estructura de control). Incluso es utilizado detrás de escena por muchos compiladores. Y tampoco se limita a los FL. Sin embargo, necesita funciones como objetos de primera clase.
pillmuncher
1
Obtiene la optimización de llamadas de cola en C y C ++ en estos días, pero en mi opinión, no pasa nada. El punto es que es una optimización. En Scheme, una llamada de cola genuina es obvia. Especialmente en C ++, muchas cosas pueden significar que su llamada de cola no es una llamada de cola. Y el desbordamiento de pila significa que su aplicación está rota. En mi opinión, debería haber algo así como una goto return ...;declaración, haciendo explícita la intención de llamar a la cola, por lo que si el compilador no puede hacerlo iterativo, es un error.
Steve314
1
@Macneil: que sé con certeza, la optimización de la cola se realiza en GCC, Clang y Visual C ++. GCC tiene conversiones más sofisticadas de recursión a iteración, que pueden manejar una serie de casos que no son recursividad de cola. Pero muchas cosas pueden salir mal. Pase un puntero a una variable local en esa llamada de cola y el marco de la pila no se puede eliminar, ya que la variable debe mantenerse viva. En C ++, los destructores de variables locales normalmente sucederán después de que la llamada "de cola" regrese, lo que significa que no es una llamada de cola.
Steve314
1
@ Mike: ese es mi punto. Si usa un estilo de codificación recursivo, sin la cola llamada "optimización" su código está potencialmente roto, ya que el desbordamiento de la pila es un error. En C ++ es una optimización: para que el compilador haga optimizaciones para que no tenga que hacerlo, está bien, pero no puede confiar en ellas. Si desea la libertad de escribir en un estilo recursivo, pero no quiere preocuparse por los problemas de profundidad de la pila, la eliminación de la cola no es la optimización, es un problema de corrección. Si la recursividad es la mejor manera de codificar algo, debería poder hacerlo, sin tener que preocuparse por el desbordamiento de la pila.
Steve314
3

Ramificación de subprocesos sin problemas, tiene una sintaxis como una función, pero se ejecuta en un subproceso separado y no puede acceder a los datos que inicialmente no se le han pasado.

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

Cuando se llama a una rama, inmediatamente devolverá un identificador.

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

El identificador se puede usar para verificar si la tarea está hecha.

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

Si se solicita el resultado antes de que se complete la ejecución, el hilo principal simplemente esperará.

[result, storage]=handle.result()

Esto no cubriría los escenarios de subprocesos múltiples más avanzados, sino que proporcionaría una forma fácilmente accesible de comenzar a utilizar múltiples núcleos.

aaaaaaaaaaaa
fuente
Eche un vistazo a Cilk, es una extensión muy limpia y simple de C: en.wikipedia.org/wiki/Cilk . No sé si hay alguna handle.finished()prueba, pero spawny syncson todo lo que necesita para el 90% de las tareas de programación paralela.
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

Los bloques FIRST y THEN se ejecutan si alguno de los 3 condicionales se evalúa como verdadero El primer bloque se ejecuta antes del bloque condicional y ENTONCES se ejecuta después de que se haya ejecutado el bloque condicional.

ELSE escritura condicional o final después de la declaración FIRST y THEN son independientes de estos bloques.

Se puede leer como:

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

Estas funciones son solo una forma de leer. No crearían alcance. Es más como un gosub / return de Basic.

Utilidad y legibilidad como tema de discusión.

Maniero
fuente
2

A veces me encuentro escribiendo un bucle que necesita hacer algo diferente durante la primera iteración. Por ejemplo, mostrar etiquetas <th> en lugar de etiquetas <td>.

Manejo esta situación con una bandera booleana. Algo como esto:

first = true

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

Parece una tontería comprobar el valor de firstcada iteración cuando será falso la mayor parte del tiempo.

Me gustaría tener una opción de bucle para especificar un cuerpo de bucle diferente en la primera iteración. No habría necesidad de una variable separada. El código compilado tampoco necesitaría uno, porque el código generado tendría dos cuerpos, uno para la primera iteración y otro para el resto.

Barry Brown
fuente
La pregunta SO tiene una idea similar, pero con un operador "entonces" que se utilizará en los valores. De esa manera, probablemente no tenga código duplicado. Por ejemploprint(out, first "<th>" then "<td>")
Macneil el
1
La mejor manera es iniciar la iteración +1.
Josh K
1
El caso común es un bucle con el manejo entre, por ejemplo, enumerar elementos con un separador de coma. Estrictamente, eso es if (!first) gimme-a-comma ();, pero casi lo mismo. Sin embargo, tendría una objeción: si lo envuelve adecuadamente termina con cosas como el método de combinación de cadenas de Python, sin embargo, a menudo necesita el patrón básico, el bucle subyacente no necesita reescribirse con tanta frecuencia.
Steve314
Como dice Josh, por supuesto, sacar el primer elemento del bucle es válido. En el caso del separador de comas significa código duplicado, pero puede ser una llamada de función duplicada. Personalmente, prefiero la ineficiencia de los if (!first), pero los micro optimizadores pueden plantear objeciones de predicción de rama.
Steve314
1
@Barry Brown: Desenrollar la primera iteración del bucle puede o no ser más rápido que verificar una bandera booleana. Un predictor de bifurcación decente predecirá mal en el peor de los casos las primeras 2 iteraciones, predigo :) Preferiría usar if (!first)y dejar que un compilador de optimización decida si el cuerpo del bucle es lo suficientemente pequeño como para desenrollarlo y deshacerse de él firstes una ganancia neta.
j_random_hacker
1

[copiado de mi propia respuesta en stackoverflow]


ignoring - Para ignorar las excepciones que ocurren en un determinado bloque de código.

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

Con una construcción de control ignorante, puede escribirla de manera más concisa y más legible como:

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

[Scala proporciona esto (y muchas otras construcciones de control de manejo de excepciones) en su biblioteca estándar, en el paquete util.control. ]

missingfaktor
fuente
44
Las excepciones no deben ser ignoradas.
Josh K
1
Excepto que, en contexto, una excepción puede no ser un error.
Steve314
1
@Josh, @Steve: hay casos en los que desea ignorar las excepciones. Vea este hilo para algunos de estos casos.
missingfaktor
Una excepción es una alerta de que algo puede estar mal. Un try..catchbloque vacío es una cosa; te obliga a reconocer el error e ignorarlo deliberadamente; Si bien arrojar fragmentos de código bajo una omisión global puede generar problemas cuando se lanzan esas excepciones, así como generar malos hábitos de programación.
Josh K
@ Josh: acabo de eliminar dos comentarios porque no estaba pensando bien, pero hay muchas esperanzas para este. Si esa declaración es global, probablemente esté de acuerdo, pero a mí me parece una estructura de bloques. IOW es como un trybloque, excepto que enumera las excepciones que detecta e ignora por adelantado, en lugar de más adelante. Eso incluso podría ser una ventaja de legibilidad, por ejemplo, dejarle saber al lector que un archivo faltante no es un error incluso antes de leer la llamada abierta.
Steve314
1

En lugar de:

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

Hazlo en Python, o ahora también en C #:

action = val2func[myEnum]
action()

Scala tiene muchas características nuevas.

Finalmente, lenguajes como Clojure pueden ampliarse para proporcionar la funcionalidad adicional.

Trabajo
fuente
1
C puede hacer esto también. Y Pascal. Probablemente todos esos viejos idiomas de los años 70/80/90. Una matriz de funciones se convierte en una matriz de punteros de función, pero eso no es gran cosa. Donde se hace más fácil es cuando tienes funciones anónimas - ya sabes, como en Lisp, ML, ...
Steve314
Steve314 - corrección: este es un ejemplo de un diccionario que asigna cualquier valor a una función. Aquí es donde las cosas se vuelven un poco más limpias que la mayoría de las cosas de los años 70/80/90.
Trabajo
1

Tengo dos ideas

A menudo encuentro que me estoy repitiendo en catchbloques. Esto puede ser de alguna manera ayudado a través de métodos de extracción, pero eso puede causar desorden innecesario si los métodos son muy cortos o no son dignos de un método. Por lo tanto, sería bueno anidar catchbloques:

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
 }

En la programación web, a menudo me encuentro haciendo algo como esto (este no es un ejemplo real, así que no analice casos ficticios):

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

En Javascript (bueno, ECMAScript, y tal vez otros con los que no estoy familiarizado), ya que cualquier valor puede evaluarse como una condición, ||puede ayudar.

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

Realmente me gusta cómo se ve, y deseo que más idiomas, especialmente algunos en el lado del servidor, tengan soporte para ello. (PHP puede evaluar cualquier cosa como condición, pero convierte toda la expresión condicional en un valor booleano en lugar de preservar el valor. No sé acerca de Python o Ruby.) Podría volverse difícil de manejar después de tres casos más o menos, pero si tiene más de tres casos, también puede tener un mal diseño de software.

Nicole
fuente
Python hace algo como tu || evaluación, pero cuando finalmente obtuvo su x if c else ysintaxis de expresión condicional , una razón importante que sucedió fue porque muchas expresiones que usaban esta semántica tenían errores ||y &&eran sutilmente defectuosas. IIRC, un caso común era un valor (como cero) que era válido para la aplicación que se estaba tratando false, por lo que se descartaba para que se utilizara una reserva no válida. Sin embargo, vea mi respuesta WRT, el lenguaje de programación Icon, que puede tener expresiones como get1() else get2() else default.
Steve314
1

El interruptor generalizado ha dicho anteriormente:

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

En Haskell:

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

fuente
0

En C # me gustaría usar simples switch () { ... }, pero extensibles con tales expresiones:

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;

}

Y así. Lo mismo con números u otros tipos (que admite IComparable,IConvertible , ...)

Esto podría hacer que mi código sea más lacónico y legible.

Genio
fuente
La caída en los casos es un mal conocido, y generalmente estoy bien sin él. Y saltos es demasiado GOTOish para cualquier programación sensata. Pero tener expresiones y no estáticas en el caso sería una adición encantadora.
CodexArcanum
0

Es una pregunta divertida, como dijo @Macneil.

Mi estructura de control inusual favorita, que descubrí (humilde tos), es la ejecución diferencial .

Tiene ciertos usos. Para mí, el uso abrumador es en la programación de interfaces de usuario, que es una instancia del problema más general de mantener datos redundantes en correspondencia. Por un lado, hay datos de aplicaciones y, por otro, hay controles de interfaz de usuario que deben mantenerse de acuerdo. Esto suena como "vinculante", pero en realidad hay mucho más.

Por lo general, lo implemento por macros en C o C ++. En C # tengo que hacerlo mediante declaraciones de expansión manual. Eso es un dolor, pero funciona.

Una vez que lo implementé en términos de macros Lisp, y luego estaba muy limpio. No requirió cuidado por parte del programador. Podría haber hecho lo mismo en cualquier otro lenguaje estructurado si me hubiera tomado la molestia de escribir un analizador completo y luego generar todas las cosas correctas. Ese es un gran proyecto, y no lo he hecho.

Mike Dunlavey
fuente
0

Las estructuras de control "tradicionales" fortienen que ver con el control del trabajador, manteniéndolo subordinado a las ideologías corruptas de la élite capitalista gobernante. Es por eso que uso estructuras de control alternativas como en su ph0rlugar. Es como for, pero más radical: no te sorprenderá ph0rusar traje y corbata, escupiendo algunas BS corporativas. ph0rlo mantiene real, hombre.

¡Luchar contra el poder!

j_random_hacker
fuente
0

forBucle más simple

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
}
Gulshan
fuente