-1

我将以下文本放入一些 Ruby 对象中,以便我可以将它们写入数据库以在 Rails 应用程序中使用。这些数据是波浪预报模型的一些输出,该模型显示了海洋中特定点的海洋膨胀。第一列是日期和小时,然后是组合膨胀(对此不感兴趣),然后是单个膨胀,在任何一个小时内可能出现 1 到 6 个膨胀。

 +-------+-----------+-----------------+-----------------+-----------------+
 | day & |  Hst  n x |    Hs   Tp  dir |    Hs   Tp  dir |    Hs   Tp  dir |
 |  hour |  (m)  - - |    (m)  (s) (d) |    (m)  (s) (d) |    (m)  (s) (d) |
 +-------+-----------+-----------------+-----------------+-----------------+
 | 15  3 | 0.94  3   |   0.74  4.4  69 |   0.43 10.6 186 |   0.39  4.8 351 |
 | 15  4 | 0.90  3   |   0.71  4.2  68 |   0.43 10.7 186 |   0.34  4.7 347 |
 | 15  5 | 0.85  3   |   0.65  4.1  72 |   0.42 10.7 186 |   0.35  4.4 351 |
 | 15  6 | 0.81  3   |   0.61  4.2  72 |   0.42 10.7 186 |   0.32  4.5 350 |
 | 15  7 | 0.77  2   |                 |   0.41 10.8 186 |                 |
 | 15  8 | 0.73  2   |                 |   0.40 10.8 186 |                 |
 | 15  9 | 0.70  3   |   0.51  3.8  74 |   0.40 10.7 187 |   0.26  4.1 350 |
 | 15 10 | 0.67  3   |   0.49  3.8  73 |   0.39 10.7 187 |   0.24  4.2 349 |
 | 15 11 | 0.65  3   |   0.47  3.7  73 |   0.38 10.7 186 |   0.23  4.1 352 |
 | 15 12 | 0.63  2   |                 |   0.37 10.7 187 |                 |
 | 15 13 | 0.63  2   |                 |   0.35 10.6 187 |                 |

我对日期、涌浪数量以及每次涌浪的信息感兴趣。我所追求的是一个包含天/小时作为键的对象,并且还包含每个膨胀的单独数据。涌浪的数量每小时会有所不同。如果我加载行:

| 15 11 | 0.65  3   |   0.47  3.7  73 |   0.38 10.7 186 |   0.23  4.1 352 |

我想通过以下调用从对象中获取信息:

@forecast.date              #=> 15:11
@forecast.numswells         #=> 3 for the total swells present on that date 
@forecast.swell.1.height    #=> 0.47
@forecast.swell.1.direction #=> 73
@forecast.swell.3           #=> a swell object with all info in it for swell 3

我认为我需要的是一个具有可变长度存储其他对象的对象。那可能吗?关于我应该阅读什么的任何指示?

4

2 回答 2

7

这是一个解析该文本行的对象:

class DayOnTheWater

  Swell = Struct.new(:hs, :tp, :d)

  attr_reader :day, :hour

  def initialize data_line
    data = data_line.split(/[|\s]/).delete_if {|col| col.empty?}
    @day = data.shift
    @hour = data.shift
    2.times { data.shift } # remove combined swell data
    @swells = data.each_slice(3).map { |hs, tp, d| Swell.new(hs.to_f, tp.to_f, d.to_i) }
  end

  def swells
    @swells.to_enum
  end

end

example = '| 15 11 | 0.65  3   |   0.47  3.7  73 |   0.38 10.7 186 |   0.23  4.1 352 |'

object =  DayOnTheWater.new(example)
puts "day: #{object.day}"
puts "hour: #{object.hour}"
puts "\nSWELL DATA"
object.swells.each { |swell| puts swell.inspect }
puts "\nExample statistic:"
puts "Max Hs: #{object.swells.max { |a,b| a.hs <=> b.hs }}"

输出:

day: 15
hour: 11

SWELL DATA
#<struct DayOnTheWater::Swell hs=0.47, tp=3.7, d=73>
#<struct DayOnTheWater::Swell hs=0.38, tp=10.7, d=186>
#<struct DayOnTheWater::Swell hs=0.23, tp=4.1, d=352>

Example statistic:
Max Hs: #<struct DayOnTheWater::Swell hs=0.47, tp=3.7, d=73>

结构适用于简单的值对象。它们有缺点,但输入起来很快。对于公开事物列表的对象,我总是返回一个 Enumerable 而从不返回底层数据对象(这里的 swells 方法)。这很重要,因为 Enumerables 是不可变的(只读);这可以防止其他对象更改数据。

于 2012-11-16T20:45:58.187 回答
1

一些建议:

  • 你应该更好地使用swells复数而不是swell一组东西。
  • 对于不同长度的东西,Array 是最合适的。当引用 Array 的特定元素时,您应该使用[]方法而不是1,2等方法。(可能首先这是不可能的)。在这种情况下,请注意您应该从 开始0,而不是1
  • length您可以通过在膨胀上应用该方法轻松获得膨胀的数量。你不应该有一个特定的方法numswells,除非它会被特别频繁地使用。

我会做这样的事情:

data =
"| 15 11 | 0.65  3   |   0.47  3.7  73 |   0.38 10.7 186 |   0.23  4.1 352 |"

class Forecast
  attr_reader :date, :swells
  def initialize string
    _, date, _, swells = string.split("|", 4)
    @date = date.scan(/\d+/).join(":")
    @swells = swells.scan(/[^\|]+/).select{|s| s =~ /\S/}.map{|s| Swell.new(s)}
  end
end

class Swell
  attr_reader :height, :tp, :direction
  def initialize string
    @height, @tp, @direction = string.split(/\s+/).drop(1).map(&:to_f)
  end
end

@forecast = Forecast.new(data)
p @forecast.date
p @forecast.swells.length
p @forecast.swells[0].height
p @forecast.swells[0].direction
p @forecast.swells[2]

#=> "15:11"
#=> 3
#=> 0.47
#=> 73.0
#=> #<Swell:0x000000016401d0 @height=0.23, @tp=4.1, @direction=352.0>
于 2012-11-17T04:41:42.060 回答