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