0

我想通过仅读取一次字符串(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

所以基本上我只是不知道如何让一个变量成为另一个变量或方法的引用,这样我就可以将它设置为一个特定的值。或者也许还有另一种更有效的方法来实现这一目标?

谢谢!

4

3 回答 3

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模块。您可以使用它来拆分字符串并找到命令及其参数。

于 2012-07-17T00:14:27.917 回答
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']`

将更清楚地传达您的意图(并且更具可读性),但有替代方案总是好的。你的旅费可能会改变。

于 2012-07-17T00:19:37.723 回答
0

当托尔可以为您完成繁重的工作时,为什么要重新发明轮子?

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"

于 2012-07-17T11:44:49.167 回答