Bạn có thể trọn đời xây Rails apps mà không bao giờ cần bitwise operators. Nếu mới lập trình, có thể không biết "bitwise" nghĩa là gì.
Nhưng khi bạn quan tâm đến systems nơi efficiency quan trọng và resources limited, bitwise operations trở nên crucial. Chúng dùng rộng rãi trong network protocols, cryptography, Unix file permissions và embedded systems.
Ngoài ra, để thực sự hiểu computer làm gì như cộng hai số, bạn phải hiểu bitwise operations.
Vì Ruby và nhiều languages khác có native support, chúng là tool tuyệt vời thêm vào toolbox, dù chỉ để hiểu khi gặp chúng trong tương lai.
Bài này cover: bitwise operations là gì, examples ứng dụng, cách dùng tốt nhất trong Ruby. Bắt đầu.
Bitwise Operations Là Gì?
Ở lowest level trong computer, chỉ có 1s và 0s, gọi là bits. Mọi operation khác trong Ruby cuối cùng lưu thành 1s và 0s, nhưng software transform chúng back-and-forth giữa thứ ta thấy và thứ thực sự lưu. Ví dụ, string "hello" lưu thành chuỗi 1s và 0s.
Bitwise operations cho phép access trực tiếp bits đó, thường represent numbers, và làm "gì đó" với chúng. Một số operations giúp implement features elegant và efficient hơn.
Hai aspects quan trọng: chúng cực kỳ effective lưu information (dùng bits trực tiếp), và rất fast execution.
Basic behavior: có set bits, apply operator. Cũng dùng operators với hai sets bits. Xem operations:
NOT Bitwise Operation
Unary operation, chỉ apply cho một set bits, đơn giản: replace 1s thành 0s và ngược lại. Symbol: ~.
~101000 = 010111
AND Bitwise Operation
Áp dụng cho hai sets bits; symbol &; logic:
1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0
Với hai equal-length sets bits, result là apply logic cho từng pair:
0110 AND 0111
-----
0110
Trong Ruby:
25.to_s(2) # 11001
30.to_s(2) # 11110
(25 & 30).to_s(2) # 11000
OR Bitwise Operation
Tương tự AND, operation khác áp dụng cho hai sets bits là bitwise OR. Symbol |, logic:
1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0
Nếu có 1 ở bất kỳ bên nào, result 1, ngược lại 0. Xem OR với nhiều bits:
0110 OR 0111
-----
0111
Trong Ruby:
25.to_s(2) # 11001
30.to_s(2) # 11110
(25 | 30).to_s(2) # 11111
Practical Example 1: Permissions
Có thể tự hỏi tại sao operations này useful. Chúng có vẻ low-level. Có thể bạn không plan làm network protocols, graphics hay cryptography.
Nhưng bạn chắc làm permissions trước. Permissions là area bitwise operations rất useful. Hãy tưởng tượng permissions system users perform actions trên document:
- View
- Edit
- Delete
- Invite other users
Model actions thành roles:
- Assistant: view và edit.
- Observer: chỉ view.
- Author: tất cả actions.
Model như thế nào? Làm sao biết user có role? Cách dùng bitwise operations.
Lưu một set bits cho mỗi user represent permissions:
(Bắt đầu từ bên phải)
1st bit: View
2nd bit: Edit
3rd bit: Delete
4th bit: Invite other users
Ví dụ:
0001 = Chỉ view document.
0011 = View và edit.
1001 = View, không edit, không delete, invite others.
Khi đã set values cho users, fast comparisons với permission muốn check. Check user có edit được không, dùng bitwise AND:
# Bit mask. Chỉ chứa value muốn check, bit 2 cho editing.
EDIT_PERMISSION_MASK = 0b0010
# Method check nhanh:
def can_edit_document?(user_permissions)
(EDIT_PERMISSION_MASK & user_permissions) != 0
end
Nếu bitwise AND cho khác 0, bit đó set:
0010 AND 1101
----
0000 == 0 => không có permission
0010 AND 1110
----
0010 != 0 => có permission
Apply logic tương tự cho permission khác bằng cách đổi vị trí bit check, constants và methods:
VIEW_PERMISSION_MASK = 0b0001
EDIT_PERMISSION_MASK = 0b0010
DELETE_PERMISSION_MASK = 0b0100
INVITE_PERMISSION_MASK = 0b1000
Thêm, dynamically define permissions và store new permissions với quick bit check.
Ví dụ Assistant chỉ view và edit, permissions = 0011. Store trong DB, check assistant có perform action không với methods trên.
ASSISTANT_MASK = VIEW_PERMISSION_MASK | EDIT_PERMISSION_MASK # = 0011
# Optionally, method check user là assistant:
def is_assistant?(user)
(user.permissions == ASSISTANT_MASK)
end
Nếu familiar, vì đây là approach dùng cho file permissions trong Unix-based systems.
Practical Example 2: Positions In A Team
Dùng bitwise operations thêm 😉. Case common: positions trong sports team hoặc job positions. Chọn basketball team.
Basketball có 5 positions: Point guard, Shooting guard, Small forward, Power forward, Center.
Assign bits cho từng position:
00001 Point guard
00010 Shooting guard
00100 Small forward
01000 Power forward
10000 Center
Trong Ruby:
POINT_GUARD_POSITION = 0b00001
SHOOTING_GUARD_POSITION = 0b00010
SMALL_FORWARD_POSITION = 0b00100
POWER_FORWARD_POSITION = 0b01000
CENTER_POSITION = 0b10000
POINT_GUARD_POSITION | SHOOTING_GUARD_POSITION | SMALL_FORWARD_POSITION | POWER_FORWARD_POSITION | CENTER_POSITION
# = 31
Check whole team present:
# p1...p5 là positions của từng player
is_full_team_present = (p1 | p2 | p3 | p4 | p5 == 31)
Tại sao? Bitwise OR operation, nếu có tất cả positions, kết quả: 11111.
# OR bitwise operation
00001 | 00010 | 00100 | 01000 | 10000
-----
11111
11111 = 31, vì 2^0 + 2^1 + 2^2 + 2^3 + 2^4 = 31.
Không strictly related bitwise, nhưng với data modeling này cũng check nếu two players có thể exchange. Rất đơn giản:
def can_be_exchanged?(player1, player2)
player1.position == player2.position
end
XOR
Operation khác với hai sets bits là XOR, symbol ^.
XOR = exclusive OR, logic:
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0
Result = 1 chỉ khi một trong hai bits = 1; nếu cả hai bằng nhau thì 0.
Operation này dùng trong algorithms compare number với chính nó, vì x ^ x = 0.
Shifts
Group operations "move" bits left/right trong set. Giải thích.
Bit shift: move bits one direction, left hoặc right:
00010111 left-shift <------- 00101110
10010111 right-shift -------> 11001011
Move/shift bits n times. Left shift applied to number 5 twice trong Ruby:
5.to_s(2) # 101
(5 << 2).to_s(2) # 10100
Left shift = <<. Right shift = >>:
5.to_s(2) # 101
(5 >> 2).to_s(2) # 1
Chỉ còn 1, vì bits "0" và "1" từ 101 bị discarded.
Chia 2 Với Right Shift
Thú vị: right shift cho kết quả chia 2:
10.to_s(2) # 1010
(10 >> 1).to_s(2) # 101
10 >> 1 # 5
Nhân 2 Với Left Shift
Tương tự, left shift nhân 2:
10.to_s(2) # 1010
(10 << 1).to_s(2) # 10100
10 << 1 # 20
Nhanh Check Odd/Even
Ví dụ đơn giản bitwise operation real fast và easy.
AND operation với 1, chỉ 1. Số 0s tùy computer, và 1. Với 2:
2 = 00000010
& 00000001
-------------
00000000
Với 4:
4 = 00000100
& 00000001
-------------
00000000
Với 5?
5 = 00000101
& 00000001
-------------
00000001
Ra 1. Đoán được ý nghĩa?
AND với 1, nếu even → 0, odd → 1. Dùng fact này tạo methods Ruby:
def is_odd?(number)
number & 1
end
def is_even?(number)
is_odd?(number) == 0
end
# Hoặc:
def is_even?(number)
(number & 1) == 0
end
Muốn đi sâu hơn, trải nghiệm mind-blowing moments với bits, check bitwise hacks collection.
Kết Luận
Bitwise operations khó follow nếu chưa gặp, nhưng khi familiar bạn better prepared deal với existing/future projects dùng chúng. Plus, tool mới khi plan solution cho problem trong code.