在这个Ruby代码中,除了使用基于时间间隔分配标签的巨型if/else之外,还有另一种选择
我继承了一个Ruby脚本,它读取一个csv文件,其中包含“电视节目”,有一个开始时间和结束时间,格式如下:在这个Ruby代码中,除了使用基于时间间隔分配标签的巨型if/else之外,还有另一种选择,ruby,algorithm,data-structures,matrix,Ruby,Algorithm,Data Structures,Matrix,我继承了一个Ruby脚本,它读取一个csv文件,其中包含“电视节目”,有一个开始时间和结束时间,格式如下: start_time = 20:00:00 end_time = 20:45:00 目标是根据开始和结束时间为每个电视节目分配一个“时间段”(以下值之一): 23:00:00 - 05:00:00 = Late Night = l 05:00:00 - 09:00:00 = Morning = m 09:00:00 - 17:00:00 = Day Time = d
start_time = 20:00:00
end_time = 20:45:00
目标是根据开始和结束时间为每个电视节目分配一个“时间段”(以下值之一):
23:00:00 - 05:00:00 = Late Night = l
05:00:00 - 09:00:00 = Morning = m
09:00:00 - 17:00:00 = Day Time = d
17:00:00 - 20:00:00 = Evening = e
20:00:00 - 23:00:00 = Prime = p
现在我有一个巨大的if/else语句,大约有100行Ruby代码:
if(start_time >= 50000 && start_time < 90000) #start time is between 5 and 9 am
if(end_time <= 90000)
@timeSlot = ["Morning"]
puts "timeSlot = [Morning]"
elsif(end_time <= 170000 && end_time > 90000)
@timeSlot = ["Morning", "Daytime"]
puts "timeSlot = [Morning, Daytime]"
elsif(end_time <= 200000 && end_time > 90000 && end_time > 170000)
@timeSlot =["Morning", "Daytime", "Evening"]
puts "timeSlot =[Morning, Daytime, Evening]"
elsif(end_time <= 230000 && end_time > 90000 && end_time > 170000 && end_time > 200000)
@timeSlot =["Morning", "Daytime", "Evening", "Prime"]
puts "timeSlot =[Morning, Daytime, Evening, Prime]"
else
@timeSlot =["Morning", "Daytime", "Evening", "Prime", "LateNight"]
puts "timeSlot =[Morning, Daytime, Evening, Prime, LateNight]"
end
elsif (start_time >= 90000 && start_time < 170000)
.........
........
end
如果(开始时间>=50000和开始时间<90000)开始时间在上午5点到9点之间
如果(结束时间170000)
@时隙=[“上午”、“白天”、“晚上”]
将“时间段=[早上、白天、晚上]”
elsif(结束时间90000和结束时间>170000和结束时间>200000)
@时隙=[“上午”、“白天”、“晚上”、“黄金时段”]
将“时间段=[早上、白天、晚上、黄金时段]”
其他的
@时隙=[“上午”、“白天”、“晚上”、“黄金时段”、“深夜”]
将“时间段=[早上、白天、晚上、黄金时段、深夜]”
终止
elsif(开始时间>=90000和开始时间<170000)
.........
........
终止
我试图改变实现,使代码易于维护、扩展和阅读。
我对这个问题的第一次尝试是使用excel中的矩阵直观地解决它,如图所示
这就是直观显示的问题。现在的问题是如何在代码中以有效的方式实现这一点
欢迎提供任何建议有几种方法可以优化代码库
- 用下划线写数字(ruby在数字中忽略它们,但它们增强了可读性)<代码>5_00_00等于上午5点。这样更容易将小时与分钟分开
- 您可以在
中为每个时隙制定条件,这为条件提供了一个可读的名称Proc
- 电视节目的老虎机阵列是可变的。因此,如果条件适用,我们可以添加一个匹配时间慢<代码>show.slots我将假设问题“…以有效的方式在代码中执行此操作?”是关于如何提出一个更优雅的解决方案,而不是获得一个更高效的运行时算法
首先,我注意到嵌套的if语句包含冗余条件检查,例如
然后在if语句中使用这些变量。这将减少打字错误,可能会意外地超过50000个,等等elsif(end_time <= 200000 && end_time > 90000 && end_time > 170000)
希望这是朝着正确方向的有益推动。尽管您的业务规则很少有其他人提到的缺点,但它很有趣。下面是我如何处理它的require 'time' start_time = "20:00:00" end_time = "20:45:00" start_time = Time.strptime(start_time,"%H:%M:%S") end_time = Time.strptime(end_time,"%H:%M:%S") slot_times = { :late_night => {start: "23:00:00", end: "05:00:00", slot: "Late Night"}, :morning => {start: "05:00:00", end: "09:00:00", slot: "Morning"}, :day_time => {start: "09:00:00", end: "17:00:00", slot: "Day Time"}, :evening => {start: "17:00:00", end: "20:00:00", slot: "Evening"}, :prime => {start: "20:00:00", end: "23:00:00", slot: "Prime"} } slot_times.each do |k,v| x,y = Time.strptime(v[:start],"%H:%M:%S"), Time.strptime(v[:end],"%H:%M:%S") puts v[:slot] if start_time.between?(x,y) || end_time.between?(x,y) #=> Evening, Prime end
还有一个变体class Time\u Slot
[:深夜、上午、白天、时间、晚上] require 'time' RANGES = [ ["00:00:00", "Late Night"], ["05:00:00", "Morning"], ["09:00:00", "Day Time"], ["17:00:00", "Evening"], ["20:00:00", "Prime"], ["23:00:00", "Late Night"]] NBR_PERIODS = RANGES.size LAST_PERIOD = NBR_PERIODS - 1
你的情况有很多冗余。例如,
与end\u time>90000和&end\u time>170000
相同。你没有明确你要遵守的规则。问题还不清楚。你需要确定:早晨和:白天的时间范围(明显的剪切粘贴嘘声)。您不应该使用秒而不是小时(或者修改end\u time>170000
)吗?例如,23:00开始的节目应该是深夜,而不是黄金时段和深夜。此外,我建议您按时间顺序列出标签,因此深夜打印时是最后一次(并且只打印一次)。顺便说一句,其他人,为什么在这个问题上投反对票?这在语法上是正确的,但在逻辑上是不正确的。抱歉,如果您选择开始时间=21:00:00,结束时间=04:00:00,则此操作无效。输出是基本的。它应该是程序运行的所有时间段。这对我很有效。这是最好的解决方案,因为它不需要硬编码时间段,而且它是一个单独的类,是模块化的。非常感谢!!:)这是一个优越的解决方案,因为它将策略与实施分离。这些范围甚至可以从数据库或文件中加载。间隔?
TIME_5AM = 50000 TIME_9AM = 90000 ...
require 'time' start_time = "20:00:00" end_time = "20:45:00" start_time = Time.strptime(start_time,"%H:%M:%S") end_time = Time.strptime(end_time,"%H:%M:%S") slot_times = { :late_night => {start: "23:00:00", end: "05:00:00", slot: "Late Night"}, :morning => {start: "05:00:00", end: "09:00:00", slot: "Morning"}, :day_time => {start: "09:00:00", end: "17:00:00", slot: "Day Time"}, :evening => {start: "17:00:00", end: "20:00:00", slot: "Evening"}, :prime => {start: "20:00:00", end: "23:00:00", slot: "Prime"} } slot_times.each do |k,v| x,y = Time.strptime(v[:start],"%H:%M:%S"), Time.strptime(v[:end],"%H:%M:%S") puts v[:slot] if start_time.between?(x,y) || end_time.between?(x,y) #=> Evening, Prime end
class Time_Slot < Struct.new(:name, :begin, :end) def overlap?(range) range.include?(self.begin) || range.begin.between?(self.begin, self.end) end end TIME_SLOTS = [ Time_Slot.new(:Late_night, 0, 5), Time_Slot.new(:Morning, 5, 9), Time_Slot.new(:Day_time, 9, 17), Time_Slot.new(:Evening, 17, 20), Time_Slot.new(:Prime, 20, 23), Time_Slot.new(:Late_night, 23, 24)] def calc_slots(start, stop) range = start...stop TIMESLOTS.select{|ts| ts.overlap?(range)}.map(&:name) end p calc_slots(1,20) #=>[:Late_night, :Morning, :Day_time, :Evening]
require 'time' RANGES = [ ["00:00:00", "Late Night"], ["05:00:00", "Morning"], ["09:00:00", "Day Time"], ["17:00:00", "Evening"], ["20:00:00", "Prime"], ["23:00:00", "Late Night"]] NBR_PERIODS = RANGES.size LAST_PERIOD = NBR_PERIODS - 1
class TimesToList def initialize @ranges = [] RANGES.each { |r| @ranges << Time.strptime(r.first,"%H:%M:%S") } end def list_of_periods(start_time, end_time) start_period = secs_to_period(Time.strptime(start_time, "%H:%M:%S")) end_period = secs_to_period(Time.strptime(end_time, "%H:%M:%S")) ((start_time <= end_time) ? (start_period..end_period).to_a : (start_period..LAST_PERIOD).to_a + (0..end_period).to_a).map {|p| p == 0 ? LAST_PERIOD : p}.uniq.sort.map {|p| RANGES[p].last} end private def secs_to_period(t) NBR_PERIODS.times {|i| return i if i == LAST_PERIOD or t < @ranges[i+1]} end end
TimesToList.new.list_of_periods("23:48:00", "10:15:00") # => ["Morning", "Day Time", "Late Night"]