Ruby中的嵌套条件语句

时间:2015-06-04 17:08:53

标签: ruby-on-rails ruby

我的问题有两个方面:

  1. 我没有意识到我可以像这样嵌套多个条件。这看起来很糟糕。我想想我知道这里发生了什么,但真的可以解释一下这样的人我能真正理解这个概念吗?

  2. 由于我不太了解嵌套条件,我在重构上有点迷失,这个区域我开始时很弱。你们是否会如此善良地提出一些可能的重构解决方案和解释?这将极大地帮助我有限的理解。

  3. def valid_triangle?(a, b, c, sum)
      if a != 0 || b != 0 || c != 0
        if a >= b
          largest = a
          sum += b
        else largest = b
          sum += a
        end
        if c > largest
          sum += largest
          largest = c
        else sum += c
        end
        if sum > largest
          return "true"
        else return "false"
        end
      else return false
      end
    end
    

3 个答案:

答案 0 :(得分:4)

你可以通过类似Ruby的方式来减少这种情况:

def valid_triangle?(*sides)
  case (sides.length)
  when 3
    sides.sort!

    sides[0] + sides[1] >= sides[2]
  else
    false
  end
end

尽可能尝试将您的逻辑表达为对数据的一系列转换,它通常更容易理解。在这种情况下,将传入点视为一个数组并对它们进行排序,而不是为每个点设置特殊情况逻辑。这个特殊的案例代码总是很麻烦,难以测试,如果你犯了一个小错误就容易出现细微的失败。

值得注意的是,在Ruby中,您应该将if语句格式化为:

if condition
  # ...
elsif condition
  # ...
else
  # ...
end

else之后包含代码可能是有效的语法,但要查看它会让人感到困惑。您似乎正在针对largest == b进行测试并犯了一个错误,无意中执行了一项任务,当真的是else块的内容时,不是elusive的条件。

可以轻松更新此代码以包含负长度测试,例如在您的逻辑中添加sides[0] > 0

此外,包含Ruby 隐式 return,其中提供的最后一个值是从方法返回的值。 case语句实际上传播了触发值的值,这使得传递信息变得非常方便。

答案 1 :(得分:1)

嵌套条件

实际上,嵌套条件的概念与任何其他类型的条件的概念没有太大的不同。条件控制是否执行其中的所有代码。因此,如果将一个条件嵌套在另一个条件内,则外条件将控制是否执行内部条件。

some_var = 5
if some_var > 1
  # Code here will be executed
  if some_var > 10
    # Code here will not be executed
  end
end

if some_var > 10
  # Code here will not be executed
  # The following conditional won't be checked at all,
  # because execution won't reach this point.
  if some_var > 1
    # This won't be executed.
  end
end
# The execution point will immediately jump to here once
# the some_var > 10 condition is determined to be false.

重构代码

至于重构代码,这里有很多东西可以改进。例如,嵌套条件通常可以而且应该避免。不是因为它们难以理解,而是因为通常有更多可读的替代方案。

例如,您的代码将整个方法封装在一个大的条件中,基本上说“除非这个条件为真,否则不执行此方法的其余部分”:

if a != 0 || b != 0 || c != 0
  # ...
else
  return false # Note: Statements are conventionally
               # placed on the next line after else.
               # Not on the same line.
end

这可以通过在方法开头包含一个条件来避免,如果条件为假,则立即返回:

return false unless a != 0 || b != 0 || c != 0

我注意到的另一件事是你在某些情况下返回字符串而不是布尔值:

if sum > largest
  return "true"
else
  return "false"
end

这不是一个好主意。而是返回一个实际的布尔值:

if sum > largest
  return true
else
  return false
end

事实上,这整个条件并不是那么有用。 sum > largest已经在一个案例中评估为true而在另一个案例中评估为false,因此您可以:

return sum > largest

另外,为什么要将sum作为参数传递?那不应该总是从零开始吗?您可以从方法签名中删除该语句,只需在方法体中将sum初始化为0:

sum = 0

现在让我们看看你的代码是什么样的:

def valid_triangle?(a, b, c)
  return false unless a != 0 || b != 0 || c != 0

  sum = 0

  if a >= b
    largest = a
    sum += b
  else
    largest = b
    sum += a
  end

  if c > largest
    sum += largest
    largest = c
  else
    sum += c
  end

  return sum > largest
end

看起来好多了。中间的代码仍然相当复杂。让我们暂时考虑一下......似乎你想把largest设置为三边的最大值,然后将总和设置为剩余边的总和。使用数组排序非常容易:

sides = [a, b, c].sort

现在,sides包含所有方面的排序数组。最后一个元素将是最大的...

largest = sides.last

......其他两方可用于查找总和:

sum = sides[0] + sides[1]

现在让我们看看我们在哪里:

def valid_triangle?(a, b, c)
  return false unless a != 0 || b != 0 || c != 0

  sides = [a, b, c].sort

  largest = sides.last
  sum = sides[0] + sides[2]

  return sum > largest
end

尼斯!

我怎么做

通过更好地理解Ruby的一些内置方法,可以使这更简单。我不会详细介绍所有细节,但是这里我可能会编写这段代码:

def valid_triangle?(a, b, c)
  sides = [a, b, c].sort
  sides.all?{|s| s != 0} && sides.pop <= sides.reduce(:+)
end

这是如何工作的?让我分解一下。

首先,和以前一样,我们将所有边都收集到一个排序列表中,这样最小边是第一个元素,最大边是最后一个元素。这将使我们以后的计算变得更加容易。

sides = [a, b, c].sort

接下来,我们检查所有方面是否为非零。

sides.all?{|s| s != 0}

然后,我们检查最大的一侧是否至少与另一侧的总和一样小。我这样做是通过从排序列表中删除最大的一边,然后检查该值是否小于列表中其余两边的总和(使用sides.reduce(:+)找到):

sides.pop <= sides.reduce(:+)

这两个条件的结果与&&结合并从方法返回(Ruby自动返回方法中最后执行的语句,因此我们可以在这里省略return关键字。)

这是一个列表链接,指向我在此解决方案中使用的内置方法的文档。请检查文档中是否有任何您不理解的方法,以了解它们的工作原理:

答案 2 :(得分:0)

在不触及核心算法的情况下,您可以采取以下措施来改进方法结构。

def valid_triangle?(a, b, c, sum)

  # terminate early if inputs are not in expected state
  return false if [a,b,c].include? 0

    if a >= b
      largest = a
      sum += b
    else 
      largest = b
      sum += a
    end

    # space to separate this from the previous if/else block
    if c > largest
      sum += largest
      largest = c
    else 
      sum += c
    end

  # be consistent on return type (string or boolean)
  sum > largest

  # ternary if can be used if return type is not a boolean 
  # strings are not recommended for true/false values though
  # sum > largest ? "true" : "false"
end