& trong ruby
Mình mới làm quen với ruby và đôi khi gặp kiểu viết như thế này: 'con ga nay'.split.map(&:length).reduce(&:*) => 18 user&.name.to_s => "" mình thấy dấu & rất hữu dụng trong ruby và bắt đầu tìm hiểu về nó, dưới đây là một số cách dùng phổ biến của &. & cho ...
Mình mới làm quen với ruby và đôi khi gặp kiểu viết như thế này:
'con ga nay'.split.map(&:length).reduce(&:*) => 18
user&.name.to_s => ""
mình thấy dấu & rất hữu dụng trong ruby và bắt đầu tìm hiểu về nó, dưới đây là một số cách dùng phổ biến của &.
& cho phép and bit trong ruby, hãy xem ví dụ sau
irb(main):001:0> 14 & 13 => 12
tương đương với
irb(main):001:0> "#{14.to_s(2)} & #{13.to_s(2)} = #{12.to_s(2)}" => "1110 & 1101 = 1100"
& còn được dùng như phép giao nhau giữa các tập hợp
irb(main):001:0> [1,2,3] & [1,2,5,6] => [1, 2]
true && false => false
chắc các bạn đều biết && chính là logic của AND rồi, và cách viết && ngắn hơn và có vẻ cool hơn :v hãy thử:
irb(main):056:0> a = true and false => false irb(main):057:0> a => true
irb(main):058:0> a = true && false => false irb(main):059:0> a => false
bạn có nghĩ rằng && và AND giống nhau nữa không =)). Hãy xem ở đây bạn sẽ thấy rằng độ ưu tiên thực hiện của AND thấp hơn của &&. Như vậy trường hợp thứ 1 ở trên sẽ tương đương với cách viết sau:
irb(main):056:0> (a = true) and false => false irb(main):057:0> a => true
vậy tại sao vừa có && và AND tương tự như || và OR. Trong hầu hết trường hợp giá trị của chúng như nhau nhưng về ý nghĩa sử dụng AND và OR được coi là "control flow operators" (toán tử điều khiển luồng) còn && và || được coi là "logical operators" (toán tử logic). Một trong những trường hợp phổ biến dùng AND hơn && đó là control luồng xử lý data theo từng bước ví dụ
compile_code() AND assemble_binary() AND install_binary()
Bạn có 1 table user_courses (id, user_id, course_id) chứa các bản ghi mà trong đó user học course tương ứng. Trong một vài tình huống
if user_course && user_course.user && user_course.user.address ......... end
hoặc có thể viết
if user_course.try(:user).try(:address) ......... end
hoặc là dùng &.
if user_course&.user&.address ......... end
3 cách dùng này liệu có hoàn toàn như nhau hay không? hãy xem ví dụ dưới đây
irb(main):117:0* user_course = UserCourse.new(user: nil) => #<UserCourse id: nil, course_id: nil, user_id: nil> irb(main):118:0> user_course.user.name NoMethodError: undefined method `name' for nil:NilClass irb(main):119:0> user_course && user_course.user && user_course.user.name => nil irb(main):120:0> user_course.try(:user).try(:name) => nil irb(main):121:0> user_course&.user&.name => nil
bây giờ hãy thử với
irb(main):122:0> user_course = UserCourse.new(user: false) => #<UserCourse id: nil, course_id: nil, user_id: nil> irb(main):118:0> user_course.user.name NoMethodError: undefined method `name' for nil:NilClass irb(main):123:0> user_course && user_course.user && user_course.user.name => false irb(main):124:0> user_course.try(:user).try(:name) => nil irb(main):125:0> user_course&.user&.name NoMethodError: undefined method `name' for false:FalseClass
như các bạn đã thấy ở trường hợp 2 này &&, try và &. trả về 3 giá trị hoàn toàn khác nhau. ở trường hợp của &&
irb(main):138:0> user_course.user => false
khi thực hiện user_course && user_course.user sẽ tương đương với nil && false khi thực hiện tới đây thì phép toán sẽ trả về false luôn và skip qua phần tử cuối cùng. Sự việc sẽ khác nếu bạn đổi thứ tự
irb(main):139:0>user_course.user.name && user_course && user_course.user NoMethodError: undefined method `name' for false:FalseClass
trường hợp của try và &. như sau
irb(main):144:0> nil.try(:name) => nil irb(main):146:0> false&.name NoMethodError: undefined method `name' for false:FalseClass
như các bạn thấy save navigation operator (&.) có thể thực hiện với nil nhưng không thể thực hiện được với false, mặc dù nil và false đều là object. try() sẽ không quan tâm đến Object đó có tồn tại method được gọi hay không, &. sẽ tương đương với try!
user_course.try!(:user).try!(name) NameError: undefined local variable or method `name' for main:Object
Blocks là một trong những đặc trưng khá hay của ruby hãy xem
'con ga nay'.split.map {|s| s.length}.reduce {|product, n| product * n } => 18
và với cách viết ngắn gọn hơn
'con ga nay'.split.map(&:length).reduce(&:*)
vậy ký tự & rốt cuộc đã làm cái gì vậy?
- nếu object là Proc: & convert nó sang block
- nếu object không phải là Proc: & call function to_proc của object, sau đó convert nó sang block. API của to_proc viết bằng C bạn có thể tham khảo tại đây. ý nghĩa của nó trên ruby có thể diễn đạt như sau
class Symbol def to_proc ->(obj, args = nil) { obj.send(self, *args) } end end
hãy xem ví dụ dưới đây để hiểu về cách mà & đã convert symbol to block (names = 'con ga nay'.split)
names.map &:to_s // & sẽ gọi to_proc của chính object nên dòng trên sẽ thành names.map &:to_s.to_proc // thay thế to_proc bằng xử lý trong method names.map &->(name, args = nil) { name.send(:to_s, *args) } // map sẽ thực hiện method với từng phần tử names.map &->(name) { name.send(:to_s) } // thay vì object.send(:xxx) ta thay bằng cách gọi trực tiếp method của object names.map &->(name) { name.to_s } // lambada ->{name.to_s} map với mỗi phần tử của names, ký tự & convert lambada thành proc. names.map { |name| name.to_s }
http://ablogaboutcode.com/2012/01/04/the-ampersand-operator-in-ruby http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/ https://www.tinfoilsecurity.com/blog/ruby-demystified-and-vs http://maximomussini.com/posts/ruby-to_proc/