none
¿Alguna forma de escribir este código más compacto sin perder eficiencia?

    Pregunta

  • Este es el código:

        For nI = 1 To 50
          estRecurso = Recurso(nI)
          If estRecurso.Id <> Nothing Then lisRecursos.Add(estRecurso)
        Next

    Recurso(nI) es una función que devuelve un elemento cuyo tipo de datos es una pequeña estructura de dos elementos y lo asigna a estRecurso. Luego se comprueba si el campo Id no es nothing y si no lo es agrega el elemento tipo estructura a lisRecursos.

    Lo anterior también se puede escribir así:

        For nI = 1 To 50: If (Recurso(nI).Id <> Nothing) Then lisRecursos.Add(Recurso(nI)) Next

    Pero se pierde eficiencia porque Recurso(nI) se calcula dos veces.

    Me parece que se puede agregar un elemento a la lista con lisRecursos.Agregate u otro método de lista que permita escribir el primer código en una sola línea pero con la misma o mejor eficiencia.




    viernes, 6 de enero de 2017 3:21

Respuestas

  • "James2016-2" preguntó:

    ¿Por casualidad eres el usuario James-2016, sin el -2? Voy a responderte como si lo fueras, pero si no es así, te pido disculpas de antemano.

    > Lo anterior también se puede escribir así:
    >
    >    For nI = 1 To 50: If (Recurso(nI).Id <> Nothing) Then lisRecursos.Add(Recurso(nI)) Next
    >
    > Pero se pierde eficiencia porque Recurso(nI) se calcula dos veces.

    No recuerdo si en otra ocasión te he comentado que una cosa es lo que tu escribas y otra muy diferente lo que posteriormente haga el compilador de Visual Basic con el código que tú has escrito.

    Utilices un bucle For ... Next como un bloque, o en una sola línea, al final el compilador de Visual Basic te va a generar "algo diferente", que en definitiva será el código que finalmente se ejecutará.

    Si por ejemplo tienes este bucle:

    >    For nI = 1 To 50
    >      estRecurso = Recurso(nI)
    >      If estRecurso.Id <> Nothing Then lisRecursos.Add(estRecurso)
    >   Next

    El compilador te va a generar "algo parecido" a lo siguiente:

        Dim nI As Integer = 1
        Do
            estRecurso = Recurso(nI)
            If (Not estRecurso.Id Is Nothing) Then
                lisRecursos.Add(estRecurso)
            End If
            nI += 1
        Loop While (nI <= 50)

    Y es igual de eficiente que el bucle For ... Next que tú has escrito, aunque observes que tiene un "sinfín" de líneas. Así que, si no deseas llamar dos veces a la función Recurso, no le des más vueltas al asunto y utiliza el bucle For ... Next como bloque.

    Obviamente, si en tú código llamas dos veces a la función Recurso, el código generado por el compilador también va a realizar dos veces la llamada a dicha función.

    Escribo que es "algo parecido" porque en realidad todos los compiladores .NET (incluido el compilador de Visual Basic .NET) lo que te va a generar es unas 30 líneas, más o menos, para ejecutar ese bucle For ... Next con código perteneciente al Lenguaje Intermedio de Microsoft, más conocido como código MSIL o simplemente IL, por lo que es el compilador el que establece la sintaxis que se debe utilizar en el código, y no lo que nosotros hemos escrito:

    Proceso de ejecución administrada

    > Me parece que se puede agregar un elemento a la lista con lisRecursos.Agregate

    No sé yo si la función Agregate(Of T) te va a servir para añadir elementos a una supuesta lista genérica.

    Y aparte, también te comento que con las expresiones lambda, o donde se utilice LINQ, puede parecer que se ejecuta muy poco código (una línea, cuatro líneas como mucho), pero después, el compilador también hará con la expresión lambda lo que estime conveniente, y esas dos, tres líneas que has escrito, lo mismo te las convierte en 200 líneas de lenguaje MSIL, tal y como te he comentado anteriormente.

    La verdad es que observo en tus preguntas una cierta "obsesión" con el número de líneas que se ejecutan, o si éstas son muy grandes o pequeñas. Yo que tú me limitaría a escribir un código eficiente, pero por eficiente, no quiere decir que todo se tenga que ejecutar en dos líneas.

    Groso modo, un código eficiente es el que hace uso de procedimientos y funciones para no tener que estar repitiendo el mismo código en diferentes procedimientos, y suele realizar las llamadas a los métodos adecuados para llevar a buen término el trabajo que se le ha encomendado, almacenando los valores intermedios en variables declaradas con el tipo de dato adecuado, y desechando éstas cuando sus valores ya no sean necesarios, sin importar el número de líneas que ocupe el código fuente escrito, siempre y cuando éstas sean las estrictamente necesarias: ni una más pero tampoco ni una menos. ¡Fíjate que sencillo! ;-)

    > Recurso(nI) es una función que devuelve un elemento cuyo tipo de datos es una pequeña estructura
    >
    > estRecurso = Recurso(nI)
    > If estRecurso.Id <> Nothing Then lisRecursos.Add(estRecurso)

    Ignoro el tipo de dato que tiene el elemento Id dentro de esa estructura que comentas que devuelve la función Recurso. Si por casualidad es un tipo de dato numérico (Integer, Long, Decimal, etc.), te comento que dichos tipos de datos son estructuras (una declaración Structure), lo que significa que son tipos de datos de valor y por tanto su valor NUNCA será Nothing: será 0, si son tipos de datos numéricos.

    Diferente sería si el elemento Id es del tipo String, Form1, DataTable, MiClase (una declaración Class), en cuyo caso estaríamos ante un tipo de dato de referencia, y por tanto el valor sí puede ser Nothing.

    Te lo comento para que lo tengas en cuenta a fin de no obtener resultados inesperados:

    Tipos de valor y tipos de referencia


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.


    viernes, 6 de enero de 2017 7:51
    Moderador
  • Enrique ya te ha comentado que en cuanto a eficiencia en tiempo de ejecución, tu primer ejemplo con dos líneas es perfectamente eficaz una vez compilado. Pero si por "eficiencia" te refieres a "mínimo esfuerzo al teclear el código fuente", para poderlo escribir con el mínimo número de líneas, entonces tienes esta opción:

    Haz que tu variable lisRecursos en lugar de ser un List(Of loquesea) sea una clase que hereda de List(Of loquesea). Y dentro de esa clase, añade un método que se llame "AgregarSiNoEsNothing" que obviamente por dentro hará algo así como "If Not argumento is Nothing Then Me.Add(argumento)". Una vez hecho eso, desde fuera puedes manejarlo con un bucle como este:

     For nI = 1 To 50: lisRecursos.AgregarSiNoEsNothing(Recurso(nI)): Next

    • Marcado como respuesta James2016-2 viernes, 6 de enero de 2017 15:29
    viernes, 6 de enero de 2017 8:30
  • James2016-2,

    ¿Tienes alguna limitante de líneas en tu editor de código? (no lo tomes a mal, ayudará a entender)

    Es bueno que tengas claro que escribir menos líneas no tiene relación directa con que el resultado sea un código óptimo o eficiente, concéntrate en escribir lo necesario y como buena práctica escribe código legible, que permita fácil comprensión y mantenimiento o refactorización, no escribas código que sólo tu entenderás. Evita que alguien que lea tú código diga: ¿y ahora que quiso hacer aquí?. Como bien mencionó Enrique, el código óptimo o eficiente tiene que ver con otras cosas como reutilización, encapsulación, definición correcta de tipos, niveles de ámbito de variables, etc., sería un error si descuidas esos puntos -y otros realmente importantes- pero tienes tu código "bien compactadito", ¿leíste acerca de la métrica complejidad ciclomática que te propuse en un hilo anterior?

    No digo que lo que tienes ahora es altamente complicado de entender, hago mención para que no llegues a los extremos de agregar complejidad al código por ahorrarte pulsaciones de tecla o línea de código. Fíjate algo, ¿la función AgregarSiNoEsNothing() la reutilizarás? ¿o la escribirás únicamente para cumplir el capricho de escribir menos líneas?. Si la reutilizarás es correcto pero si sólo sirve para el caso puntual no le veo sentido además que si sumas todas las líneas escritas caes en lo mismo.



    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    • Marcado como respuesta James2016-2 viernes, 6 de enero de 2017 18:32
    viernes, 6 de enero de 2017 15:55

Todas las respuestas

  • "James2016-2" preguntó:

    ¿Por casualidad eres el usuario James-2016, sin el -2? Voy a responderte como si lo fueras, pero si no es así, te pido disculpas de antemano.

    > Lo anterior también se puede escribir así:
    >
    >    For nI = 1 To 50: If (Recurso(nI).Id <> Nothing) Then lisRecursos.Add(Recurso(nI)) Next
    >
    > Pero se pierde eficiencia porque Recurso(nI) se calcula dos veces.

    No recuerdo si en otra ocasión te he comentado que una cosa es lo que tu escribas y otra muy diferente lo que posteriormente haga el compilador de Visual Basic con el código que tú has escrito.

    Utilices un bucle For ... Next como un bloque, o en una sola línea, al final el compilador de Visual Basic te va a generar "algo diferente", que en definitiva será el código que finalmente se ejecutará.

    Si por ejemplo tienes este bucle:

    >    For nI = 1 To 50
    >      estRecurso = Recurso(nI)
    >      If estRecurso.Id <> Nothing Then lisRecursos.Add(estRecurso)
    >   Next

    El compilador te va a generar "algo parecido" a lo siguiente:

        Dim nI As Integer = 1
        Do
            estRecurso = Recurso(nI)
            If (Not estRecurso.Id Is Nothing) Then
                lisRecursos.Add(estRecurso)
            End If
            nI += 1
        Loop While (nI <= 50)

    Y es igual de eficiente que el bucle For ... Next que tú has escrito, aunque observes que tiene un "sinfín" de líneas. Así que, si no deseas llamar dos veces a la función Recurso, no le des más vueltas al asunto y utiliza el bucle For ... Next como bloque.

    Obviamente, si en tú código llamas dos veces a la función Recurso, el código generado por el compilador también va a realizar dos veces la llamada a dicha función.

    Escribo que es "algo parecido" porque en realidad todos los compiladores .NET (incluido el compilador de Visual Basic .NET) lo que te va a generar es unas 30 líneas, más o menos, para ejecutar ese bucle For ... Next con código perteneciente al Lenguaje Intermedio de Microsoft, más conocido como código MSIL o simplemente IL, por lo que es el compilador el que establece la sintaxis que se debe utilizar en el código, y no lo que nosotros hemos escrito:

    Proceso de ejecución administrada

    > Me parece que se puede agregar un elemento a la lista con lisRecursos.Agregate

    No sé yo si la función Agregate(Of T) te va a servir para añadir elementos a una supuesta lista genérica.

    Y aparte, también te comento que con las expresiones lambda, o donde se utilice LINQ, puede parecer que se ejecuta muy poco código (una línea, cuatro líneas como mucho), pero después, el compilador también hará con la expresión lambda lo que estime conveniente, y esas dos, tres líneas que has escrito, lo mismo te las convierte en 200 líneas de lenguaje MSIL, tal y como te he comentado anteriormente.

    La verdad es que observo en tus preguntas una cierta "obsesión" con el número de líneas que se ejecutan, o si éstas son muy grandes o pequeñas. Yo que tú me limitaría a escribir un código eficiente, pero por eficiente, no quiere decir que todo se tenga que ejecutar en dos líneas.

    Groso modo, un código eficiente es el que hace uso de procedimientos y funciones para no tener que estar repitiendo el mismo código en diferentes procedimientos, y suele realizar las llamadas a los métodos adecuados para llevar a buen término el trabajo que se le ha encomendado, almacenando los valores intermedios en variables declaradas con el tipo de dato adecuado, y desechando éstas cuando sus valores ya no sean necesarios, sin importar el número de líneas que ocupe el código fuente escrito, siempre y cuando éstas sean las estrictamente necesarias: ni una más pero tampoco ni una menos. ¡Fíjate que sencillo! ;-)

    > Recurso(nI) es una función que devuelve un elemento cuyo tipo de datos es una pequeña estructura
    >
    > estRecurso = Recurso(nI)
    > If estRecurso.Id <> Nothing Then lisRecursos.Add(estRecurso)

    Ignoro el tipo de dato que tiene el elemento Id dentro de esa estructura que comentas que devuelve la función Recurso. Si por casualidad es un tipo de dato numérico (Integer, Long, Decimal, etc.), te comento que dichos tipos de datos son estructuras (una declaración Structure), lo que significa que son tipos de datos de valor y por tanto su valor NUNCA será Nothing: será 0, si son tipos de datos numéricos.

    Diferente sería si el elemento Id es del tipo String, Form1, DataTable, MiClase (una declaración Class), en cuyo caso estaríamos ante un tipo de dato de referencia, y por tanto el valor sí puede ser Nothing.

    Te lo comento para que lo tengas en cuenta a fin de no obtener resultados inesperados:

    Tipos de valor y tipos de referencia


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.


    viernes, 6 de enero de 2017 7:51
    Moderador
  • Enrique ya te ha comentado que en cuanto a eficiencia en tiempo de ejecución, tu primer ejemplo con dos líneas es perfectamente eficaz una vez compilado. Pero si por "eficiencia" te refieres a "mínimo esfuerzo al teclear el código fuente", para poderlo escribir con el mínimo número de líneas, entonces tienes esta opción:

    Haz que tu variable lisRecursos en lugar de ser un List(Of loquesea) sea una clase que hereda de List(Of loquesea). Y dentro de esa clase, añade un método que se llame "AgregarSiNoEsNothing" que obviamente por dentro hará algo así como "If Not argumento is Nothing Then Me.Add(argumento)". Una vez hecho eso, desde fuera puedes manejarlo con un bucle como este:

     For nI = 1 To 50: lisRecursos.AgregarSiNoEsNothing(Recurso(nI)): Next

    • Marcado como respuesta James2016-2 viernes, 6 de enero de 2017 15:29
    viernes, 6 de enero de 2017 8:30
  • Buenas, sí, soy yo, el usuario James-2016, como le comenté en una conversación anterior llevo varios días sin poder hacer preguntas en el foro, por alguna razón que desconozco no puedo hacer preguntas con la otra cuenta por lo que tuve que crearme una cuenta alternativa.

    Bueno, respecto a su comentario me dice:

    "No recuerdo si en otra ocasión te he comentado que una cosa es lo que tu escribas y otra muy diferente lo que posteriormente haga el compilador de Visual Basic con el código que tú has escrito."

    Sí, alguna vez me comentó algo al respecto y entiendo que así es, el compilador estandariza el código, por así decirlo y puede que lo expanda, aún cuando así sea, en lo personal me es más cómodo trabajar con código lo más compacto posible, para mi no es lo mismo ver 10 líneas de código que dos o tres que hagan lo mismo, por eso me gusta compactarlo en la medida de lo posible.

    Efectivamente el elemento Id es de tipo string.

    Lo de eficiente me refería que en la segunda forma aún cuando es una línea, se ejecuta dos veces la función y eso ya no es muy eficiente que digamos y tampoco se trata de sacrificar eficiencia por reducir la cantidad de líneas visibles.

    Bueno, sabiendo que en programación hay distintas alternativas para resolver una cuestión ese era más o menos el sentido de la pregunta.

    viernes, 6 de enero de 2017 15:14
  • Bueno sí, hablar de eficiencia en el código también lo entiendo así como lo comenta Enrique: en el menor tiempo de ejecución posible, eso muy aparte del aspecto que tenga el código.

    No digo que un código más corto sea necesariamente más eficiente, así lo entiendo, digamos que son dos cosas distintas solo que busco una alternativa para escribir el código lo más compacto posible pero sin sacrificar eficiencia en cuanto al tiempo de ejecución.

    Me gusta la alternativa que propone, con eso consigo hacer lo mismo en una sola línea. Gracias, me gustó esa idea.



    • Editado James2016-2 viernes, 6 de enero de 2017 15:34
    viernes, 6 de enero de 2017 15:29
  • James2016-2,

    ¿Tienes alguna limitante de líneas en tu editor de código? (no lo tomes a mal, ayudará a entender)

    Es bueno que tengas claro que escribir menos líneas no tiene relación directa con que el resultado sea un código óptimo o eficiente, concéntrate en escribir lo necesario y como buena práctica escribe código legible, que permita fácil comprensión y mantenimiento o refactorización, no escribas código que sólo tu entenderás. Evita que alguien que lea tú código diga: ¿y ahora que quiso hacer aquí?. Como bien mencionó Enrique, el código óptimo o eficiente tiene que ver con otras cosas como reutilización, encapsulación, definición correcta de tipos, niveles de ámbito de variables, etc., sería un error si descuidas esos puntos -y otros realmente importantes- pero tienes tu código "bien compactadito", ¿leíste acerca de la métrica complejidad ciclomática que te propuse en un hilo anterior?

    No digo que lo que tienes ahora es altamente complicado de entender, hago mención para que no llegues a los extremos de agregar complejidad al código por ahorrarte pulsaciones de tecla o línea de código. Fíjate algo, ¿la función AgregarSiNoEsNothing() la reutilizarás? ¿o la escribirás únicamente para cumplir el capricho de escribir menos líneas?. Si la reutilizarás es correcto pero si sólo sirve para el caso puntual no le veo sentido además que si sumas todas las líneas escritas caes en lo mismo.



    Espero que la información proporcionada te haya sido de utilidad, quedo atento a tus comentarios.
    • Marcado como respuesta James2016-2 viernes, 6 de enero de 2017 18:32
    viernes, 6 de enero de 2017 15:55
  • Hola, agradezco tu comentario y todos los demás, ningún comentario lo tomo a mal y menos de programadores experimentados que aconsejan desde su conocimiento y su experiencia, creo yo que por intentar reducir el código anterior no dejo de tener en cuenta lo que comentaste en esa oportunidad sobre reutilización y facilidad de mantenimiento.

    En este caso particular esa función quizá la use dos o tres veces a lo largo del programa y creo que eso justificaría la función, aunque como dices no reduciría el número de líneas solo las apartaría de esa parte del código.

    En cuanto a la facilidad de mantenimiento puede que el primer código sea más legible que lo segundo, pero en este caso particular eso también lo veo relativo, me explico, AgregarSiNoEsNothing resume lo que hace lo primero con solo leer el nombre, más o menos como una caja negra, no importa el cómo sino el qué es lo que hace y en ese sentido resulta muy legible, entendible, digerible sin mucho esfuerzo, sin necesidad de tener siquiera que revisar el código ya se sabe que hace esa tarea.

    No es tanto por ahorrarme pulsaciones de teclado sino que a mi me parece que ver menos líneas de código en ciertos casos (como en este) me facilita la programación, no obstante debo admitir que a veces resumir demasiado termina haciendo ilegible el código.

    Dices: "Evita que alguien que lea tú código diga: ¿y ahora que quiso hacer aquí?. "

    Toda la razón, no solo para que alguien más lo vea, sino incluso para mi mismo, quiero revisar lo que hago ahora en uno o dos años y seguirlo entendiendo sin más esfuerzo del necesario.

    Revisé lo que comentas sobre la métrica complejidad ciclomática referida a las estructuras de control, a la medición de caminos que puede tomar el flujo del programa.

    Dicho lo anterior, todo lo que has dicho lo encuentro muy razonable y atendible, créeme que lo tomo en cuenta.

    Saludos.

    viernes, 6 de enero de 2017 18:32