Ruby—链接大量 OR 语句的好方法? (检查数组越界)
Ruby—Nice way to chain lots of OR statements? (checking against array out-of-bounds)
我正在做一个编码谜题,你是一个数组中的矿工,你不能越界。我有这个代码:
if x > minemap.length-1 or x < 0 or y > minemap[0].length-1 or y < 0
return false
end
是否有 nicer/cleaner/one-thing-per-line 链接大量 OR 语句的方法?
首先,在条件中使用 or
和 and
而不是 ||
和 &&
在 Ruby 中不是惯用的,因为它们具有不同的优先级和可能并不总是如你所愿 (Style Guide reference)。
至于实际的问题,这样的事情更惯用 Ruby:
(0...minemap.length).cover?(x) && (0...minemap[0].length).cover?(y)
这使用 Range#cover? 检查 x
和 y
是否在正确的范围内,并且 returns false
除非是正确的。
您可以使用或等于赋值运算符 ||=
,将每个条件放在一个新行上:
is_true = false # initial condition
is_true ||= x > minemap.length-1
is_true ||= x < 0
is_true ||= y > minemap[0].length-1
is_true ||= y < 0
is_true # will be true or false
除了其他答案之外,您不需要显式 return 真值或假值,因为这是通过表达式的计算隐式完成的:
# will return true or false
def out_of_bounds?
x > minemap.length-1 || x < 0 || y > minemap[0].length-1 || y < 0
end
你也可以创建一个相应的方法来取反:
def in_bounds?
!out_of_bounds?
end
虽然我支持@MichaelKohl 的回答,但它确实创建了临时对象,因为每个 if
语句都为内存分配了两个范围对象,并且调用了它们的 #cover
实例方法。
这些对象存在于等待 GC(垃圾收集器)工作的内存中,这很神奇,它们的存在和使用都会浪费资源和 CPU 周期。
这可能不是问题,但它可能会降低性能,并且在循环中使用或如果经常调用 if
语句时可能会成为问题。
另一方面...
IF 你知道你的数组从不包含 nil
(即,如果数组包含有效的 true
/ false
或其他数字值,您可以简单地使用:
unless x < 0 || y < 0 || minemap[x].nil? || minemap[x][y].nil?
# ...
end
或如果 你知道 x 和 y 总是 0 或正数,使用数学...
if (minemap.length - x) > 0 && (minemap[x].length - y) > 0
# ...
end
或者使用带有附加条件的数学...
if x >=0 && y>= 0 && (minemap.length - x) > 0 && (minemap[x].length - y) > 0
# ...
end
祝你好运。
我会经常利用 any?
和 all?
并变得更加地道:
class Something
def the_question?
[ thing_1, thing_2, thing_3 ].any?
end
protected
def thing_1
# …
end
def thing_2
# …
end
def thing_3
# …
end
end
在您的特定情况下,您 运行 遇到了问题,因为您的个人陈述一团糟:他们在局部变量上调用 []
方法,然后在其上调用方法.换句话说,这个 context/method 可能正在处理它应该通过接口与之交互的对象的细节(而不是实现细节)。
N 个范围和 N 个相应点的最通用解决方案:
ranges = [0...minemap.length, 0...minemap[0].length]
values = [x, y]
ranges.zip(values).map { |e| e.reduce(&:cover?) }.all?
#⇒ true
也可能明确查询第一个不适合的:
ranges.zip(values).detect { |e| !e.reduce(&:cover?) }
全部不适合:
ranges.zip(values).reject { |e| e.reduce(&:cover?) }
等等
假设地图具有固定大小,我会在创建地图之前将地图的宽度和高度存储在变量或常量中:
HEIGHT = 5
WIDTH = 10
@minemap = Array.new(WIDTH) { Array.new(HEIGHT) }
然后,我将使用 between?
检查 x
和 y
是否在范围内:
return unless x.between?(0, WIDTH-1) && y.between?(0, HEIGHT-1)
# do something with minemap[x][y]
顺便说一句,使用带有坐标键的哈希而不是嵌套数组通常更容易,因此您可以使用:minemap[x, y]
而不是 minemap[x][y]
。
我正在做一个编码谜题,你是一个数组中的矿工,你不能越界。我有这个代码:
if x > minemap.length-1 or x < 0 or y > minemap[0].length-1 or y < 0
return false
end
是否有 nicer/cleaner/one-thing-per-line 链接大量 OR 语句的方法?
首先,在条件中使用 or
和 and
而不是 ||
和 &&
在 Ruby 中不是惯用的,因为它们具有不同的优先级和可能并不总是如你所愿 (Style Guide reference)。
至于实际的问题,这样的事情更惯用 Ruby:
(0...minemap.length).cover?(x) && (0...minemap[0].length).cover?(y)
这使用 Range#cover? 检查 x
和 y
是否在正确的范围内,并且 returns false
除非是正确的。
您可以使用或等于赋值运算符 ||=
,将每个条件放在一个新行上:
is_true = false # initial condition
is_true ||= x > minemap.length-1
is_true ||= x < 0
is_true ||= y > minemap[0].length-1
is_true ||= y < 0
is_true # will be true or false
除了其他答案之外,您不需要显式 return 真值或假值,因为这是通过表达式的计算隐式完成的:
# will return true or false
def out_of_bounds?
x > minemap.length-1 || x < 0 || y > minemap[0].length-1 || y < 0
end
你也可以创建一个相应的方法来取反:
def in_bounds?
!out_of_bounds?
end
虽然我支持@MichaelKohl 的回答,但它确实创建了临时对象,因为每个 if
语句都为内存分配了两个范围对象,并且调用了它们的 #cover
实例方法。
这些对象存在于等待 GC(垃圾收集器)工作的内存中,这很神奇,它们的存在和使用都会浪费资源和 CPU 周期。
这可能不是问题,但它可能会降低性能,并且在循环中使用或如果经常调用 if
语句时可能会成为问题。
另一方面...
IF 你知道你的数组从不包含 nil
(即,如果数组包含有效的 true
/ false
或其他数字值,您可以简单地使用:
unless x < 0 || y < 0 || minemap[x].nil? || minemap[x][y].nil?
# ...
end
或如果 你知道 x 和 y 总是 0 或正数,使用数学...
if (minemap.length - x) > 0 && (minemap[x].length - y) > 0
# ...
end
或者使用带有附加条件的数学...
if x >=0 && y>= 0 && (minemap.length - x) > 0 && (minemap[x].length - y) > 0
# ...
end
祝你好运。
我会经常利用 any?
和 all?
并变得更加地道:
class Something
def the_question?
[ thing_1, thing_2, thing_3 ].any?
end
protected
def thing_1
# …
end
def thing_2
# …
end
def thing_3
# …
end
end
在您的特定情况下,您 运行 遇到了问题,因为您的个人陈述一团糟:他们在局部变量上调用 []
方法,然后在其上调用方法.换句话说,这个 context/method 可能正在处理它应该通过接口与之交互的对象的细节(而不是实现细节)。
N 个范围和 N 个相应点的最通用解决方案:
ranges = [0...minemap.length, 0...minemap[0].length]
values = [x, y]
ranges.zip(values).map { |e| e.reduce(&:cover?) }.all?
#⇒ true
也可能明确查询第一个不适合的:
ranges.zip(values).detect { |e| !e.reduce(&:cover?) }
全部不适合:
ranges.zip(values).reject { |e| e.reduce(&:cover?) }
等等
假设地图具有固定大小,我会在创建地图之前将地图的宽度和高度存储在变量或常量中:
HEIGHT = 5
WIDTH = 10
@minemap = Array.new(WIDTH) { Array.new(HEIGHT) }
然后,我将使用 between?
检查 x
和 y
是否在范围内:
return unless x.between?(0, WIDTH-1) && y.between?(0, HEIGHT-1)
# do something with minemap[x][y]
顺便说一句,使用带有坐标键的哈希而不是嵌套数组通常更容易,因此您可以使用:minemap[x, y]
而不是 minemap[x][y]
。