Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Rubys Bitwise Toolbox:연산자, 응용 프로그램 및 마술

평생을 Rails 앱을 구축하는 데 보낼 수 있으며 비트 연산자를 사용할 필요가 없습니다. 프로그래밍이 처음이라면 "비트 단위"가 무엇을 의미하는지조차 모를 수 있습니다.

그러나 효율성이 중요하고 리소스가 제한된 시스템에 관심을 갖는 순간 비트 연산이 중요해집니다. 네트워크 프로토콜, 암호화, Unix 파일 권한 및 임베디드 시스템과 같이 광범위하게 사용됩니다.

또한 컴퓨터가 두 숫자를 더하는 것과 같은 작업을 수행하는 방법을 실제로 이해하려면 비트 연산을 이해해야 합니다.

Ruby 및 기타 많은 언어가 기본 지원을 제공하므로 나중에 이러한 언어를 접하게 될 경우 무슨 일이 일어나는지 이해하기 위한 용도일지라도 도구 상자에 추가할 수 있는 훌륭한 도구입니다.

이 기사에서는 비트 연산이 무엇인지, 적용할 수 있는 몇 가지 예와 Ruby에서 비트 연산을 가장 잘 사용하는 방법을 다룰 것입니다. 시작하겠습니다.

비트 연산이란 무엇입니까?

모든 컴퓨터의 가장 낮은 수준에는 비트라고도 하는 1과 0만 있습니다. Ruby나 다른 프로그래밍 언어에서 사용하는 다른 모든 작업은 결국 1과 0으로 저장되지만 컴퓨터의 소프트웨어는 우리가 보는 것과 실제로 저장된 것 사이를 효과적으로 앞뒤로 변환합니다. 예를 들어 "hello"라는 문자열을 1과 0의 체인으로 저장합니다.

비트 연산을 사용하면 일반적으로 숫자를 나타내는 해당 비트에 직접 액세스하여 "무언가" 작업을 수행할 수 있습니다. 일부 작업은 보다 우아하고 효율적인 방식으로 소프트웨어의 기능을 구현하는 데 도움이 됩니다.

현재 비트 연산에 대해 강조해야 할 두 가지 중요한 측면이 있습니다. 비트를 직접 사용하기 때문에 정보를 저장하는 데 매우 효과적이며 실행이 매우 빠릅니다.

기본 동작은 비트 세트를 갖고 이에 연산자를 적용하는 것입니다. 두 개의 비트 세트와 함께 연산자를 사용할 수도 있습니다. 몇 가지 작업을 살펴보겠습니다.

비트 연산이 아님

이것은 단항 연산으로 비트 세트에만 적용되며 1을 0으로 또는 그 반대로 바꾸는 것만 큼 간단합니다. ~ 기호로 표시됩니다. .

~101000 = 010111

AND 비트 연산

AND는 두 비트 세트에 적용되는 연산입니다. 기호는 &입니다. 다음 논리를 따릅니다.

1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0

따라서 동일한 길이의 비트 세트가 두 개 있는 경우 결과는 이 논리를 각 개별 쌍에 적용하는 것입니다.

0110 AND
0111
-----
0110

루비:

25.to_s(2)           # 11001
30.to_s(2)           # 11110
(25 & 30).to_s(2)    # 11000

OR 비트 연산

AND 연산과 유사하게 두 비트 세트에도 적용되는 또 다른 연산은 비트 OR입니다. 기호는 |입니다. 다음 논리를 따릅니다.

1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0

따라서 양쪽에 1이 있으면 결과는 1이고 그렇지 않으면 0입니다. 매우 간단합니다! 더 많은 비트가 있는 OR 연산을 살펴보겠습니다.

0110 OR
0111
-----
0111

그리고 루비:

25.to_s(2)           # 11001
30.to_s(2)           # 11110
(25 | 30).to_s(2)    # 11111

실제 예 1:권한

이 시점에서 이러한 작업이 왜 유용한지 궁금할 수 있습니다. 결국, 그들은 꽤 낮은 수준으로 보입니다. 네트워크 프로토콜, 그래픽 또는 암호화로 직접 작업할 계획이 없을 수도 있습니다.

하지만 이전에 권한으로 작업한 적이 있을 것입니다. 권한은 비트 연산이 정말 유용할 수 있는 영역 중 하나입니다. 사용자가 문서에서 다양한 작업을 수행할 수 있는 권한 시스템이 있다고 상상해 보십시오.

  • 보기
  • 수정
  • 삭제
  • 다른 사용자 초대

이제 이러한 작업을 다양한 역할로 모델링하고자 합니다.

  • 어시스턴트: 문서를 보고 수정할 수 있습니다.
  • 관찰자: 문서를 볼 수만 있습니다.
  • 저자: 모든 작업을 수행할 수 있습니다.

우리는 이것을 어떻게 모델링 할 수 있습니까? 사용자에게 이러한 역할 중 하나가 있는지 어떻게 알 수 있습니까? 한 가지 대답은 비트 연산을 사용하는 것입니다.

우리는 각 사용자가 가지고 있는 권한을 나타내는 비트 세트를 하나만 저장합니다.

(Starting from the right side)
1st bit: View
2nd bit: Edit
3rd bit: Delete
4th bit: Invite other users

예:

0001 = Can only view the document.
0011 = Can view and edit the document.
1001 = Can view, can't edit, can't delete and can invite others.

일부 사용자에 대해 이러한 값을 설정하면 원하는 권한과 매우 빠르게 비교할 수 있습니다. 사용자가 문서를 편집할 수 있는지 확인한다고 가정해 보겠습니다. 비트 AND 연산을 사용할 수 있습니다.

# This is called a bit mask. It contains only the value we want to check, in this case the second bit for editing.
EDIT_PERMISSION_MASK = 0b0010

# We can define a quick method to check this: 
def can_edit_document?(user_permisions)
  (EDIT_PERMISSION_MASK & user_permisions) != 0
end

즉, 비트 AND 연산이 0이 아닌 다른 값을 제공하면 해당 비트가 설정됩니다.

0010 AND
1101
----
0000 == 0 so we don't have the permission

0010 AND
1110
----
0010 != 0 so we have the permission

확인하려는 비트의 위치를 ​​수정하여 다른 권한에 동일한 논리를 적용할 수 있으므로 이러한 상수와 해당 메서드로 끝납니다.

VIEW_PERMISSION_MASK   = 0b0001
EDIT_PERMISSION_MASK   = 0b0010
DELETE_PERMISSION_MASK = 0b0100
INVITE_PERMISSION_MASK = 0b1000

또한 빠른 비트 확인을 통해 권한을 동적으로 정의하고 향후 새 권한을 저장할 수 있습니다.

예를 들어, 어시스턴트는 문서를 보고 편집만 할 수 있으므로 해당 사용자에 대한 권한은 0011이 됩니다. . 그 값을 데이터베이스에 저장하면 어시스턴트가 앞에서 정의한 방법으로 작업을 수행할 수 있는지 쉽게 확인할 수 있습니다.

ASSISTANT_MASK = VIEW_PERMISSION_MASK | EDIT_PERMISSION_MASK
# This will be: 0011

# Optionally, we could use this method to check if this user is an assistant. This method could be defined inside the User class.
def is_assistant?(user)
  (user.permissions == ASSISTANT_MASK)
end

이 모든 것이 친숙하게 들린다면 이는 Unix 기반 시스템에서 파일 권한에 일반적으로 사용되는 것과 동일한 접근 방식이기 때문입니다.

실제 예 2:팀의 위치

비트 연산을 조금 더 사용합시다 😉.

비교적 일반적인 다른 경우에 적용할 수 있습니다. 스포츠 팀의 다른 직위나 회사의 직위입니다. 단순화하기 위해 농구 팀과 함께 가자.

농구에서 게임을 할 때 5가지 포지션이 있습니다:- 포인트 가드- 슈팅 가드- 스몰 포워드- 파워 포워드- 센터

그리고 각 위치에 비트 세트를 할당할 수 있습니다. 00001 Point guard 00010 Shooting guard 00100 Small forward 01000 Power forward 10000 Center

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

이제 전체 팀이 다음과 같이 있는지 확인하는 것과 같은 몇 가지 흥미로운 작업을 수행할 수 있습니다.

# p1...p5 are the positions of each player
is_full_team_present = (p1 | p2 | p3 | p4 | p5 == 31)

왜요? 비트 OR 연산을 수행하여 모든 위치가 있는 경우 11111이 됩니다.

# OR bitwise operation
00001 |
00010 |
00100 |
01000 |
10000
-----
11111

2^0 + 2^1 + 2^2 + 2^3 + 2^4 =31이기 때문에 11111은 31입니다.

이것은 비트 연산과 엄격하게 관련이 없지만 이 데이터 모델링을 통해 두 플레이어를 교환할 수 있는지 확인할 수도 있습니다. 이것은 매우 간단합니다:

def can_be_exchanged?(player1, player2)
  player1.position == player2.position
end

XOR

두 개의 비트 세트로 수행할 수 있는 또 다른 작업은 기호가 ^인 XOR입니다. .

XOR은 배타적 논리합을 의미하며 다음 논리를 따릅니다.

1 | 1 = 0
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0

따라서 결과는 두 비트 중 하나가 1인 경우에만 1이 됩니다. 둘 다 같으면 0이 됩니다.

이 연산은 x ^ x =0이므로 일부 알고리즘에서 숫자를 자신과 비교하는 데 사용됩니다.

교대

이것은 세트 내에서 비트를 한쪽 또는 다른 쪽으로 "이동"하는 흥미로운 작업 그룹입니다. 설명하겠습니다.

비트 시프트를 사용하여 비트를 왼쪽 또는 오른쪽으로 한 방향으로 이동하거나 "이동"합니다.

00010111 left-shift
<-------
00101110
10010111 right-shift
------->
11001011

비트를 n번 이동하거나 이동할 수 있습니다. 다음은 Ruby에서 숫자 5에 두 번 적용된 왼쪽 시프트입니다.

5.to_s(2) # 101
(5 << 2).to_s(2) # 10100

보시다시피 왼쪽 시프트는 <<로 표시됩니다. 오른쪽 시프트는>>:

를 사용합니다.
5.to_s(2) # 101
(5 >> 2).to_s(2) # 1

이 경우 1*01의 비트 "0"과 "1" 때문에 하나만 얻습니다. * 삭제되었습니다.

오른쪽으로 2로 나누기

비트 시프트에 대한 한 가지 흥미로운 사실은 비트 시프트를 사용하여 일부 수학 연산을 계산할 수 있다는 것입니다. 과거에는 이것이 더 빨랐지만 오늘날에는 게임 개발과 같이 리소스에 더 많은 제약이 있는 작업을 하는 프로그래머가 거의 독점적으로 사용합니다.

숫자에 오른쪽 시프트를 적용하면 2로 나눈 결과를 얻습니다.

10.to_s(2)        # 1010
(10 >> 1).to_s(2) # 101
10 >> 1           # 5

왼쪽 시프트로 2 곱하기

같은 방식으로 왼쪽 시프트로 2를 곱할 수 있습니다.

10.to_s(2)        # 1010
(10 << 1).to_s(2) # 10100
10 << 1           # 20

숫자가 홀수인지 짝수인지 빠르게 확인

정말 빠르고 쉽게 따라할 수 있는 비트 연산의 아주 간단한 예가 있습니다.

1을 사용하여 AND 연산을 한다고 상상해 보십시오. 따라서 우리가 사용하는 컴퓨터에 따라 0과 1이 될 수 있습니다. 2를 사용하여 수행해 보겠습니다.

2 = 00000010 &
    00000001
-------------
    00000000

이제 4:

4 = 00000100 &
    00000001
-------------
    00000000

5는 어떻습니까?

5 = 00000101 &
    00000001
-------------
    00000001

이제 우리는 1을 얻었습니다. 이것이 무엇을 의미하는지 짐작할 수 있습니까?

이 AND를 1로 하면 숫자가 짝수이면 0이 되고 홀수이면 1이 됩니다. 이 사실을 사용하여 Ruby에서 몇 가지 메서드를 쉽게 만들 수 있습니다.

def is_odd?(number)
  number & 1
end
def is_even?(number)
  is_odd?(number) == 0
end
# Or:
def is_even?(number)
  (number & 1) == 0
end

더 나아가고 싶거나 비트로 놀라운 순간을 경험하고 싶다면 이 비트 핵 컬렉션에서 더 많은 트릭을 확인하세요.

결론

Bitwise 작업은 한 번도 본 적이 없다면 따라하기 어렵지만 일단 익숙해지면 이를 사용하는 기존 또는 미래 프로젝트를 처리할 준비가 더 잘 될 것입니다. 또한 코드의 문제에 대한 솔루션을 계획할 때 새로운 도구를 갖게 됩니다.