Допустим, я пишу функцию для преобразования температурных шкал. Я хочу поддерживать по крайней мере градусы Цельсия, Фаренгейта и Кельвина. Лучше передать исходный масштаб и целевой масштаб как отдельные параметры функции или какой-то комбинированный параметр?
Пример 1 - отдельные параметры: функция convertTemperature ("по Цельсию", "по Фаренгейту", 22)
Пример 2 - комбинированный параметр: функция convertTemperature ("c-f", 22)
Код внутри функции, вероятно, имеет значение. С двумя параметрами логика определения того, какую формулу мы собираемся использовать, немного сложнее, но один параметр почему-то кажется неправильным.
Мысли?





Используйте первый вариант, но вместо того, чтобы разрешать буквальные строки (которые подвержены ошибкам), возьмите постоянные значения или перечисление, если ваш язык поддерживает это, например:
convertTemperature (TempScale.CELSIUS, TempScale.FAHRENHEIT, 22)
Мой голос - это два параметра для типов конверсии, один для ценности (как в вашем первом примере). Однако я бы использовал перечисления вместо строковых литералов.
Используйте перечисления, если ваш язык позволяет это, для спецификаций модуля.
Я бы сказал, что код внутри будет проще с двумя. У меня была бы таблица с предварительным добавлением, умножением и пост-сложением, и я бы пропустил значение через элемент для одной единицы, а затем через элемент для другой единицы в обратном порядке. В основном преобразование входной температуры в общее базовое значение внутри, а затем из другого блока. Вся эта функция будет управляться таблицей.
Зависит от того, сколько конверсий вы собираетесь получить. Я бы, наверное, выбрал один параметр в виде перечисления: рассмотрим эту расширенную версию преобразования.
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);
Но результат не имеет смысла, и вам придется потерпеть неудачу во время выполнения. Да, я знаю, что в данный момент вы имеете дело только с температурой, но общая концепция все еще остается в силе (я верю).
Вы делаете необоснованное предположение, что вам нужно использовать универсальное перечисление Units вместо чего-то более полезного для этой цели, например перечисления TemperatureUnits.
Я бы сделал перечисление типов температуры и передал 2 параметра шкалы. Что-то вроде (в C#):
public void ConvertTemperature(TemperatureTypeEnum SourceTemp,
TemperatureTypeEnum TargetTemp,
decimal Temperature)
{}
Несколько вещей:
Я бы использовал перечислимый тип, который может проверить средство проверки синтаксиса или компилятор, а не строку, в которой могут быть ошибки. В псевдо-PHP:
define ('kCelsius', 0); определить ('kFarenheit', 1); определить ('kKelvin', 2); $ a = ConvertTemperature (22, кЦельсия, кФаренгейта);
Кроме того, мне кажется более естественным разместить то, с чем вы работаете, в данном случае температуру, которую нужно преобразовать, первый. Это дает логический порядок вашим параметрам (преобразовать - что? Из? В?) И, таким образом, помогает с мнемоникой.
я бы выбрал
Example 1 - separate parameters: function convertTemperature("celsius", "fahrenheit", 22)
В противном случае в определении функции вам пришлось бы разобрать «c-f» на «градусы Цельсия» и «Фаренгейта», чтобы получить требуемые масштабы преобразования, что могло бы стать беспорядочным.
Если вы предоставляете пользователям что-то вроде окна поиска Google, им приятно иметь удобные ярлыки, такие как «c-f». Однако ниже я бы преобразовал «c-f» в «celsius» и «fahrenheit» во внешней функции перед вызовом convertTemperature (), как указано выше.
В этом случае отдельные параметры выглядят совершенно непонятными;
Функция конвертировать температура из одна шкала в другой масштаб.
IMO естественнее передавать исходный и целевой масштабы как отдельные параметры. Я определенно не хочу пытаться понять формат первого аргумента.
При написании таких дизайнов я люблю думать про себя: «Если бы мне нужно было добавить дополнительную единицу, какой дизайн помог бы сделать это проще всего?» Делая это, я прихожу к выводу, что перечисления было бы проще всего по следующим причинам:
1) Добавить новые значения просто. 2) Я избегаю сравнения строк
Однако как написать метод преобразования? 3p2 равно 6. Это означает, что существует 6 различных комбинаций Цельсия, Фаренгейта и Кельвина. Что, если бы я хотел добавить новый умеренный формат «foo»? Это будет означать 4p2, что составляет 12! Еще два? 5п2 = 20 комбинаций. Еще три? 6р2 = 30 комбинаций!
Вы можете быстро увидеть, как каждая дополнительная модификация требует все новых и новых изменений кода. По этой причине я не занимаюсь прямыми преобразованиями! Вместо этого я делаю промежуточное преобразование. Я бы выбрал одну температуру, скажем, по Кельвину. И сначала я перешел в кельвин. Затем я переводил градусы Кельвина в желаемую температуру. Да, это требует дополнительных вычислений. Однако это значительно упрощает масштабирование кода. добавление новой единицы измерения температуры всегда приводит только к двум новым модификациям кода. Легко.
Ваша функция будет намного более надежной, если вы воспользуетесь первым подходом. Если вам нужно добавить еще одну шкалу, это еще одно значение параметра, которое нужно обработать. Во втором подходе добавление другой шкалы означает добавление столько значений, сколько у вас уже было шкал в списке, умноженное на 2 (например, чтобы добавить K к C и F, вам нужно будет добавить KC, KF, CK и CF.)
Хороший способ структурировать вашу программу - сначала преобразовать все, что поступает, в произвольно выбранный промежуточный масштаб, а затем преобразовать из этого промежуточного масштаба в исходящий масштаб.
Лучшим способом было бы иметь небольшую библиотеку наклонов и пересечений для различных масштабов, и просто искать числа для входящих и исходящих масштабов и выполнять расчет за один общий шаг.
Зависит от языка.
Как правило, я бы использовал отдельные аргументы с перечислениями.
Если это объектно-ориентированный язык, я бы порекомендовал температурный класс с температурой, хранящейся внутри, как вам нравится, а затем функции для вывода ее в любых необходимых единицах:
temp.celsius (); // возвращает температуру объекта в градусах Цельсия
Я всегда ищу способы использовать объекты для решения моих проблем с программированием. Надеюсь, это означает, что я в большей степени являюсь объектно-ориентированным человеком, чем когда я использовал функции только для решения проблем, но это еще предстоит выяснить.
В 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);
}
}
и некоторые тесты:
// 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'
Добавление преобразований Кельвина осталось в качестве упражнения.
В C# (и, вероятно, Java) было бы лучше создать класс температуры, который хранит температуру в частном порядке как Цельсия (или что-то еще) и который имеет свойства Цельсия, Фаренгейта и Кельвина, которые делают все преобразования для вас в своих операторах get и set?
Кстати, для реализации версии с тремя параметрами, как предлагается в вопросе, не требуется больше усилий.
Все это линейные функции, поэтому вы можете реализовать что-то вроде
float LinearConvert(float in, float scale, float add, bool invert);
где последний bool указывает, хотите ли вы выполнить прямое преобразование или отменить его.
В рамках вашей техники преобразования вы можете иметь пару масштаб / сложение для X -> Кельвина. Когда вы получаете запрос на преобразование формата X в Y, вы можете сначала запустить X -> Kelvin, затем Kelvin -> Y, изменив процесс Y -> Kelvin (перевернув последний bool на 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.
Я бы хотел, чтобы был способ принять несколько ответов. Основываясь на всех рекомендациях, я думаю, что буду придерживаться нескольких параметров, изменяя строки на перечисления / константы и перемещая значение, которое нужно преобразовать, в первую позицию в списке параметров. Внутри функции я буду использовать Кельвин как общую золотую середину.
Раньше я писал отдельные функции для каждого преобразования, а общая функция convertTemperature () была просто оболочкой с вложенными операторами switch. Я пишу как на классическом ASP, так и на PHP, но я хотел оставить вопрос открытым для любого языка.
Вот мой взгляд на это (с использованием 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 - одна функция, которая управляет всеми ними. знак равно
Ха-ха ... ваш ответ хочу здесь, когда я начал свой. Ты подтолкнул меня на это! :-)