发布于 2026-01-05 5 阅读
0

提升你的 Ruby 技能:编写简洁代码、布尔值/条件语句、变量赋值、守卫子句、方法调用、安全导航器、哈希和数组,等等,还有更多!

提升你的 Ruby 技能:编写简洁的代码

布尔值/条件语句

变量赋值

警戒条款

调用方法

安全导航员

哈希和数组

等等,还有更多!

我们都经历过这种情况:提交了一个自认为非常完美、无比优秀的拉取请求(PR)。它被标记为“已批准”,但实际上却并非如此。我们的代码并没有想象中那么严谨。于是,我们向大家开放了PR,收到了许多关于如何进一步优化代码的宝贵建议。

这在编程新手阶段非常常见,事实上,我也是这么学到很多技巧的。多亏了许多资深开发者从一开始就阅读并点评我的代码,我才学会了如何编写简洁的代码。但我可以告诉你一个秘密:学习永无止境。我已经从事编程工作7年了,但仍然会收到关于如何改进代码的建议。

以下是一些常用的方法,您可以使用一些语法糖来简化基本的 Ruby 逻辑。语法糖是指编程语言(例如 Ruby)中的一种语法,旨在使代码更易于阅读或表达。

布尔值/条件语句

三元运算符

让我们从一些基本的布尔逻辑开始,来设置一个变量。如果y是偶数,我们想把它设置x为 1;如果y是奇数,我们想把它设置x为 2。以下是我们在 Ruby 入门时学到的方法。

if y.even?
  x = 1
else
  x = 2
end
Enter fullscreen mode Exit fullscreen mode

上面的代码包含了两条赋值语句。这意味着如果我们需要修改变量 x,就必须在两处进行修改,这并不理想。一种简化代码的方法是:

x = if y.even?
  1
else 
  2
end
Enter fullscreen mode Exit fullscreen mode

现在,我们只需要做x一次赋值,这稍微简单一些。但即使这样,也需要 5 行代码才能完成。让我们看看如何才能把它简化成一行。

x = y.even? ? 1 : 2
Enter fullscreen mode Exit fullscreen mode

?这被称为三元运算符。三元运算符的逻辑使用“(条件) ? (真值) : (假值)”语句来简化 if/else 结构。它首先将表达式求值为真或假,然后根据求值结果执行两个给定语句中的一个。语法如下:

test-expression ? if-true-expression : if-false-expression
Enter fullscreen mode Exit fullscreen mode

这在完善逻辑语句方面非常有用。

内联布尔值/条件语句

除了简洁的三元运算符之外,我们再来看看另一种将布尔逻辑简化为一行代码的方法。下面是一个包含布尔逻辑的基本方法。

def say_number_type(number)
  if number.even?
    puts "I am even!"
  end
end
Enter fullscreen mode Exit fullscreen mode

为了使代码更简洁,我们可以这样写这个布尔值:

def say_number_type(number)
  puts "I am even!" if number.even?
end
Enter fullscreen mode Exit fullscreen mode

同样的方法也适用于unless

def say_number_type(number)
  puts "I am even!" unless number.odd?
end
Enter fullscreen mode Exit fullscreen mode

注意:使用内联布尔值或条件语句有时被称为使用“修饰符语法”。

变量赋值

假设我们有一个场景,需要根据一个变量是否存在来赋予另一个变量不同的值。例如,我们有两个变量ab如果a变量存在(非空),我们希望将变量赋值xa。如果a变量为空,我们希望将x变量赋值为b。我们可以这样做。

if a.nil?
  x = b
else 
  x = a
end
Enter fullscreen mode Exit fullscreen mode

或者,根据我们上面学到的知识,我们可以这样做

x = a.nil? ? b : a
Enter fullscreen mode Exit fullscreen mode

但是,当处理可能出现的 nil 值时,我们可以使用管道运算符使代码更加简洁,如下所示:

x = a || b
Enter fullscreen mode Exit fullscreen mode

如果a`is` 不为 nil,则该赋值语句会将 `is` 的值赋给a`x` x。如果a`is` 为 nil,则它会将 `is` 的值赋给b`x`。请查看下面的控制台示例。

irb(main)> a = 1
=> 1
irb(main)> b = 2
=> 2
irb(main)> x = a || b
=> 1

irb(main)> a = nil
=> nil
irb(main)> x = a || b
=> 2
Enter fullscreen mode Exit fullscreen mode

我们可以使用管道运算符连接两个变量,也可以将其与等于运算符一起使用。假设我们想将变量赋值x为nil a,但前提x是 nil 为 nil。如果xnil 不为 nil,我们希望保留 nil 的值x。为了实现这一点,我们可以这样做:

x ||= a
Enter fullscreen mode Exit fullscreen mode

以下是一个控制台示例,详细展示了其工作原理:

irb(main)> x = 1
=> 1
irb(main)> a = 2
=> 2
irb(main)> x ||= a
=> 1
irb(main)> x = nil
=> nil
irb(main)> x ||= a
=> 2
Enter fullscreen mode Exit fullscreen mode

a这告诉 Ruby 仅当 ` to`为 `nil`x时才赋值x。当你期望有一个默认值但又不能保证它一定存在时,这非常有用。假设你有以下方法

def add_one(number)
  number + 1
end
Enter fullscreen mode Exit fullscreen mode

如果number值为 nil,那么当我们尝试向其中加 1 时就会抛出错误NoMethodError。我们可以通过类似这样的方式避免抛出错误:

def add_one(number)
  number ||= 0
  number + 1
end
Enter fullscreen mode Exit fullscreen mode

number ||= 0这样可以确保,如果number未定义,我们的方法不会引发错误,因为它会被设置为 0。如果你想更进一步,还可以将赋值运算符与参数一起放入函数中!🤯

def add_one(number = 0)
  number + 1
end
Enter fullscreen mode Exit fullscreen mode

调用此方法时,如果不传递数字,Ruby 会将数字设置为 0。

irb(main)> add_one
=> 1
irb(main)> add_one(1)
=> 2
Enter fullscreen mode Exit fullscreen mode

警戒条款

守卫子句是指在满足特定条件时提前退出方法。守卫子句可以帮助您简化方法逻辑并编写更优化的代码。让我们来看一个使用守卫子句的例子。假设您有以下方法。tester

def tester(arg)
  if arg == 1
    # logic
  else
   'error'
  end
end
Enter fullscreen mode Exit fullscreen mode

在这种情况下,如果我们的参数arg不等于 1,则返回字符串“error”。我们可以tester通过使用如下所示的 guard 子句来简化此方法。

def tester(arg)
 return 'error' unless arg == 1 # Guard Clause
 # logic
end
Enter fullscreen mode Exit fullscreen mode

方法开始时,我们立即检查 `a` 的值是否arg等于 1。如果等于,则立即返回。由于我们知道要做什么,因此无需在方法中处理其他所有事情。这在代码优化方面尤其有用。例如,请注意以下情况:

def tester(arg)
  user = User.find(123)
  event = Event.find(123)

  if arg == 1
    # logic
  else
    'error'
  end
end
Enter fullscreen mode Exit fullscreen mode

在这里,我们在执行 if/else 布尔语句之前就已经收集数据并执行逻辑了。最终,如果arg结果不等于 1,那么我们就浪费了大量时间和资源来收集不需要的数据。通过在方法的第一行检查条件,可以使用 guard 语句来确保不会浪费这些资源。

我还要补充一点,如果你想使用 guard 子句根据某个条件返回 nil,你可以这样做:

def tester(arg)
  return unless arg == 1
  # logic
end
Enter fullscreen mode Exit fullscreen mode

在上述例子中,return单独使用就相当于你写了return nil

def tester(arg)
  # nil is unnecessary 
  return nil unless arg == 1
  # logic
end
Enter fullscreen mode Exit fullscreen mode

不过,在 Ruby 中,这种情况下你不需要显式地定义 nil。第一个例子return unless arg == 1就是你想要的写法,也是你很可能会看到其他人这样写的。

调用方法

假设你正在处理一组响应某个方法的对象,并且你想使用类似 `getResult` 的方法来收集该方法的结果map。通常,你会这样做:

a = [1, 2, 3, 4]
string_list = a.map{|obj| obj.to_s}
# string_list = ["1", "2", "3", "4"]
Enter fullscreen mode Exit fullscreen mode

我们可以使用一些语法糖进一步简化它,并且仍然可以得到相同的结果。

string_list = a.map(&:to_s)
# string_list = ["1", "2", "3", "4"]
Enter fullscreen mode Exit fullscreen mode

任何我们这组对象(在本例中是整数)响应的方法都可以使用&:method相同的语法在每个对象上调用。如果你想了解 Ruby 的底层工作原理,我建议你阅读这篇博客文章。

安全导航员

你们当中有多少人以前写过类似的代码?✋ 我肯定写过!

def friends_name(user)
  return unless user.present?
  friend = user.friend
  if friend.present?
    friend.name
  end
end
Enter fullscreen mode Exit fullscreen mode

这里做了很多存在性检查,只是为了确保我们永远不会对 nil 对象调用方法,从而避免出现可怕的错误# NoMethodError。与其进行所有这些检查,我们可以使用 Ruby 的安全导航器 (Safety Navigator)!如果对 nil 对象调用方法,安全导航器会直接返回 nil。这意味着我们可以将方法简化为:

def friends_name(user)
  user&.friend&.name
end
Enter fullscreen mode Exit fullscreen mode

如果该链中的任何对象缺失,我们将直接返回 nil。以下是另一个简单的控制台示例,希望能进一步帮助您理解安全导航器的工作原理。

irb> [].last.to_s
=> ""
irb> [].last.even?
NoMethodError: undefined method `even?' for nil:NilClass
    from (irb):3
    from /usr/bin/irb:11:in `<main>'
irb> [].last&.even?
=> nil
Enter fullscreen mode Exit fullscreen mode

哈希和数组

括号 vs do/end

在我的哈希数组教程中,我大量使用了 do/end 代码块来清晰地展示代码逻辑。然而,方括号表示法是编写更简洁代码的绝佳方式。正如我们在教程中看到的,它非常适合将多个逻辑串联起来。

result = [1, 2, 3, 4].map{|n| n + 2}.select{|n| n.even?}.reject{|n| n == 6} 
# result = [4]
Enter fullscreen mode Exit fullscreen mode

现在你可能会想,面对这么多选择,我该如何知道该用哪个呢?!通常来说,一个好的经验法则是:

  • 如果代码块中只执行一行代码,则使用括号。
  • 如果你的逻辑超过一行,请使用 do/end 表示法。

单行括号示例:

a = [1, 2, 3]
a.map{|number| number + 10}
Enter fullscreen mode Exit fullscreen mode

多行 do/end 示例:

a = [1, 2, 3]
a.map do |number|
  if number.even?
    puts "I am even"
    number/2
  else
    puts "I am odd"
    number
  end
end
Enter fullscreen mode Exit fullscreen mode

请记住,不同的地方或不同的人可能有不同的风格。刚到一个新的地方时,一定要留意其他人的做法。你一开始很可能会想效仿他们。随着经验的积累,你会逐渐形成自己的风格和习惯。

连锁

我想快速回顾一下我上面用到的链式调用示例:

result = [1, 2, 3, 4].map{|n| n + 2}.select{|n| n.even?}.reject{|n| n == 6} 
# result = [4]
Enter fullscreen mode Exit fullscreen mode

这段代码非常简单明了,因此将每个步骤串联起来是使其更简洁的绝佳选择。但是,如果每个步骤的逻辑比较复杂,为了提高可读性,有时您可能需要将它们拆分开来。以下是一个为了提高可读性而拆分代码的示例。

plus_two_array = [1, 2, 3, 4].map{|n| n + 2}
even_numbers_array = plus_two_array.select{|n| n.even?}
remove_the_sixes_array = even_numbers_array.reject{|n| n == 6} 
Enter fullscreen mode Exit fullscreen mode

每个步骤及其结果都定义得非常清晰,这使得更复杂的逻辑更容易理解和遵循。我之所以要指出这一点,是因为有时编写最简洁的代码并非最佳选择。所有这些策略都很棒,但请注意,有时您可能需要为了代码的可读性而牺牲一些简洁性。

等等,还有更多!

显然,编写更简洁高效的代码的方法还有很多。这里只列举了一小部分,但我认为这是一个很好的起点。如果你发现了其他有助于改进代码的技巧,欢迎在评论区分享。一如既往,如果你有任何疑问或需要澄清的地方,请随时提出!

如果您想快速查阅这些方法,我制作了一个方便的速查表,其中只包含每个示例的代码。

文章来源:https://dev.to/molly/level-up-your-ruby-skillz-writing-compact-code-438k