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 语句的方法?

首先,在条件中使用 orand 而不是 ||&& 在 Ruby 中不是惯用的,因为它们具有不同的优先级和可能并不总是如你所愿 (Style Guide reference)。 至于实际的问题,这样的事情更惯用 Ruby:

(0...minemap.length).cover?(x) && (0...minemap[0].length).cover?(y)

这使用 Range#cover? 检查 xy 是否在正确的范围内,并且 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? 检查 xy 是否在范围内:

return unless x.between?(0, WIDTH-1) && y.between?(0, HEIGHT-1)
# do something with minemap[x][y]

顺便说一句,使用带有坐标键的哈希而不是嵌套数组通常更容易,因此您可以使用:minemap[x, y] 而不是 minemap[x][y]