比较每个循环中的值

时间:2018-01-22 12:49:32

标签: ruby rspec

我有一个应用程序,其目的是从命令行获取坐标,并从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传递到地址时执行某些错误处理的任何建议。

3 个答案:

答案 0 :(得分: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值。如果它包含在测试脚本中,那么我们就不会。

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

规格/ 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 :(得分: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

我希望这会有所帮助。

答案 2 :(得分: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

相关问题