12/08/2018, 16:57

6 chức năng dễ gây nhầm lẫn của Ruby

Ruby là một ngôn ngữ ngắn gọn, tiện dụng, dễ dùng, dễ nghiện. Để có được sự tiện dụng này, những người thiết kế ngôn ngữ đã phải hy sinh một số chi tiết nhỏ, khiến cho đôi khi lập trình viên trở nên nhầm lẫn. Dưới đây là 6 tiện ích nhỏ của Ruby mà lập trình viên nên cẩn thận mỗi khi sử dụng. 1. ...

Ruby là một ngôn ngữ ngắn gọn, tiện dụng, dễ dùng, dễ nghiện. Để có được sự tiện dụng này, những người thiết kế ngôn ngữ đã phải hy sinh một số chi tiết nhỏ, khiến cho đôi khi lập trình viên trở nên nhầm lẫn.

Dưới đây là 6 tiện ích nhỏ của Ruby mà lập trình viên nên cẩn thận mỗi khi sử dụng.

1. Hàm []

Một trong những hàm tiện dụng nhất và được yêu thích nhất của Ruby.

Giống như trong các ngôn ngữ khác, hàm [] được dùng để truy cập đến phần tử của Array:

array = [1, 2, 3]
array[0] # => 1

hoặc một địa chỉ của Hash:

hash = {foo: "bar"}
hash[:foo] # => "bar"

Với một String, ta có thể truy cập đến kí tự bên trong:

"Hello World"[0] # => "H"

Điều rắc rối ở đây là, ta có thể sử dụng [] để gọi đến attribute của một instance:

book = Book.first
book["total_pages"] # => 100

hoặc sử dụng để gọi lamda:

hello = (-> (name) { "Hi, #{name}!" })
hello["John"] # => "Hi, John"

Chuyện gì sẽ sảy ra nếu ta liên kết các chức năng này lại với nhau?

wise_words_factory = (-> (number_of_elements) { (1..number_of_elements).map { WideWord.random } })

wise_words_factory[10][0][:category] # "Body builder"
wise_words_factory[10][0][:words] # "No pain, no gain"
wise_words_factory[10][0][:words][0] # "N"

Chỉ một dòng code trông có vẻ bình thường, nhưng đã dùng đến 3 kỹ thuật khác nhau chỉ với hàm [], và là cơn ác mộng cho việc debugging.

Bonus: Vì [] là một hàm nên ta hoàn toàn có thể sử dụng hàm try hoặc send để gián tiếp gọi đến:

array = [1, 2, 3, 4]
array.send :[], 0 # => 1
array.try :[], 1     # => 2

2. Toán tử %

Cũng giống như hàm [], toán tử % trong Ruby cũng được tận dụng tối đa:

Được sử dụng để tính module số nguyên như các ngôn ngữ khác:

103 % 100 # => 3

Ngoài ra, toán tử % còn được sử dụng để tạo format cho string, dẫn đến những nhầm lẫn "khó đỡ":

"%s: %d" % ["age", 18] # => age: 18

Để tránh nhầm lẫn, ta có thể sử dụng module Kernel.format cũng cho cũng kết quả.

Kernel.format(format, "age", 18) # => age: 18

3. Hàm Integer#zero?

Với những ai không biết về hàm zero?, thì đâu là một giải thích đơn giản:

assert_equal(1 == 0, 1.zero?) # => true

Nhìn qua thì hàm zero? trông rất đẹp và gọn, thậm chí còn được khuyến khích sử dụng bởi những gem check code convention như Rubocop.

Nhưng hàm này có thể gây ra nhiều rắc rối hơn là giải quyết, bởi vì về bản chất hai biểu thức trên không hoàn toàn bằng nhau. Sử dụng toán tử == 0 sẽ thực hiện so sánh bằng với một hằng số, trong khi hàm zero?, trong lý thuyết hướng đối tượng, sẽ gửi một lời gọi hàm đến đối tượng, và trả về kết quả nếu có method được định nghĩa.

arr = [1, 2, 3, 4]
arr == 0 # => false
arr.zero? # => NoMethodError

Vậy nên hãy sử dụng == 0, hoặc là chắc chắn bạn sử dụng zero? với kiểu dữ liệu số.

4. Biến toàn cục $[number]

Hãy cùng theo dõi hành động regex matching dưới đây:

# test.rb
string = "Hi, John!"
matched = %r(^(.+), (.+)!$).match(string)
matched[0] => "Hi, John!"
matched[1] => "Hi"
matched[2] => "John"

Không có gì đặc biệt. Nhưng Ruby còn hỗ trợ một cách khác để lấy dữ liệu như sau:

string = "Hi, John!"
%r(^(.+), (.+)!$).match(string)
$1 => "Hi"
$2 => "John"

Đến đây bạn có thể đập bàn và hét lên: "Thay đổi biến toàn cục cho mỗi thao tác regex matching có thể sinh ra cả đống bug tiềm tàng!". Không sao, team phát triển Ruby cũng đã nghĩ đến điều này. Theo như documentation, các biến toàn cục này là cục bộ theo các luồng và các hàm. Về cơ bản các biến toàn cục này không phải là các biến toàn cục             </div>
            
            <div class=

0