style general coding code coding-style

coding-style - general - airbnb style code



La mejor forma de escribir una función de conversión (18)

Digamos que estoy escribiendo una función para convertir escalas de temperatura. Quiero apoyar al menos a Celsius, Fahrenheit y Kelvin. ¿Es mejor pasar la escala fuente y la escala objetivo como parámetros separados de la función, o algún tipo de parámetro combinado?

Ejemplo 1 - parámetros separados: función convertTemperature ("celsius", "fahrenheit", 22)

Ejemplo 2 - parámetro combinado: función convertTemperature ("cf", 22)

El código dentro de la función es probablemente donde cuenta. Con dos parámetros, la lógica para determinar qué fórmula vamos a usar es un poco más complicada, pero un parámetro único no se siente bien de alguna manera.

¿Pensamientos?


Aquí está mi opinión sobre esto (usando PHP):

function Temperature($value, $input, $output) { $value = floatval($value); if (isset($input, $output) === true) { switch ($input) { case ''K'': $value = $value - 273.15; break; // Kelvin case ''F'': $value = ($value - 32) * (5 / 9); break; // Fahrenheit case ''R'': $value = ($value - 491.67) * (5 / 9); break; // Rankine } switch ($output) { case ''K'': $value = $value + 273.15; break; // Kelvin case ''F'': $value = $value * (9 / 5) + 32; break; // Fahrenheit case ''R'': $value = ($value + 273.15) * (9 / 5); break; // Rankine } } return $value; }

Básicamente, el valor $input se convierte a la escala Celsius estándar y luego se convierte nuevamente a la escala $output , una función para gobernarlos todos. =)


Al escribir tales diseños, me gusta pensar: "Si tuviera que agregar una unidad adicional, ¿qué diseño haría que fuera más fácil hacerlo?" Al hacer esto, llegué a la conclusión de que las enumeraciones serían más fáciles por las siguientes razones:

1) Agregar nuevos valores es fácil. 2) Evito hacer una comparación de cadenas

Sin embargo, ¿cómo se escribe el método de conversión? 3p2 es 6. Entonces eso significa que hay 6 combinaciones diferentes de Celsius, Fahrenheit y Kelvin. ¿Qué pasa si quiero agregar un nuevo formato templado "foo"? Eso significaría 4p2 que es 12! ¿Dos más? 5p2 = 20 combinación. ¿Tres más? 6p2 = 30 combinaciones!

Puede ver rápidamente cómo cada modificación adicional requiere más y más cambios en el código. Por esta razón, ¡no hago conversiones directas! En cambio, hago una conversión intermedia. Escogería una temperatura, por ejemplo, Kelvin. E inicialmente, me convertiría en kelvin. Luego convertiría kelvin a la temperatura deseada. Sí, da como resultado un cálculo extra. Sin embargo, hace que escalar el código sea mucho más fácil. agregar agregar una nueva unidad de temperatura siempre resultará en solo dos nuevas modificaciones al código. Fácil.


Creo que iré por una dirección u otra. Podrías escribir un mini-lenguaje que haga cualquier tipo de conversión, como lo hacen las unidades :

$ units ''tempF(-40)'' tempC -40

O use funciones individuales como hace el módulo Convert :: Temperature Perl reciente:

use Convert::Temperature; my $c = new Convert::Temperature(); my $res = $c->from_fahr_to_cel(''59'');

Pero eso trae a colación un punto importante: ¿el idioma que está utilizando ya tiene funciones de conversión? Si es así, ¿qué convención de codificación usan? Entonces, si el lenguaje es C, sería mejor seguir el ejemplo de las funciones de la biblioteca atoi y strtod (no probadas):

double fahrtocel(double tempF){ return ((tempF-32)*(5/9)); } double celtofahr(double tempC){ return ((9/5)*tempC + 32); }

Al escribir esta publicación, encontré una publicación muy interesante sobre el uso de emacs para convertir fechas. Lo que se lleva para este tema es que usa el estilo de una función por conversión. Además, las conversiones pueden ser muy oscuras. Tiendo a hacer cálculos de fechas usando SQL porque parece poco probable que haya muchos errores en ese código. En el futuro, voy a analizar el uso de emacs.


Depende de cuántas conversiones va a tener. Probablemente elegiría un parámetro, dado como una enumeración: considere esta versión expandida de la conversión.

enum Conversion { CelsiusToFahrenheit, FahrenheitToCelsius, KilosToPounds } Convert(Conversion conversion, X from);

Ahora tiene seguridad de tipo de usuario en el punto de llamada: no se pueden dar los parámetros correctamente tipeados que dan un resultado de tiempo de ejecución incorrecto. Considera la alternativa.

enum Units { Pounds, Kilos, Celcius, Farenheight } Convert(Unit from, Unit to, X fromAmount);

Puedo escribir de forma segura llamada

Convert(Pounds, Celcius, 5, 10);

Pero el resultado no tiene sentido, y tendrás que fallar en el tiempo de ejecución. Sí, sé que solo estás lidiando con la temperatura en este momento, pero el concepto general aún se mantiene (creo).


Depende del idioma

En general, usaría argumentos separados con enumeraciones.

Si se trata de un lenguaje orientado a objetos, entonces recomendaría una clase de temperatura, con la temperatura almacenada internamente como prefiera y luego funcione para producirla en cualquier unidad que se necesite:

temp.celsius (); // devuelve la temperatura de la temperatura del objeto en celsius


En C # (y probablemente Java), sería mejor crear una clase de temperatura que almacene las temperaturas de forma privada como Celcius (o lo que sea) y que tenga propiedades Celcius, Fahrenheit y Kelvin que realicen todas las conversiones en sus instrucciones de obtención y configuración.


En este caso, los parámetros individuales parecen totalmente oscuros;

La función convierte la temperatura de una escala a otra .
OMI es más natural pasar las escalas de origen y destino como parámetros separados. Definitivamente no quiero tratar de captar el formato del primer argumento.


Haría una enumeración de los tipos de temperatura y pasaría los 2 parámetros de escala. Algo así como (en c #):

public void ConvertTemperature(TemperatureTypeEnum SourceTemp, TemperatureTypeEnum TargetTemp, decimal Temperature) {}


Mi voto es dos parámetros para los tipos de conversión, uno para el valor (como en su primer ejemplo). Sin embargo, usaría enumeraciones en lugar de literales de cadenas.


Por cierto, no tiene que ser más trabajo implementar la versión de tres parámetros, como se sugiere en el enunciado de la pregunta.

Estas son todas funciones lineales, por lo que puede implementar algo así como

float LinearConvert(float in, float scale, float add, bool invert);

donde el último bool indica si quieres hacer la transformación directa o invertirla.

Dentro de su técnica de conversión, puede tener un par escala / agregar para X -> Kelvin. Cuando recibe una solicitud para convertir el formato X a Y, primero puede ejecutar X -> Kelvin, luego Kelvin -> Y invirtiendo el proceso Y -> Kelvin (pasando el último bool a LinearConvert).

Esta técnica te proporciona algo así como 4 líneas de código real en tu función de conversión, y una pieza de datos para cada tipo que necesites convertir.


Siempre estoy buscando formas de usar objetos para resolver mis problemas de programación. Espero que esto signifique que soy más OO que cuando solo estaba usando funciones para resolver problemas, pero eso está por verse.

Cª#:

interface ITemperature { CelciusTemperature ToCelcius(); FarenheitTemperature ToFarenheit(); } struct FarenheitTemperature : ITemperature { public readonly int Value; public FarenheitTemperature(int value) { this.Value = value; } public FarenheitTemperature ToFarenheit() { return this; } public CelciusTemperature ToCelcius() { return new CelciusTemperature((this.Value - 32) * 5 / 9); } } struct CelciusTemperature { public readonly int Value; public CelciusTemperature(int value) { this.Value = value; } public CelciusTemperature ToCelcius() { return this; } public FarenheitTemperature ToFarenheit() { return new FarenheitTemperature(this.Value * 9 / 5 + 32); } }

y algunas pruebas:

// Freezing Debug.Assert(new FarenheitTemperature(32).ToCelcius().Equals(new CelciusTemperature(0))); Debug.Assert(new CelciusTemperature(0).ToFarenheit().Equals(new FarenheitTemperature(32))); // crossover Debug.Assert(new FarenheitTemperature(-40).ToCelcius().Equals(new CelciusTemperature(-40))); Debug.Assert(new CelciusTemperature(-40).ToFarenheit().Equals(new FarenheitTemperature(-40)));

y un ejemplo de error que este enfoque evita:

CelciusTemperature theOutbackInAMidnightOilSong = new CelciusTemperature(45); FarenheitTemperature x = theOutbackInAMidnightOilSong; // ERROR: Cannot implicitly convert type ''CelciusTemperature'' to ''FarenheitTemperature''

Agregar conversiones Kelvin se deja como un ejercicio.


Similar a lo que @Rob @wcm y @David explicaron ...

public class Temperature { private double celcius; public static Temperature FromFarenheit(double farenheit) { return new Temperature { Farhenheit = farenheit }; } public static Temperature FromCelcius(double celcius) { return new Temperature { Celcius = celcius }; } public static Temperature FromKelvin(double kelvin) { return new Temperature { Kelvin = kelvin }; } private double kelvinToCelcius(double kelvin) { return 1; // insert formula here } private double celciusToKelvin(double celcius) { return 1; // insert formula here } private double farhenheitToCelcius(double farhenheit) { return 1; // insert formula here } private double celciusToFarenheit(double kelvin) { return 1; // insert formula here } public double Kelvin { get { return celciusToKelvin(celcius); } set { celcius = kelvinToCelcius(value); } } public double Celcius { get { return celcius; } set { celcius = value; } } public double Farhenheit { get { return celciusToFarenheit(celcius); } set { celcius = farhenheitToCelcius(value); } } }


Su función será mucho más robusta si usa el primer enfoque. Si necesita agregar otra escala, ese es un valor de parámetro más para manejar. En el segundo enfoque, agregar otra escala significa agregar tantos valores como ya tenía en la lista, multiplicado por 2. (Por ejemplo, para agregar K a C y F, debe agregar KC, KF, CK y CF.)

Una forma decente de estructurar su programa sería convertir primero lo que entra en una escala intermedia arbitrariamente elegida, y luego convertir de esa escala intermedia a la escala saliente.

Una mejor manera sería tener una pequeña biblioteca de pendientes e intersecciones para las diversas escalas, y simplemente buscar los números para las escalas entrante y saliente y hacer el cálculo en un paso genérico.


Unas pocas cosas:

  • Utilizaría un tipo enumerado que un verificador de sintaxis o compilador puede verificar en lugar de una cadena que puede escribirse mal. En Pseudo-PHP:

    define (''kCelsius'', 0); define (''kFarenheit'', 1); define (''kKelvin'', 2); $ a = ConvertTemperature (22, kCelsius, kFarenheit);

Además, me parece más natural colocar la cosa en la que opera, en este caso la temperatura a convertir, primero . Le da un orden lógico a sus parámetros (convertir - ¿de qué? A?) Y por lo tanto ayuda con mnemónicos.


Use enumeraciones, si su idioma lo permite, para las especificaciones de la unidad.

Diría que el código interno sería más fácil con dos. Tendría una tabla con pre-agregar, multiplicar y post-agregar, y ejecutar el valor a través del elemento para una unidad, y luego a través del elemento para la otra unidad en reversa. Básicamente convertir la temperatura de entrada a un valor base común dentro, y luego a la otra unidad. Toda esta función estaría dirigida por tablas.


Vaya con la primera opción, pero en lugar de permitir cadenas literales (que son propensas a errores), tome valores constantes o una enumeración si su idioma lo admite, como este:

convertTemperature (TempScale.CELSIUS, TempScale.FAHRENHEIT, 22)


yo elegiría

Ejemplo 1 - parámetros separados: función convertTemperature ("celsius", "fahrenheit", 22)

De lo contrario, dentro de la definición de su función tendría que analizar "cf" en "celsius" y "fahrenheit" de todos modos para obtener las escalas de conversión requeridas, lo que podría complicarse.

Si está proporcionando algo como el cuadro de búsqueda de Google a los usuarios, tener accesos directos prácticos como "cf" es bueno para ellos. Debajo, sin embargo, convertiría "cf" en "celsius" y "fahrenheit" en una función externa antes de llamar a convertTemperature () como se indicó anteriormente.


Ojalá hubiera alguna forma de aceptar múltiples respuestas. De acuerdo con las recomendaciones de todos, creo que me quedaré con los parámetros múltiples, cambiando las cadenas a enumeraciones / constantes, y moviendo el valor a convertir a la primera posición en la lista de parámetros. Dentro de la función, usaré Kelvin como punto medio común.

Anteriormente había escrito funciones individuales para cada conversión y la función convertTemperature () general era simplemente un contenedor con instrucciones de conmutación anidadas. Escribo tanto en ASP clásico como en PHP, pero quería dejar la pregunta abierta a cualquier idioma.