Решение на Първа задача - температура и химични елементи от Никола Жишев

Обратно към всички решения

Към профила на Никола Жишев

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 17 успешни тест(а)
  • 0 неуспешни тест(а)

Код

module Temperature
module Converter
def convert_between_temperature_units(units, from, to)
temperature = convert_to_celsius[from][units]
convert_from_celsius[to][temperature]
end
private
def convert_to_celsius
{
'K' => ->(temp) { (temp - 273.15) },
'F' => ->(temp) { (temp - 32) / (9.0 / 5) },
'C' => ->(temp) { temp }
}
end
def convert_from_celsius
{
'K' => ->(temp) { temp + 273.15 },
'F' => ->(temp) { temp * (9.0 / 5) + 32 },
'C' => ->(temp) { temp }
}
end
end
module MeltingPoints
include Converter
MELTING_POINTS = {
'water' => 0,
'ethanol' => -114,
'gold' => 1_064,
'silver' => 961.8,
'copper' => 1_085
}.freeze
def melting_point_of_substance(substance, unit, from: 'C')
temperature = MELTING_POINTS[substance]
convert_between_temperature_units(temperature, from, unit)
end
end
module BoilingPoints
include Converter
BOILING_POINTS = {
'water' => 100,
'ethanol' => 78.37,
'gold' => 2_700,
'silver' => 2_162,
'copper' => 2_567
}.freeze
def boiling_point_of_substance(substance, unit, from: 'C')
temperature = BOILING_POINTS[substance]
convert_between_temperature_units(temperature, from, unit)
end
end
end
include Temperature::Converter
include Temperature::BoilingPoints
include Temperature::MeltingPoints

Лог от изпълнението

.................

Finished in 0.00974 seconds
17 examples, 0 failures

История (2 версии и 3 коментара)

Никола обнови решението на 11.10.2016 18:55 (преди над 7 години)

+module Temperature
+ KNOWN_UNITS = %w(K F C).freeze
+
+ module Converter
+ include Temperature
+
+ class UnknownUnitError < StandardError; end
+
+ def convert_between_temperature_units(temperature, from, to)
+ raise UnknownUnitError unless known_units?([from, to])
+ method_convert = "#{from.downcase}_convert_#{to.downcase}"
+ send(method_convert, temperature)
+ end
+
+ private
+
+ def c_convert_k(temperature)
+ temperature + 273.15
+ end
+
+ def c_convert_f(temperature)
+ temperature * (9.0 / 5) + 32
+ end
+
+ def k_convert_c(temperature)
+ temperature - 273.15
+ end
+
+ def k_convert_f(temperature)
+ in_celsius = k_convert_c(temperature)
+ c_convert_f(in_celsius)
+ end
+
+ def f_convert_c(temperature)
+ (temperature - 32) / (9.0 / 5)
+ end
+
+ def f_convert_k(temperature)
+ in_celsius = f_convert_c(temperature)
+ c_convert_k(in_celsius)
+ end
+
+ KNOWN_UNITS.each do |unit|
+ name = unit.downcase
+ converter = "#{name}_convert_#{name}"
+ define_method(converter, -> (temp) { temp })
+ end
+ end
+
+ module MeltingPoints
+ include Converter
+ def melting_point_of_substance(substance, unit, from: 'C')
+ temperature = melting_points[substance]
+ convert_between_temperature_units(temperature, from, unit)
+ end
+
+ private
+
+ def melting_points
+ {
+ 'water' => 0,
+ 'ethanol' => -114,
+ 'gold' => 1_064,
+ 'silver' => 961.8,
+ 'copper' => 1_085
+ }
+ end
+ end
+
+ module BoilingPoints
+ include Converter
+ def boiling_point_of_substance(substance, unit, from: 'C')
+ temperature = boiling_points[substance]
+ convert_between_temperature_units(temperature, from, unit)
+ end
+
+ private
+
+ def boiling_points
+ {
+ 'water' => 100,
+ 'ethanol' => 78.37,
+ 'gold' => 2_700,
+ 'silver' => 2_162,
+ 'copper' => 2_567
+ }
+ end
+ end
+
+ def known_units?(units)
+ units.all? { |unit| KNOWN_UNITS.include? unit }
+ end
+end
+
+include Temperature::Converter
+include Temperature::BoilingPoints
+include Temperature::MeltingPoints

Хм, как да го кажа... Задачата ти е да убиеш муха. С това решение си убил мухата. Покрай нея си разрушил стената, бутнал си си къщата... Всъщност, градът ти вече не съществува... Защото вместо да вземеш вестник си убил мухата с водородна бомба.

Метапрограмирането си има своите приложения, но тази задача определено не е едно от тях :) Целта на задачата е да я решиш по възможно най-чистия и ясен начин (подхождащ на задачата), не да покажеш сложни трикове. И такива задачи ще има, не се притеснявай. :)

Окей, имам няколко въпроса преди да пиша друго решение.

  1. boiling_points, melting_points vs константи, които правят същото, или просто да ги преименувам на default_boiling_points...? От SCREAMING CASE ме болят очите.
  2. Хвърлям някакъв ерор, ако не очаквам такъв юнит, в момента това не ме кефи особено, някакви други предположения?
  3. Понеже не ме кефи идеята да имам if-else statements, (най-много заради факта, че има още много други мерни единици за температура), си мислех какъв би бил добър начин да се напише това, с цел лесно да може да го разширявам за в бъдеще? Например да имам Temperatures#all, който връща масив от класове от температури (Celsius, Kelvin ...), които да наследяват от Temperature, така че да е нужно да имплементират някакъв даден метод, с който да си ги мачвам по буква? И след това да им викам #new.

Разбира се, мога да го изхакя на бързо на 20 реда с много ифове, но това не е забавно. Мерси предварително за фийдбака :)

  1. От SCREAMING CASE ме болят очите. - с това се свиква, освен това константите все пак трябва да се различават по нещо. А този случай е идеален за константи.
  2. Не прави нищо при липса на unit. Нормалната практика в Ruby е да не правиш подобни проверки - ако не ти е подадено нещо смислено, човекът, който ти използва функцията ще разбере за това като гръмне. Или, ако трябва да се проверява - това ще бъде направено преди да ти се извика функцията - като валидация в потребителския интерфейс.
    Освен това, тук сме казали изрично, че няма да подаваме невалидни данни.
  3. Хеш с ламбда функции. Иначе if/elsif или един case/when за тази задача е напълно нормално нещо. Направи сметката на две стъпки - две конвертирания вместо едно директно. Така ще имаш два case-а с по 3 случая, не вложени if-ове.
    Ако искаш да поддържаш нова мерна единица просто добавяш 2 реда - от нея в C и обратно. Ако го оставиш така ще трябва да добавиш N нови функции - колкото мерни единици е имало преди * 2.

Много сложно го мислиш - реши проблема просто и ако има нужда - тогава усложнявай и обобщавай.

Никола обнови решението на 14.10.2016 21:53 (преди над 7 години)

module Temperature
- KNOWN_UNITS = %w(K F C).freeze
-
module Converter
- include Temperature
-
- class UnknownUnitError < StandardError; end
-
- def convert_between_temperature_units(temperature, from, to)
- raise UnknownUnitError unless known_units?([from, to])
- method_convert = "#{from.downcase}_convert_#{to.downcase}"
- send(method_convert, temperature)
+ def convert_between_temperature_units(units, from, to)
+ temperature = convert_to_celsius[from][units]
+ convert_from_celsius[to][temperature]
end
private
- def c_convert_k(temperature)
- temperature + 273.15
+ def convert_to_celsius
+ {
+ 'K' => ->(temp) { (temp - 273.15) },
+ 'F' => ->(temp) { (temp - 32) / (9.0 / 5) },
+ 'C' => ->(temp) { temp }
+ }
end
- def c_convert_f(temperature)
- temperature * (9.0 / 5) + 32
+ def convert_from_celsius
+ {
+ 'K' => ->(temp) { temp + 273.15 },
+ 'F' => ->(temp) { temp * (9.0 / 5) + 32 },
+ 'C' => ->(temp) { temp }
+ }
end
-
- def k_convert_c(temperature)
- temperature - 273.15
- end
-
- def k_convert_f(temperature)
- in_celsius = k_convert_c(temperature)
- c_convert_f(in_celsius)
- end
-
- def f_convert_c(temperature)
- (temperature - 32) / (9.0 / 5)
- end
-
- def f_convert_k(temperature)
- in_celsius = f_convert_c(temperature)
- c_convert_k(in_celsius)
- end
-
- KNOWN_UNITS.each do |unit|
- name = unit.downcase
- converter = "#{name}_convert_#{name}"
- define_method(converter, -> (temp) { temp })
- end
end
module MeltingPoints
include Converter
+ MELTING_POINTS = {
+ 'water' => 0,
+ 'ethanol' => -114,
+ 'gold' => 1_064,
+ 'silver' => 961.8,
+ 'copper' => 1_085
+ }.freeze
+
def melting_point_of_substance(substance, unit, from: 'C')
- temperature = melting_points[substance]
+ temperature = MELTING_POINTS[substance]
convert_between_temperature_units(temperature, from, unit)
end
-
- private
-
- def melting_points
- {
- 'water' => 0,
- 'ethanol' => -114,
- 'gold' => 1_064,
- 'silver' => 961.8,
- 'copper' => 1_085
- }
- end
end
module BoilingPoints
include Converter
+
+ BOILING_POINTS = {
+ 'water' => 100,
+ 'ethanol' => 78.37,
+ 'gold' => 2_700,
+ 'silver' => 2_162,
+ 'copper' => 2_567
+ }.freeze
+
def boiling_point_of_substance(substance, unit, from: 'C')
- temperature = boiling_points[substance]
+ temperature = BOILING_POINTS[substance]
convert_between_temperature_units(temperature, from, unit)
end
-
- private
-
- def boiling_points
- {
- 'water' => 100,
- 'ethanol' => 78.37,
- 'gold' => 2_700,
- 'silver' => 2_162,
- 'copper' => 2_567
- }
- end
end
-
- def known_units?(units)
- units.all? { |unit| KNOWN_UNITS.include? unit }
- end
end
include Temperature::Converter
include Temperature::BoilingPoints
-include Temperature::MeltingPoints
+include Temperature::MeltingPoints