比较每个循环中的值

问题描述 投票:0回答:3

我有一个应用程序,其目的是从命令行获取坐标,并从0,0到每个坐标输出方向,例如0,0到1,1将是东北方的EN。当应用程序到达该坐标时,它将在丢弃传递中输出D.所以,如果我输入:

“ ./testapplication.rb "5x5 (2, 2) (3, 5)"

输出是:

EENNDENNND

到目前为止,我有以下内容:

#!/home/eamonn/.rbenv/shims/ruby

instructions = ''
addresses = ARGV.to_s.scan(/\(([^\)]+)\)/)
starting_point = ['0, 0']

addresses.each do |point|
    starting_point = starting_point[0].split(", ")
    destination = point[0].split(", ")
    x = destination[0].to_i - starting_point[0].to_i
    y = destination[1].to_i - starting_point[1].to_i

    if x < 0
        instructions << 'W' * x.abs 
    elsif x > 0
        instructions << 'E' * x 
    end
    if y < 0
        instructions << 'S' * y.abs
    elsif y > 0
        instructions << 'N' * y 
    end

    instructions << "D" 
    starting_point = point

end

puts instructions

虽然应用程序工作,我觉得它有一些问题,如代码的效率,所以任何指针都受到赞赏。

此外,我习惯在rails应用程序上编写ruby,但是当我将其作为独立的ruby应用程序编写时,我对如何为此运行测试感到有点困惑。我一直在研究使用rspec并创建一个spec文件夹并在那里编写测试。我正在考虑的测试方法是:

describe 'testing that application' do
  it 'should return the right directions' do
    expect(navigate(["5x5 (2, 2) (3, 5)"])).to equal('EENNDENNND')
  end
end

关于我是否应该在此处包含对错误输入的测试或者仅在将ARGV传递到地址时执行一些错误处理的任何建议。

ruby rspec
3个回答
1
投票

您可以像这样重构代码。

def generate_instructions(input)
  addresses = input.to_s.scan(/\(([^\)]+)\)/)
  instructions = ''
# use like array
  starting_point = [0, 0]

  addresses.each do |point|
    sx, sy = starting_point # will set 1. param like first value
    arr = point[0].split(", ") # split by , and set inside array
    dx, dy = arr[0].to_i, arr[1].to_i # set array inside variables and re-type to -integer

    x = dx - sx
    y = dy - sy

    # add into instructions
    instructions << (x < 0 ? 'W' * x.abs : 'E' * x)
    instructions << (y < 0 ? 'S' * y.abs : 'N' * y)
    instructions << 'D'

    # reset points to destination (use it like array)
    starting_point = [dx, dy]
  end
  instructions
end

puts generate_instructions(ARGV) if ARGV

测试使用RSpec

require './testapplication.rb'

describe 'Test output for generate_instructions' do
  it 'return EENNDENNND' do
    expect(generate_instructions(["5x5 (2, 2) (3, 5)"])).to be == 'EENNDENNND'
  end
end

我希望这有帮助。


1
投票

有一些改进可以做,但只是为了解决测试的主题,这是你可以采取的一种方法。重构您的代码以将进程放在单个方法中,如此...

#!/home/eamonn/.rbenv/shims/ruby

def navigate(input)
  instructions = ''
  addresses = input.to_s.scan(/\(([^\)]+)\)/)
  starting_point = ['0, 0']

  addresses.each do |point|
      starting_point = starting_point[0].split(", ")
      destination = point[0].split(", ")
      x = destination[0].to_i - starting_point[0].to_i
      y = destination[1].to_i - starting_point[1].to_i

      if x < 0
          instructions << 'W' * x.abs 
      elsif x > 0
          instructions << 'E' * x 
      end
      if y < 0
          instructions << 'S' * y.abs
      elsif y > 0
          instructions << 'N' * y 
      end

      instructions << "D" 
      starting_point = point

  end
  instructions
end

if $0 == __FILE__
  puts navigate(ARGV)
end

底部的条件意味着如果您独立运行脚本,您将只会真正获取ARGV值。如果它包含在测试脚本中,那么我们不会。

要测试它,你需要......

spec / testapplication_spec.rb

require './testapplication.rb'

describe 'testing the navigate method' do
  it 'should return the right value' do
    expect(navigate(["5x5 (2, 2) (3, 5)"]).to eq('EENNDENNND')
  end
end

因此,在测试中,您模拟navigate方法将接收的ARGV输入,以查看它是否返回正确的结果。


1
投票

这是一种更像Ruby的计​​算方法。

str = ' ./testapplication.rb "5x5 (2, 2) (3, 5) (1, 2)"'

r = /
    \(     # match a left paren
    (\d+)  # match one or more digits in capture group 1
    ,[ ]+  # match a comma followed by one or more spaces
    (\d+)  # match one or more digits in capture group 2
    \)     # match a right paren
    /x     # free-spacing regex definition mode 

(传统上写的/\((\d+), +(\d+)\\)/)1。

str.scan(r).
    map { |pair| pair.map(&:to_i) }.
    unshift([0,0]).
    each_cons(2).
    map do |(fx,fy), (tx,ty)|
      ew = tx-fx
      ns = ty-fy
      "%s%sD" % [ew >= 0 ? 'E'*ew : 'W'*(-ew), ns > 0 ? 'N'*ns : 'S'*(-ns)]
    end.join
  #=> "EENNDENNNDWWSSSD" 

步骤如下2。

a = str.scan(/\((\d+), +(\d+)\)/)
  #=> [["2", "2"], ["3", "5"], ["1", "2"]
b = a.map { |pair| pair.map(&:to_i) }
  #=> [[2, 2], [3, 5], [1, 2]]
c = b.unshift([0,0])
  #=> [[0, 0], [2, 2], [3, 5], [1, 2]]
d = c.each_cons(2)
  #=> #<Enumerator: [[0, 0], [2, 2], [3, 5], [1, 2]]:each_cons(2)>

我们可以通过将枚举器d转换为数组来查看这些元素。

d.to_a
  #=> [[[0, 0], [2, 2]], [[2, 2], [3, 5]], [[3, 5], [1, 2]]]

继续,

e = d.map do |(fx,fy), (tx,ty)|
  ew = tx-fx
  ns = ty-fy
  "%s%sD" % [ew >= 0 ? 'E'*ew : 'W'*(-ew), ns > 0 ? 'N'*ns : 'S'*(-ns)]
end
  #=> ["EENND", "ENNND", "WWSSSD"]

最后,

e.join
  #=> "EENNDENNNDWWSSSD"

考虑由枚举器d生成并传递给块的第一个元素。使用消歧(也称为分解)为块变量赋值,并执行块计算3。

(fx,fy), (tx,ty) = d.next
  #=> [[0, 0], [2, 2]]
fx
  #=> 0
fy
  #=> 0
tx
  #=> 2
ty
  #=> 2
ew = tx-fx
  #=> 2
ns = ty-fy
  #=> 2
"%s%sD" % [ew >= 0 ? 'E'*ew : 'W'*(-ew), ns > 0 ? 'N'*ns : 'S'*(-ns)]
  #-> "%s%sD" % [true ? 'E'*ew : 'W'*(-ew), true ? 'N'*ns : 'S'*(-ns)]
  #-> "%s%sD" % ['E'*ew, 'N'*ns]
  #=> "EENND"

产生e的剩余计算是相似的。

1.当使用自由间距正则表达式定义模式时,空格必须包含在字符类中(如我所做)或以其他方式保护,因为所有不受保护的空格都被删除。请注意,在常规编写的正则表达式中存在空格 - 后跟加号。自由间隔模式的优点是它可以自我记录。

2.参见String#scan,特别是治疗正则表达组,Array#unshiftEnumerable#each_cons

3.见Enumerator#next

© www.soinside.com 2019 - 2024. All rights reserved.