我想通过只读取一次字符串(O(n)时间复杂度)从Ruby中的字符串中提取一些信息。
以下是一个例子:
字符串如下所示:-location here -time 7:30pm -activity biking
我想要填充此信息的Ruby对象。所有关键字都是已知的,它们都是可选的。
def ActivityInfo
_attr_reader_ :location, :time, :activity
def initialize(str)
@location, @time, @activity = DEFAULT_LOCATION, DEFAULT_TIME, DEFAULT_ACTIVITY
# Here is how I was planning on implementing this
current_string = ""
next_parameter = nil # A reference to keep track of which parameter the current string is refering to
words = str.split
while !str.empty?
word = str.shift
case word
when "-location"
if !next_parameter.nil?
next_parameter.parameter = current_string # Set the parameter value to the current_string
current_string = ""
else
next_parameter = @location
when "-time"
if !next_parameter.nil?
next_parameter.parameter = current_string
current_string = ""
else
next_parameter = @time
when "-activity"
if !next_parameter.nil?
next_parameter.parameter = current_string
current_string = ""
else
next_parameter = @time
else
if !current_string.empty?
current_string += " "
end
current_string += word
end
end
end
end
所以基本上我只是不知道如何使变量成为另一个变量或方法的引用,以便我可以将其设置为特定值。或者也许只有另一种更有效的方法来实现这一目标?
谢谢!
答案 0 :(得分:2)
该字符串看起来很像命令行,并且有一些很好的Ruby模块可以解析它们,例如optparse
。
假设不是,这里有一种快速的方法将样本中的命令解析为哈希:
cmd = '-location here -time 7:30pm -activity biking'
Hash[*cmd.scan(/-(\w+) (\S+)/).flatten]
结果是:
{
"location" => "here",
"time" => "7:30pm",
"activity" => "biking"
}
将它扩展得更远:
class ActivityInfo
def initialize(h)
@location = h['location']
@time = h['time' ]
@activity = h['activity']
end
end
act = ActivityInfo.new(Hash[*cmd.scan(/-(\w+) (\S+)/).flatten])
将act
设置为ActivityInfo的实例,如下所示:
#<ActivityInfo:0x101142df8
@activity = "biking",
@location = "here",
@time = "7:30pm"
>
-
OP询问如何处理命令未标记-
或多个单词的情况。这些是等价的,但我更喜欢风格上的第一个:
irb(main):003:0> cmd.scan(/-((?:location|time|activity)) \s+ (\S+)/x)
[
[0] [
[0] "location",
[1] "here"
],
[1] [
[0] "time",
[1] "7:30pm"
],
[2] [
[0] "activity",
[1] "biking"
]
]
irb(main):004:0> cmd.scan(/-(location|time|activity) \s+ (\S+)/x)
[
[0] [
[0] "location",
[1] "here"
],
[1] [
[0] "time",
[1] "7:30pm"
],
[2] [
[0] "activity",
[1] "biking"
]
]
如果命令是多个单词,例如“at location”:
irb(main):009:0> cmd = '-at location here -time 7:30pm -activity biking'
"-at location here -time 7:30pm -activity biking"
irb(main):010:0>
irb(main):011:0* cmd.scan(/-((?:at \s location|time|activity)) \s+ (\S+)/x)
[
[0] [
[0] "at location",
[1] "here"
],
[1] [
[0] "time",
[1] "7:30pm"
],
[2] [
[0] "activity",
[1] "biking"
]
]
如果您需要更多灵活性,请查看Ruby的strscan
模块。您可以使用它来拆分字符串并找到命令及其参数。
答案 1 :(得分:1)
如果您只想轻松访问标志及其值,可以将字符串拆分为散列,其中每个标志都是一个键。例如:
options = Hash[ str.scan /-(\w+)\s+(\S+)/ ]
=> {"location"=>"here", "time"=>"7:30pm", "activity"=>"biking"}
然后,您可以直接引用值(例如options['location']
)或在键/值对中迭代哈希值。例如:
options.each_pair { |k, v| puts "%s %s" % [k, v] }
好吧,这是严重的过度工程,但我在这个问题上花了一点时间,因为我发现它很有趣。我没有声称以下内容很有用;我只是说我这很有趣。
如果你想解析你的选项标志并动态创建一组属性读者和设置一些实例变量而不必分别定义每个标志或变量,你可以做一下元编程。
# Set attribute readers and instance variables dynamically
# using Kernel#instance_eval.
class ActivityInfo
def initialize(str)
options = Hash[ str.scan /-(\w+)\s+(\S+)/ ]
options.each_pair do |k, v|
self.class.instance_eval { attr_reader k.to_sym }
instance_variable_set("@#{k}", v)
end
end
end
ActivityInfo.new '-location here -time 7:30pm -activity biking'
=> #<ActivityInfo:0x00000001b49398
@activity="biking",
@location="here",
@time="7:30pm">
老实说,我认为从选项哈希中明确设置变量,例如:
@activity = options['activity']`
会更清楚地传达你的意图(并且更具可读性),但是有其他选择总是好的。您的里程可能会有所不同。
答案 2 :(得分:0)
为什么Thor可以为你做重物时重新发明轮子?
class ActivityInfo < Thor
desc "record", "record details of your activity"
method_option :location, :type => :string, :aliases => "-l", :required => true
method_option :time, :type => :datetime, :aliases => "-t", :required => true
method_option :activity, :type => :string, :aliases => "-a", :required => true
def record
location = options[:location]
time = options[:time]
activity = options[:activity]
# record details of the activity
end
end
将根据您指定的数据类型为您解析选项。您可以通过编程方式调用它:
task = ActivityInfo.new([], {location: 'NYC', time: Time.now, activity: 'Chilling out'})
task.record
或者从命令行:thor activity_info:record -l NYC -t "2012-06-23 02:30:00" -a "Chilling out"