coding-style erlang

coding style - Estilo Erlang-coincidencia entre caso y función.



coding-style (6)

Llegué a la etapa en la que escribí bastante código de Erlang ahora, y puedo ver un cierto estilo (malo o bueno) en la forma en que lo he estado escribiendo. Este lenguaje en particular en el que me gustaría opinar: ¿es mejor (más legible / más rápido / lo que sea) para convertir las declaraciones de estilo de caso a la función de coincidencia de patrones?

P.ej

Comparar (un ejemplo artificial)

case {Size > 100000, Type} of {true, ets } -> %% Do something to convert to dets something; {false, dets} -> %% do something to convert to ets somethingelse; _ -> ignoreit end;

con

... maybeChangeStorage(Size, Type) ... maybeChangeStorage(Size, ets) when Size > 10000 -> something; maybeChangeStorage(Size, dets) when Size < 10000 -> somethingelse; maybeChangeStorage(_,_) -> ignoreit.

Prefiero este último en la mayoría de los casos, pero estaría interesado en otra opinión.


En cuanto a mí, el primer estilo es más claro y puede ser más rápido. Pero necesita prueba para decirlo exactamente. En el segundo caso, si el tipo! = Ets, se evaluarán tanto "Tamaño> 10000" como "Tamaño <10000".


La segunda es la forma preferida, especialmente si puede mantener las cláusulas en una sola línea:

maybeCngStor(Sz, ets) when Sz > 10000 -> something; maybeCngStor(Sz, dets) when Sz < 10000 -> somethingelse; maybeCngStor(_,_) -> ignoreit.

Hace que sea muy fácil de leer y razonar. Elija siempre el estilo que será más fácil de leer en el futuro. A menudo, se encuentra un conjunto de cláusulas en las que uno es un trazador de líneas 10 y el resto son solo una línea; rompa el largo en una función:

maybeCngStor(Sz, ets) when Sz > 10000 -> something; maybeCngStor(Sz, dets) when Sz < 10000 -> somethingelse(); maybeCngStor(_,_) -> ignoreit. somethingelse() -> (...) Return.

Las cosas pequeñas como establecer las cláusulas para alinearlas y usar nombres de variables cortos son importantes, pero no caiga en la trampa de cambiar todo a P, Q, R.

Un buen truco si usa mucho los registros es hacer coincidir los registros con las variables cortas:

#record{foo = F, bar = B, baz = Bz} = Parameter

Esto le da nombres de variables cortos que tienen sentido cuando se lanza en paracaídas a la función desde 10,000 pies en busca de un error la próxima Navidad. F obviamente es un Foo, etc, etc ...


Puedes hacer estos ejemplos más similares haciendo:

case Type of ets when Size > 10000 -> ...; dets when Size < 10000 -> ...; _ -> ... end.

Esto me parece más claro. La ventaja de dividir esto en una función separada es que le da un nombre que actúa como documentación y aparece en las huellas de la pila. Si ese fragmento de código es parte de una función más grande, lo separaría, de lo contrario está bien tal como está.

Una cosa que vale la pena considerar es que, en el caso de error, como está escrita, la función aceptará argumentos de tipo que no sean ets / dets. A menos que esto sea realmente lo que usted desea, vale la pena hacer que esta cláusula sea más restrictiva.


Si en su función lo primero que hace es abrir una cláusula de caso, es mejor convertir esta cláusula de nivel superior para que coincida con el patrón de función.


Aprender algo de Erlang para bien tiene una pequeña sección sobre cuándo elegir un case y cuándo usar una function . Se mencionan dos cosas:

  1. Están representados de la misma manera en la máquina virtual, por lo que no hay diferencia en el rendimiento entre las dos soluciones.

  2. Si necesita usar guardias contra más de un argumento, usar una función puede leer mejor.

Con todo, es sobre todo una cuestión de estilo y gusto.


(¡Ponga como respuesta para obtener el formato del código ...!)

Una cosa que encontré cuando estaba realizando algunos cambios es que este enfoque puede alterar el cortocircuito predeterminado. P.ej

case A > 10 of true -> case B > 10 of true -> dummy1; false -> dummy2 end; false -> dummy3 end

Tendría que ejecutar siempre B> 10 si lo llamas como

doTest(A > 10, B > 10)

cuando

doTest(true, true) -> dummy1; doTest(true, false) -> dummy2; doTest(false, _) -> dummy3.

que a veces no es lo que quieres!