Bạn làm việc với integer nhưng muốn dùng phương thức string (như gsub).
Làm sao?
Convert thành string (với to_s) rồi convert ngược integer (với to_i).
Ví dụ:
Bạn convert Integer 1 thành String "1".
Sau đó dùng phương thức class mới, giúp làm việc không thể trước.
Nhớ trong Ruby…
Mọi object liên kết với class, và mỗi class có tập phương thức riêng.
Trong bài này, bạn học về:
- Conversion method nào có sẵn trong Ruby
- Khác biệt giữa chúng!
- Cách chọn đúng cái trong tình huống khác nhau
Bắt đầu!
Short Conversion Methods (to_s, to_i)
Bạn có thể quen nhóm conversion method đầu tiên.
Phương thức như:
- to_i
- to_s
- to_a
Các phương thức này trả về object mới của class cụ thể đại diện cho object hiện tại.
Ví dụ:
(1..10).to_a # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Điều này nói:
"Tôi muốn convert
Range1..10 thànhArrayđại diện range đó."
Có cách Ruby gọi conversion method này ngầm cho bạn.
Như string interpolation:
"#{1}"
Này gọi 1.to_s cho bạn, dù bạn không thấy.
Bạn tự check với code này:
module Log
def to_s
puts "to_s called"
super
end
end
class Integer
prepend Log
end
puts "#{1}" # "to_s called"
Lưu ý: Dùng
FixnumthayIntegernếu Ruby 2.3 hoặc cũ. Hoặc tốt hơn, update Ruby 🙂
Các phương thức này khá permissive và không raise exception.
Ví dụ:
"aaaaaa".to_i # 0
Con số 0 này có thể bất ngờ.
Nhưng đó là kết quả khi gọi to_i trên string không có integer.
Sau bạn thấy phiên bản strict hơn của phương thức này.
Long Conversion Methods (to_str, to_int)
Bây giờ:
Nếu đã có short conversion method, tại sao cần to_str, to_int?
Khác biệt là gì?
Khác biệt nằm ở intent (ý định).
Mọi Ruby class (trừ BasicObject) implement to_s trả về string representation.
Nhưng chỉ vì class trả string không nghĩa nó behave như string!
Xem ví dụ:
"" + 1 # TypeError: no implicit conversion of Fixnum into String
Suy nghĩ:
String rỗng cộng integer nên trả gì?
Tôi không biết.
Bạn nói "1" (string có số 1).
Tại sao?
Đó không phải kỳ vọng đa số trong case này.
Không hợp lý cộng chúng… nên TypeError.
Và ngay cả nếu cho phép.
Cái này thì sao:
"" + {}
String rỗng cộng hash rỗng.
Cũng không hợp lý!
Vậy Ruby thay vì check object khác có phải string (không tốt cho Polymorphism), nó check xem nó CÓ HIỂU NHƯ string không.
Đó là lúc to_str xuất hiện.
Chỉ các class implement to_str trong Ruby 2.5:
StringNameError::messageWarning::buffer
Cách implement Conversion Method riêng
Bạn implement to_str trên bất kỳ class.
Sau đó dùng nó như string.
Ví dụ:
class Cat
def to_str
"meow"
end
end
"" + Cat.new # "meow"
Nhưng trừ khi class bạn equivalent với string thì không nên làm này.
Dùng to_s thay thế.
Btw, này không chỉ cho phương thức +.
Cũng dùng ở chỗ khác chỉ string-like object mới hợp lý.
Như:
[1,2,3].join(",")
Các long conversion method khác như to_int và to_hash theo logic tương tự.
Đó là lý do chúng ta có các phương thức khác nhau.
Cách dùng Conversion Wrappers
Nếu chưa đủ conversion method… đừng lo vì còn vài cái nữa!
Tôi gọi chúng "Conversion Wrappers".
Chúng là:
- Array()
- Integer()
- Hash[]
Đó là phương thức trông lạ!
Chú ý ngoặc đơn và ngoặc vuông…
…là điều duy nhất tách biệt phương thức này với tên class.
Mục đích?
Array() convert BẤT KỲ THỨ GÌ thành array.
Ví dụ:
Array(nil) # []
Array([]) # []
Array(1) # [1]
Array("") # [""]
Logic gì đây???
À, phương thức này tuân theo quy tắc cụ thể:
- Nếu object respond_to
to_aryhoặcto_a, nó gọi đó và trả value - Ngược lại, nó đưa object vào array rỗng và trả đó
Điều đó giải thích behavior trên.
Hữu ích nếu viết method expect array nhưng nhận bất cứ thứ gì và crash app. Bạn đảm bảo nó array bằng cách pass vào Array() trước.
Bây giờ Integer().
Có quy tắc đặc biệt:
- Nếu object là string và nội dung strict conform valid numeric representation Ruby, method trả Integer. Raise
ArgumentErrornếu format invalid. - Nếu object không phải string, thử gọi
to_int, rồito_i. - Raise
TypeErrornếu object không convert được thànhIntegerhợp lệ dùng conversion method.
Ví dụ:
Integer(1) # 1
Integer("25") # 25
Integer("abc123") # ArgumentError (invalid value for Integer(): "abc123")
Integer([]) # TypeError (can't convert Array into Integer)
Phương thức Integer() hữu ích nếu muốn 100% chắc chắn làm việc với Integer hợp lệ.
Bây giờ Hash[].
Bạn truyền mảng số phần tử chẵn để có hash mới:
Hash[[["a", 1], ["b", 2], ["c", 3]]] # {"a"=>1, "b"=>2, "c"=>3}
Hữu ích nếu build hash bằng merge element hai mảng, hoặc dùng method như map.
Tóm tắt
Bạn đã học các conversion method khác nhau trong Ruby, tại sao chúng tồn tại và cách dùng!
Hy vọng hữu ích 🙂
Nếu thích bài này bạn cũng sẽ yêu thích sách Ruby Deep Dive, check it out.