我正在尝试创建一个管理会议的Ruby on Rails网站。它应填充时隙,两者之间没有任何间隙。我已经达到了填补空位的程度。但在大多数情况下,它会留下一些空位。我无法在逻辑中找到流程。
class ConferenceService
def initialize(conference, temp_file)
self.first_track = conference.tracks.first
self.second_track = conference.tracks.last
self.file = temp_file
self.talks = []
end
def call
create_talks
set_track(1, 'Lunch')
set_track(2, 'Lunch')
set_track(1, 'Networking Event')
# set_track(2, 'Networking Event')
set_second_track_evening
end
private
def create_talks
file.read.split(/\n/).each do |line|
next if line.blank?
title = line.split(/\d|lightning/).first
length = line.scan(/\d+/).first
length = length.nil? ? 5 : length.to_i
talks << Talk.create(title: title, length: length)
end
end
attr_accessor :first_track, :second_track, :file, :talks
def set_track(track_number, track_portion)
track = track_number == 1 ? first_track : second_track
time = track_portion == 'Lunch' ? Time.zone.now.change(hour: 9) : Time.zone.now.change(hour: 13)
minutes = track_portion == 'Lunch' ? 180 : 240
talks.shuffle!
local_talks = []
n = 0
while local_talks.map(&:length).inject(0, &:+) < minutes
local_talks << talks[n]
n += 1
end
if local_talks.map(&:length).inject(0, &:+) == minutes
local_talks.each do |talk|
talk.start_time = time
track.talks << talk
time = time.advance(minutes: talk.length)
end
track.talks << Talk.create(title: track_portion, start_time: time, length: 60)
track.save
(0..local_talks.count - 1).each do |i|
talks.delete_at(i)
end
else
set_track(track_number, track_portion)
end
end
def set_second_track_evening
time = Time.zone.now.change(hour: 13)
talks.each do |talk|
talk.start_time = time
time = time.advance(minutes: talk.length)
end
second_track.talks << talks
second_track.talks << Talk.create(title: 'Networking Event', start_time: time.change(hour: 17), length: 60)
end
end
def create
@conference = Conference.new(conference_params)
build_tracks
conference_service = ConferenceService.new(@conference, input_file)
conference_service.call
respond_to do |format|
if @conference.save
format.html { redirect_to @conference, notice: 'Conference was successfully created.' }
format.json { render :show, status: :created, location: @conference }
else
format.html { render :new }
format.json { render json: @conference.errors, status: :unprocessable_entity }
end
end
end
def input_file
params['conference']['input_file']
end
Writing Fast Tests Against Enterprise Rails 60min
Overdoing it in Python 45min
Lua for the Masses 30min
Ruby Errors from Mismatched Gem Versions 45min
Common Ruby Errors 45min
Rails for Python Developers lightning
Communicating Over Distance 60min
Accounting-Driven Development 45min
Woah 30min
Sit Down and Write 30min
Pair Programming vs Noise 45min
Rails Magic 60min
Ruby on Rails: Why We Should Move On 60min
Clojure Ate Scala (on my project) 45min
Programming in the Boondocks of Seattle 30min
Ruby vs. Clojure for Back-End Development 30min
Ruby on Rails Legacy App Maintenance 60min
A World Without HackerNews 30min
User Interface CSS in Rails Apps 30min
调用set_track时出现undefined method `length' for nil:NilClass #line 42
答案 0 :(得分:1)
建议您在担心算法之前先做一些事情:
单独关注/单一责任。解析文件的代码应独立于运行业务逻辑的代码,该代码应独立于保存到数据库的代码。对于简单的逻辑(也可能是)来说,分离这些东西似乎是不必要的,但随着应用程序复杂性的增加,必要。
编写测试。当您重构代码时,您将希望确保它仍然有效。奖励:编写可以测试的代码会强制您创建可以理解的界面,这可以使代码更容易理解!
首先想出一个设计。阅读这段代码我不知道这些部分的意图是什么。我最喜欢的方法之一是使用班级,责任,合作者明信片(见https://en.wikipedia.org/wiki/Class-responsibility-collaboration_card和http://agilemodeling.com/artifacts/crcModel.htm)。
您似乎可以将此代码分解为:
TalkDouble
(或类似)。我还建议您只使用CSV而不是自己的自定义(并且难以解析)格式。例如:
class TalkScheduler
def schedule(talks, number_of_tracks: 2)
# Logic goes here, returns an array of `Tracks`
# each with a set of talks.
tracks = build_tracks(number_of_tracks)
talks.each do |talk|
tracks.sample.add_talk(talk)
end
tracks
end
def build_tracks(number)
(0..number).times.map do { Track.new }
end
end
但是,如果你正在寻找一种能够在开放空间中选择“最适合”可用会话的算法,那么你实际上是在尝试解决背包问题(https://en.wikipedia.org/wiki/Knapsack_problem)。由于通话长度有限(例如只有30,45和60),它可能不会变得组合硬,但是你会意识到你正在进入一个充满挑战的领域。
我也质疑任何人有能力以随机的谈话顺序创建会议而不是手工整理会议的价值。
在任何情况下,您都可以解决在给定时间范围内确定(随机?)会话选择的问题,具体如下:
class Schedule
SLOT_LENGTH = 15
attr_accessor :start, :length, :talks
def initialize(start:, length:)
@start = start
@length = length
@slots = length / SLOT_LENGTH
@talks = []
end
def add_talk(talk)
talks.push(talk)
end
def slots_remaining
slots - talks.map(&:length).sum / SLOT_LENGTH
end
def can_fit?(talk)
talk.length / SLOT_LENGTH <= slots_remaining
end
end
class TalkScheduler
def schedule(talks, schedules)
unscheduled_talks = talks.dup.shuffle # Always dup, even if you don't shuffle
schedules.each do |schedule|
while(talks.any?)
index = unscheduled_talks.index{|t| schedule.can_fit?(t) }
break unless index
talk = unscheduled_talks.delete_at(index)
schedule.add_talk(talk)
end
end
end
end
在决定将它们建模为会话或其他内容之前,我想更多关于模拟午餐,网络休息等等,但是使用这种类型的模式(存储由{{操纵的数据的简单ruby对象) 1}}包含复杂业务逻辑的类对我来说非常有帮助,可以简化处理复杂的工作流程,就像你在这里所做的一样。
祝你好运!