Лучший способ написать функцию преобразования

StackOverflow https://stackoverflow.com/questions/59102

  •  09-06-2019
  •  | 
  •  

Вопрос

Допустим, я пишу функцию для преобразования температурных шкал.Я хочу поддерживать как минимум градусы Цельсия, Фаренгейта и Кельвина.Лучше ли передать исходный и целевой масштаб как отдельные параметры функции или какой-то комбинированный параметр?

Пример 1 – отдельные параметры:функция ConvertTemperature("Цельсий", "Фаренгейт", 22)

Пример 2 – комбинированный параметр:функция ConvertTemperature("c-f", 22)

Код внутри функции, вероятно, имеет значение.С двумя параметрами логика определения того, какую формулу мы собираемся использовать, немного сложнее, но один параметр почему-то кажется неправильным.

Мысли?

Это было полезно?

Решение

Используйте первый вариант, но вместо того, чтобы разрешать литеральные строки (которые подвержены ошибкам), принимайте постоянные значения или перечисления, если ваш язык поддерживает это, например:

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

Другие советы

Зависит от языка.

Обычно я бы использовал отдельные аргументы с перечислениями.

Если это объектно-ориентированный язык, то я бы порекомендовал класс температуры, в котором температура хранится внутри, как вам нравится, а затем функции для ее вывода в любых необходимых единицах:

темп.Цельсий();// возвращает температуру объекта в градусах Цельсия

При написании таких дизайнов мне нравится думать про себя: «Если бы мне нужно было добавить дополнительное устройство, что бы дизайн сделает его самым простым?» Делая это, я прихожу к выводу, что перечисление будет проще по следующим причинам:

1) Добавить новые значения легко.2) Я избегаю сравнения строк

Однако как написать метод преобразования?3п2 — это 6.Это означает, что существует 6 различных комбинаций градусов Цельсия, Фаренгейта и Кельвина.Что, если я захочу добавить новый умеренный формат «foo»?Это будет означать 4p2, что равно 12!Еще два?5п2 = 20 комбинаций.Еще три?6п2 = 30 комбинаций!

Вы можете быстро увидеть, как каждая дополнительная модификация требует все новых и новых изменений в коде.По этой причине я не делаю прямых преобразований!Вместо этого я делаю промежуточное преобразование.Я бы выбрал одну температуру, скажем, Кельвин.И сначала я бы перевел в кельвины.Затем я конвертировал кельвины в желаемую температуру.Да, это приводит к дополнительным расчетам.Однако это значительно упрощает программирование кода.Добавление новой единицы измерения температуры всегда будет приводить только к двум новым изменениям в коде.Легкий.

Несколько вещей:

  • Я бы использовал перечислимый тип, который может проверить программа проверки синтаксиса или компилятор, а не строку, которую можно набрать с ошибкой.В псевдо-PHP:

    определить («кЦельсий», 0);определить ('кФаренгейт', 1);определить ('кКельвин', 2);$a = ConvertTemperature(22, кЦельсия, кФаренгейта);

Кроме того, мне кажется более естественным разместить предмет, с которым вы работаете, в данном случае температуру, которую необходимо преобразовать, первый.Это дает логический порядок вашим параметрам (конвертировать — что?от?to?) и таким образом помогает мнемотехнике.

Ваша функция будет намного более надежной, если вы воспользуетесь первым подходом.Если вам нужно добавить еще один масштаб, это еще одно значение параметра, которое нужно обработать.Во втором подходе добавление еще одной шкалы означает добавление столько значений, сколько у вас уже было шкал в списке, умноженное на 2.(Например, чтобы добавить K к C и F, вам придется добавить K-C, K-F, C-K и CF.)

Приемлемым способом структурирования вашей программы было бы сначала преобразовать все входящее в произвольно выбранный промежуточный масштаб, а затем преобразовать этот промежуточный масштаб в исходящий.

Лучшим способом было бы иметь небольшую библиотеку наклонов и точек пересечения для различных масштабов, просто искать числа для входящих и исходящих шкал и выполнять расчеты за один общий шаг.

В C# (и, возможно, в Java) было бы лучше создать класс температуры, который конфиденциально хранит температуры как Цельсия (или что-то еще) и который имеет свойства Цельсия, Фаренгейта и Кельвина, которые выполняют все преобразования за вас в своих операторах получения и установки?

Зависит от того, сколько конверсий вы собираетесь получить.Я бы, вероятно, выбрал один параметр в виде перечисления:Рассмотрим эту расширенную версию преобразования.

enum Conversion
{
  CelsiusToFahrenheit,
  FahrenheitToCelsius,
  KilosToPounds
}

Convert(Conversion conversion, X from);

Теперь у вас есть разумная безопасность типов в точке вызова - невозможно указать правильно типизированные параметры, которые дают неправильный результат во время выполнения.Рассмотрим альтернативу.

enum Units
{
  Pounds,
  Kilos,
  Celcius,
  Farenheight
}

Convert(Unit from, Unit to, X fromAmount);

Я могу спокойно печатать, позвони

Convert(Pounds, Celcius, 5, 10);

Но результат бессмысленен, и вам придется потерпеть неудачу во время выполнения.Да, я знаю, что на данный момент вы имеете дело только с температурой, но общая концепция все еще сохраняется (я думаю).

я бы выбрал

Пример 1 – отдельные параметры:функция ConvertTemperature("Цельсий", "Фаренгейт", 22)

В противном случае в определении вашей функции вам все равно придется разбирать «c-f» на «по Цельсию» и «по Фаренгейту», чтобы получить необходимые шкалы преобразования, что может привести к путанице.

Если вы предоставляете пользователям что-то вроде окна поиска Google, им будет полезно иметь удобные ярлыки, такие как «c-f».Однако ниже я бы конвертировал «c-f» в «Цельсий» и «Фаренгейт» во внешней функции перед вызовом ConvertTemperature(), как указано выше.

В этом случае отдельные параметры выглядят совершенно непонятно;

Функция преобразования температура от одна шкала к другой масштаб.
По моему мнению, более естественно передавать исходные и целевые масштабы как отдельные параметры.Я определенно не хочу пытаться понять формат первого аргумента.

Я бы сделал перечисление типов температуры и передал два параметра шкалы.Что-то вроде (в С#):


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

Я всегда ищу способы использования объектов для решения своих задач по программированию.Я надеюсь, что это означает, что я больше занимаюсь объектно-ориентированным подходом, чем когда я использовал только функции для решения проблем, но это еще предстоит выяснить.

В С#:

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

и немного тестов:

        // 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)));

и пример ошибки, которую позволяет избежать этот подход:

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

Добавление преобразований Кельвина оставлено в качестве упражнения.

Кстати, для реализации версии с тремя параметрами, как предложено в постановке вопроса, не требуется дополнительных усилий.

Это все линейные функции, поэтому вы можете реализовать что-то вроде

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

где последнее логическое значение указывает, хотите ли вы выполнить прямое преобразование или отменить его.

В рамках вашей техники преобразования вы можете использовать пару масштабирования/сложения для X -> Кельвин.Когда вы получаете запрос на преобразование формата X в Y, вы можете сначала запустить X -> Кельвин, затем Кельвин -> Y, обратив процесс Y -> Кельвин (перевернув последнее логическое значение на LinearConvert).

Этот метод дает вам примерно 4 строки реального кода в функции преобразования и по одному фрагменту данных для каждого типа, между которым вам нужно преобразовать.

Подобно тому, что объяснили @Rob @wcm и @David...

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

Думаю, я бы пошел напролом в том или ином направлении.Вы можете написать мини-язык, который выполняет любые преобразования, например единицы делает:

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

Или используйте отдельные функции, такие как недавние Конвертировать::Температура Модуль Perl выполняет:

use Convert::Temperature;

my $c = new Convert::Temperature();

my $res = $c->from_fahr_to_cel('59');

Но тут возникает важный момент: есть ли в используемом вами языке функции преобразования?Если да, то какое соглашение о кодировании они используют?Поэтому, если язык C, лучше всего следовать примеру библиотечных функций atoi и strtod (непроверенных):

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

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

При написании этого поста я столкнулся с очень интересный пост об использовании emacs для преобразования дат.Вывод из этой темы заключается в том, что здесь используется стиль «одна функция на преобразование».Кроме того, конверсии могут быть очень неясными.Я предпочитаю выполнять вычисления дат с помощью SQL, потому что маловероятно, что в этом коде много ошибок.В будущем я собираюсь изучить возможность использования emacs.

Вот мое мнение по этому поводу (с использованием 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;
}

В основном $input значение преобразуется в стандартную шкалу Цельсия, а затем снова преобразуется в $output масштаб – одна функция, которая управляет всеми."="

Мой голос - это два параметра для типов конверсии, один для значения (как в вашем первом примере).Однако я бы использовал перечисления вместо строковых литералов.

Используйте перечисления, если это позволяет ваш язык, для спецификаций единиц измерения.

Я бы сказал, что код внутри было бы проще с двумя.У меня была бы таблица с предварительным добавлением, умножением и последующим добавлением, и я пропускал бы значение через элемент для одной единицы, а затем через элемент для другой единицы в обратном порядке.По сути, преобразование входной температуры в общее базовое значение внутри, а затем в другую единицу измерения.Вся эта функция будет управляться таблицами.

Мне бы хотелось, чтобы был какой-то способ принять несколько ответов.Основываясь на общих рекомендациях, я думаю, что буду придерживаться нескольких параметров, заменив строки на перечисления/константы и переместив преобразуемое значение на первую позицию в списке параметров.Внутри функции я буду использовать Кельвин как золотую середину.

Раньше я писал отдельные функции для каждого преобразования, и общая функция ConvertTemperature() была просто оболочкой с вложенными операторами переключения.Я пишу как на классическом ASP, так и на PHP, но мне хотелось оставить вопрос открытым для любого языка.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top