Smalltalk80LanguageImplementationKor:Chapter 08
- 제 8 장 숫자 클래스
숫자 클래스(Numerical Classes)
Object
Magnitude
Character
Date
Time
Number***
Float***
Fraction***
Integer***
LargeNegativeInteger***
LargePositiveInteger***
SmallInteger***
LookupKey
Association
Link
Process
Collection
SequenceableCollection
LinkedList
Semaphore
ArrayedCollection
Array
Bitmap
DisplayBitmap
RunArray
String
Symbol
Text
ByteArray
Interval
OrderedCollection
SortedCollection
Bag
MappedCollection
Set
Dictionary
IdentifyDictionary
Stream
PositionableStream
ReadStream
WriteStream
ReadWriteStream
ExternalStream
FileStream
Random***
File
FileDirectory
FilePage
UndefinedObject
Boolean
False
True
ProcessorScheduler
Delay
SharedQueue
Behavior
ClassDescription
Class
MetaClass
Point
Rectangle
BitBit
CharacterScanner
Pen
DisplayObject
DisplayMedium
Form
Cursor
DisplayScreen
InfiniteForm
OpaqueForm
Path
Arc
Circle
Curve
Line
LinearFit
Spline
Smalltalk 프로그래밍 시스템의 주요 목표 중 하나는 정보 처리에 대해 가능한 한 획일화된 단일 메타포(metaphore)를 적용하는 데에 있다. Smalltalk 메타포는 앞에서 설명한 바와 같이 메시지를 전송함으로써 통신하는 객체 중 하나다. 이러한 메타포는 Simula에서 시뮬레이션 시스템을 구현하기 위해 사용된다. Smalltalk 메타포를 프로그래밍 시스템의 모든 측면으로 적용 시 가장 어려운 점은 산술 영역이었다. Simula는 그것이 구현한 시뮬레이션에서 고수준의 상호작용에만 객체/메시지 메타포를 사용하였다. 대부분의 알고리즘적 제어 구조를 비롯해 산술의 경우 Simula는 내장된 숫자 표현, 연산, 구문을 가진 임베디드 Algol 프로그래밍 언어에 의존한다. 두 정수를 합하는 것조차 메시지 전송으로 해석되어야 한다는 주장은 초창기 Smalltalk 시절에 약간의 저항을 불러 일으키기도 했다. 경험에 따르면 프로그래밍 언어에서 이러한 극적인 획일화로 인한 혜택은 구현에서 감수해야 하는 불편함보다 큰 것으로 나타났다. Smalltalk의 여러 버전에 걸쳐 구현 기법은 가장 공통된 산술 연산에 대한 메시지 전송 오버헤드를 감소시키도록 발전되었기 때문에 획일성을 추구함으로써 포기해야 하는 기능은 거의 없다.
수치 값을 나타내는 객체는 Smalltalk에서 이루어진 대부분의 시스템에서 사용된다 (여느 다른 프로그래밍 언어와 마찬가지로). 숫자는 본래 수학적 계산을 실행하는 데에 사용되며, 알고리즘에서 색인, 카운터, 상태나 조건의 (때로는 플래그라 불림) 인코딩으로서 사용되기도 한다. 진정수 또한 서로 boolean 마스킹 연산을 실행하는 2진 숫자(비트)의 컬렉션으로 사용된다.
각 수치 값 유형은 하나의 클래스에 의해 표현된다. 숫자 클래스는 모든 숫자가 마치 가장 일반적인 타입인 것처럼 행위하도록 구현된다. 특정 숫자 객체의 실제 클래스는 그 값을 표현하기 위해 전체적인 일반성(full generality)를 어느 정도 필요로 하는지에 따라 결정된다. 따라서 모든 숫자 객체의 외부 프로토콜은 Number 클래스로부터 상속된다. Number에는 3개의 서브클래스, Float, Fraction, Integer가 있다. Integer에는 3개의 서브클래스, SmallInteger, LargeNegativeInteger, LargeNegativeInteger가 있다. 진정수는 숫자를 비트의 시퀀스로 취급하는 기능을 지원하기 위해 추가로 프로토콜을 제공한다. 이 프로토콜은 Integer 클래스에 명시되어 있다. 시스템 내 숫자는 Float, Fraction, SmallInteger, LargePositiveInteger, LargeNegativeInteger 중 하나의 인스턴스에 해당한다. Number와 Integer 클래스는 공유 프로토콜을 명시하지만 수치 값에 대한 특정 표현은 명시하지 않는다. 따라서 Number와 Integer의 인스턴스는 전혀 생성되지 않는다.
내부 상태를 변경할 수 있는 다른 객체와 달리 숫자는 그 값을 유일한 상태로 가지며, 그 값은 절대 변경되어선 안 된다. 가령 객체 3은 그 상태를 절대로 4로 변경해선 안 되며, 이를 어기면 끔찍한 일이 발생할 것이다.
숫자 클래스의 프로토콜
숫자는 모든 숫자 객체의 프로토콜을 정의한다. 그 메시지는 표준 산술 연산과 비교 연산을 지원한다. 이들 중 대부분은 값의 실제 표현에 의존하기 때문에 Number의 서브클래스에 의해 정의된다.
산술 메시지의 프로토콜은 +, -, *, /와 같은 일반적인 이항 연산기호와 몇 가지 단항 및 키워드 메시지로 구성되어 숫자의 절대값, 숫자의 부정(negation), 숫자의 정수 몫 또는 나머지를 계산한다. 산술 메시지의 범주는 다음과 같다.
산술 | |
+ aNumber | 수신자와 aNumber 인자의 합을 응답한다. |
- aNumber | 수신자와 aNumber 인자의 차이값을 응답한다. |
* aNumber | 수신자와 aNumber 인자를 곱한 결과를 응답한다. |
/ aNumber | 수신자를 aNumber 인자로 나눈 결과를 응답한다. 정밀도는 가능한 높게 유지되므로 나눗셈이 정확하지 않으면 결과가 Fraction의 인스턴스가 될 것임을 명심한다. |
// aNumber | 음의 무한대로 절단(truncation)한 나눗셈에 의해 정의된 정수로 된 몫을 응답한다. |
\\ aNumber | 음의 무한대로 절단(truncation)한 나눗셈에 의해 정의된 정수로 된 나머지를 응답한다. 이것은 모듈로(modulo) 연산이다. |
abs | 수신자의 절대값 (양수) Number를 응답한다. |
negated | 수신자 값의 부정 Number를 응답한다. |
quo: aNumber | 0으로 절단한 나눗셈에 의해 정의된 정수 몫을 응답한다. |
rem: aNumber | 0으로 절단한 나눗셈에 의해 정의된 정수 나머지를 응답한다. |
reciprocal | 1을 수신자로 나눈 값을 응답한다. 수신자가 0일 경우 사용자에게 오류를 보고한다. |
Number 인스턴스 프로토콜 |
몇 가지 예를 들자면 아래와 같다.
표현식 | 결과 |
1 +10 | 11 |
5.6 -3 | 2.6 |
5 - 2.6 | 2.4 |
(-4) abs | 4 |
6 / 2 | 3 |
7 /2 | (7/2), 분자가 7이고 분모가 2인 Fraction |
7 reciprocal | (1/7), 분자가 1이고 분모가 7인 Fraction |
나눗셈 연산에서 정수로 된 몫과 나머지를 리턴하는 산술 메시지는 두 개의 규칙을 따른다. 하나는 결과가 되는 숫자를 0으로 절단하고, 나머지 하나는 음의 무한대로 절단한다. 0과 음의 무한대는 같은 방향에 있으므로 양의 결과에도 마찬가지다. 음의 결과인 경우 두 가지 규칙은 동일한 방향으로 올림/내림(round)된다. Number에 대한 프로토콜은 두 가지 규칙을 제공한다.
아래는 선택자들 간 관계를 보여주는 표다.
결과 | 음의 무한대로 절단 | 0 방향으로 절단 |
quotient | // | quo: |
remainder | \\ | rem: |
예를 몇 가지 소개하겠다:
표현식 | 결과 |
6 quo: 2 | 3 |
7 quo: 2 | 3 |
(7 quo: 2) + 1 | 4 |
7 quo: 2 + 1 | 2 |
7 rem: 2 | 1 |
7 // 2 | 3 |
7 \\ 2 | 1 |
7 \\ 2 + 1 | 2 |
수신자와 인자의 부호가 동일할 경우 quo:, rem: 또는 // 의 결과는 항상 부호가 양인 값을 리턴하고, 부호가 다르면 항상 음의 값을 리턴한다. \\는 항상 양의 결과를 생성한다.
추가적 수학 함수는 다음과 같다.
산술 함수 | |
exp | 수신자의 지수에 해당하는 부동소수점 수를 응답한다. |
In | 수신자의 자연 대수를 응답한다. |
log: aNumber | 수신자의 로그 밑을 응답한다. |
floorLog: radix | 수신자의 로그 밑 기수의 floor를 응답하되, floor는 음의 무한대 방향으로 수신자와 가장 가까운 정수를 나타낸다. |
raisedTo: aNumber | 수신자를 인자 aNumber만큼 거듭제곱한 값을 응답한다. |
raisedToInteger: anInteger | 수신자를 인자 aNumber만큼 거듭제곱한 값을 응답하되, 인자는 Integer 유형이어야 한다. |
sqrt | 수신자의 양의 제곱근인 부동소수점 수를 응답한다. |
squared | 수신자의 세제곱값을 응답한다. |
Number 인스턴스 프로토콜 |
몇 가지 예를 들자면 다음과 같다.
표현식 | 결과 |
2.718284 In | 1.0 |
6 exp | 403.429 |
2 exp | 7.38906 |
7.38906 In | 1.99998 (that is, 2) |
2 log: 2 | 1.0 |
2 floorLog: 2 | 1 |
6 log: 2 | 2.58496 |
6 floorLog: 2 | 2 |
6 raisedTo: 1.2 | 8.58579 |
6 raisedToInteger: 2 | 36 |
64 sqrt | 8.0 |
8 squared | 64 |
숫자가 짝수인지 홀수인지, 아니면 음수인지 양수인지 처리하는 숫자의 프로퍼티는 아래의 메시지로 검사할 수 있다.
검사하기 | |
even | 수신자가 짝수인지 응답한다. |
odd | 수신자가 홀수인지 응답한다. |
negative | 수신자가 0보다 작은 값인지 응답한다. |
positive | 수신자가 0보다 크거나 같은 값인지 응답한다. |
strictlyPositive | 수신자가 0보다 큰 값인지 응답한다. |
sign | 수신자가 0보다 크면 1을, 0보다 작으면 -1을, 그 외의 경우 0을 응답한다. |
Number 인스턴스 프로토콜 |
절단과 내림을 처리하는 숫자의 프로퍼티는 아래의 프로토콜에서 제공된다.
절단과 내림(truncation and round off) | |
ceiling | 양의 무한대 방향으로 수신자와 가장 가까운 정수를 응답한다. |
floor | 음의 무한대 방향으로 수신자와 가장 가까운 정수를 응답한다. |
truncated | 0의 방향으로 수신자와 가장 가까운 정수를 응답한다. |
truncateTo: aNumber | 0의 방향으로 수신자와 가장 가까운 aNumber 인자의 다음 배수를 응답한다. |
rounded | 수신자에 가장 가까운 정수를 응답한다. |
roundTo: aNumber | 수신자에 가장 가까운 aNumber 인자의 3배 값을 응답한다. |
Number 인스턴스 프로토콜 |
Number를 Integer로 변환해야 할 때마다 절단된 메시지를 사용할 수 있으며, 예를 몇 가지 들어보겠다.
표현식 | 결과 |
16.32 ceiling | 17 |
16.32 floor | 16 |
16.32 truncated | 16 |
16.32 truncateTo:5 | 15 |
16.32 truncateTo:5.1 | 15.3 |
16.32 rounded | 16 |
16.32 roundTo: 6 | 18 |
16.32 roundTo:6.3 | 18.9 |
Number 클래스에 제공되는 프로토콜은 숫자를 다른 유형의 객체나 다른 표현 단위로 변환하기 위한 여러 메시지를 포함한다. 숫자는 각도(degree)나 라디안과 같은 다양한 단위 측정도 표현 가능하다. 아래는 변환을 실행하는 메시지들이다.
변환하기 | |
degreesToRadians | 수신자는 각도를 표현하는 것으로 추측된다. 라디안으로 변환값을 응답한다. |
radiansToDegrees | 수신자는 라디안을 표현하는 것으로 추측된다. 각도로 변환값을 응답한다. |
Number 인스턴스 프로토콜 |
따라서 아래와 같이 변환된다.
30 degreesToRadians = 0.523599
90 degreesToRadians = 1.5708
삼각함수와 로그함수는 수학 함수를 위한 프로토콜에 포함된다. 삼각 함수 cos, sin, tan에 대한 수신자는 라디안으로 측정된 각도로, 함수 arcCos, arcSin, arcTan의 결과는 라디안으로 측정된 각도다.
아래 예제에서는 30도가 0.523599 라디안으로, 90도가 1.5708 라디안으로 주어진다.
표현식 | 결과 |
0.523599 sin | 0.5 |
0.523599 cos | 0.866025 |
0.523599 tan | 0.57735 |
1.5708 sin | 1.0 |
0.57735 arcTan | 0.523551 |
1.0 arcSin | 1.5708 |
어떤 Integer 유형이 다른 유형의 Integer에 자신을 추가하도록 요청 받으면 리턴된 결과는 자연스레 Integer의 유형이 될 것이다. 두 개의 Float를 합할 때도 마찬가지로 결과가 되는 클래스는 피연산자의 클래스와 동일해질 것이다. 두 개의 피연산자가 SmallInteger이고 그들의 총합의 절대값이 SmallInteger로 표현하기엔 너무 클 경우, 결과는 LargePositiveInteger와 LargeNegativeInteger 중 하나가 될 것이다. 피연산자가 다른 클래스일 경우 결과의 적절한 클래스를 결정하는 일은 약간 더 복잡하다. 두 가지 디자인 기준이 있는데, 가능한 한 정보의 손실이 적을 것이란 점이 첫 번째 기준이고, 두 번째는 어떤 피연산자가 메시지의 수신자이고 어떤 것이 인자에 해당하는지와 상관없이 가환연산(commutative operations)은 동일한 결과를 도출한다는 기준이다. 따라서 3.1*4를 예로 들면 4*3.1과 같은 결과를 리턴할 것이다.
여러 클래스의 숫자에 실행한 연산의 결과에 적절한 표현은 각 클래스로 할당된 일반성(generality)의 수치적 측정으로 결정된다. 클래스의 일반성이 높으면 일반성 측정에 대해 수치가 클 것이다. 각 클래스는 그 인스턴스를 좀 더 일반적인 클래스에서 동일한 값으로 된 인스턴스로 변환할 수 있어야 한다. 일반성의 측정은 어떤 피연산자를 변환해야 하는지 결정 시 사용된다. 이렇게 산술 연산은 어떠한 수치적 정보도 손실하지 않고 가환성(commutativity) 법칙을 준수한다. 두 숫자 클래스 간에 차이가 정밀성의 문제에만 해당한다면 (“정밀성”이란 숫자에 제공된 정보의 측정을 의미) 정밀한 클래스에는 더 높은 수준의 일반성이 할당된다. 정밀성이 문제가 되지 않을 때를 대비해 어림수에 더 높은 일반성을 임의로 할당하였다 (따라서 Float은 Fraction보다 더 일반적이다).
Smalltalk-80 시스템에서 일반성 계층구조는 다음과 같으며, 가장 일반적인 숫자가 가장 위에 위치한다.
- Float
- Fraction
- LargePositiveInteger, LargeNegativeInteger
- SmallInteger
필요한 강제 변환을 지원하도록 고안된 Number 프로토콜 내 메시지는 "coercing"(강제 변환) 메시지로 분류된다.
coercing | |
coerce: aNumber | Answer a number representing the argument, aNumber, that is the same kind of Number as the receiver. This method must be defined by all subclasses of Number. |
generality | Answer the number representing the ordering of the receiver in the generality hierarchy. |
retry: aSymbol coercing: aNumber | An arithmetic operation denoted by the symbol, aSymbol, could not be performed with the receiver and the argument, aNumber, as the operands because of the difference in representation. Coerce either the receiver or the argument, depending on which has the lower generality, and then try the arithmetic operation again. If the symbol is the equals sign, answer false if the argument is not a Number. If the generalities are the same, then retry:coercing: should not have been sent, so report an error to the user. |
Number instance protocol |
따라서 32.45 * 4를 평가하려고 하면 Float와 SmallInteger의 곱은 아래 표현식을 평가하는 결과를 낳을 것이고,
32.45 retry: #* coercing: 4
인자 4는 4.0으로 강제 변환될 것이다 (Float은 SmallInteger보다 일반성 수준이 높다). 이후 곱셈이 성공적으로 실행될 것이다.
일반성의 수치 측정과 관련된 숫자의 계층구조의 정의는 기본 Smalltalk-80 시스템에 제공되는 숫자 유형에도 효과가 있는데, 일반성은 이러한 숫자 유형에 이행적(transitive)이기 때문이다. 하지만 모든 유형의 숫자에 이용할 수 있는 기법은 제공하지 않는다.
Intervals(제 10장에 상세히 설명)는 두 메시지 중 하나를 숫자로 전송하면 생성할 수 있다. 그러한 interval의 각 요소마다 요소를 블록 값으로 하여 블록을 평가할 수 있다.
인터벌(intervals) | |
to: stop | 수신자부터 stop 인자까지 Interval을 응답하되 각각의 다음 요소는 이전 요소에서 1씩 증가시켜 계산된다. |
to: stop by: stop | 수신자부터 stop 인자까지 Interval을 응답하되 각각의 다음 요소는 이전 요소에서 step 만큼 증가시켜 계산된다. |
to: stop do: aBlock | 수신자부터 stop 인자까지 Interval을 응답하되 각각의 다음 요소는 이전 요소에서 1씩 증가시켜 계산된다. Interval의 각 요소마다 aBlock 인자를 평가한다. |
to: stop by: step do: aBlock | 수신자부터 stop 인자까지 Interval을 응답하되 각각의 다음 요소는 이전 요소에서 step 만큼 증가시켜 계산된다. Interval의 각 요소마다 aBlock 인자를 평가한다. |
Number 인스턴스 프로토콜 |
따라서 아래를 평가하면
a ← 0.
10 to: 100 by: 10 do: [ :each | a ← a + each]
a의 최종 값은 550이 될 것이다.
a가 #('one' 'two' 'three' 'four' 'five') 배열이라면 배열 내 각 요소는 1부터 배열의 크기까지 interval에 있는 색인에서 접근할 수 있다. 아래 표현식은 각 요소를 변경시켜 처음 문자만 유지되도록 한다.
1 to: a size do: [:index | a at: index put: ((a at: index) at: 1)]
결과적 배열은 #('o' 't' 't' "f' "f')가 된다. 배열과 마찬가지로 문자열의 요소는 at: 과 at:put: 메시지를 이용해 접근 가능함을 주목한다. 문자열이나 배열과 같은 객체로 전송되는 메시지는 제 9장과 10장에서 상세히 다루겠다.
Float과 Fraction 클래스
Float과 Fraction 클래스는 두 가지 non-integral 값의 표현을 제공한다. Floats는 근사한 실수의 표현으로, ±10±32 범위의 6자리수 정도의 정확도를 표현한다. 몇 가지 예를 들자면 다음과 같다.
8.0
13.3
0.3
2.5e6
1.27e - 30
-12.987654e12
Fractions는 유리수의 표현으로서 항상 정확할 것이다. Fraction에서 실행한 모든 산술 연산은 줄인 분수 결과(reduced fractional result)를 응답한다.
Float의 인스턴스는 메서드에 리터럴 표기로 인해 (예: 3.14159) 혹은 또 다른 Float를 인자로 한 산술 연산의 결과로 생성할 수 있다.
Fraction의 인스턴스는 피연산자 중 하나가 Fraction이고 다른 하나가 Float이 아닌 경우 산술 연산의 결과로 생성될 수 있다. (Float이라면 Float의 일반성 숫자는 Fraction의 숫자보다 높으므로 결과는 Float이 될 것이다.) Fraction의 인스턴스는 수학적 나눗셈 연산(/)이 두 개의 Integer에서 실행되고 결과가 integral이 아닐 경우에도 생성될 수 있다. 뿐만 아니라 Fraction에 대한 클래스 프로토콜은 인스턴스를 생성하기 위해 numerator: numInteger denominator: denInteger 형태의 메시지 전송을 지원한다.
Float은 해당하는 상수를 리턴하기 위해 pi 메시지에 응답한다. 이는 수신자의 분수와 정수 부분을 (fractionPart와 integerPart) 리턴하기 위해 절단과 내림 프로토콜을 추가하고, 수신자를 Fraction으로 (asFraction) 변환하기 위해 변환 프로토콜을 추가한다. 이와 비슷하게 Fraction 클래스는 수신자를 Float으로 (asFloat) 변환하기 위한 변환 프로토콜을 추가한다.
정수 클래스
Integer 클래스는 진정수에 특정적인 프로토콜을 추가한다. 이는 세 개의 서브클래스를 갖는다. 하나는 SmallInteger 클래스로, 계수와 색인에 자주 발생하는 정수값의 상당한 범위에 대해 공간 절약적인 표현을 제공한다. 표현은 하나의 머신 단어가 표현 가능한 크기보다 약간 더 작게 범위를 제한한다. 정수 부호에 따라 LargePositiveInteger 또는 LargeNegativeInteger의 인스턴스로 표현되는 큰 정수들은 그 크기를 제한하지 않는다. 큰 정수의 일반성을 제공하면 계산 시간이 길어진다. 따라서 큰 정수에서 산술 연산의 결과가 작은 정수로 표현 가능할 경우 사실상 작은 정수가 될 것이다.
Number 클래스로부터 상속된 메시지 외에도 Integer 클래스는 변환 프로토콜(asCharacter, asFloat, asFraction), 추가로 인쇄 프로토콜(printOn: aStream base: b, radix: baseInteger)과 열거 프로토콜을 추가한다. 따라서 8 radix: 2는 2r1000가 된다.
열거의 경우, timesRepeat: aBlock 메시지를 이용해 블록을 진정수의 횟수만큼 반복하여 평가하는 것이 가능하다. 아래를 예로 들어보면,
a ← 1.
10 timesRepeat: [a ← a + a]
블록에는 인자가 없다. a의 결과는 2^10 또는 1024가 된다.
Integer 클래스는 일반적으로 숫자에 대해 명시되지 않은 인수분해와 가분성 프로토콜을 제공한다.
인수분해와 가분성 | |
factorial | 수신자의 계승을 응답한다. 수신자는 0보다 작아선 안 된다. |
gcd: anInteger | 수신자와 anInteger 인자의 최대공약수를 응답한다. |
lcm: anInteger | 수신자와 anInteger 인자의 최소공배수를 응답한다. |
Integer 인스턴스 프로토콜 |
예를 들면 아래와 같다.
표현식 | 결과 |
3 factorial | 6 |
55 gcd: 30 | 5 |
6 lcm: 10 | 30 |
정수의 숫자 프로퍼티 외에도 일부 알고리즘은 정수를 비트의 시퀀스로 해석할 수 있다는 사실을 이용한다. 따라서 비트 조작에 관한 프로토콜은 Integer에 명시된다.
비트조작(bit manipulation) | |
allMask: anInteger | 인자 anInteger를 비트 마스크로 취급한다. anInteger에서 1에 해당하는 비트 모두 수신자에서 1인지 응답한다. |
anyMask: anInteger | 인자 anInteger를 비트 마스크로 취급한다. anInteger에서 1에 해당하는 비트 모두 수신자에서 1인지 응답한다. |
noMask: anInteger | 인자 anInteger를 비트 마스크로 취급한다. anInteger에서 1에 해당하는 비트 중 어떤 비트도 수신자에서 1에 해당하지 않는지 여부를 응답한다. |
bitAnd: anInteger | 비트가 논리적이고(and) 수신자의 비트이며(and) 인자 anInteger의 비트인 Integer를 응답한다. |
bitOr: anInteger | 비트가 논리적이거나(or) 수신자의 비트이고(and) 인자 anInteger의 비트인 Integer를 응답한다. |
bitXor: anInteger | 비트가 논리적이거나(xor) 수신자의 비트이고(and) 인자 anInteger의 비트인 Integer를 응답한다. |
bitAt: index | 수신자의 위치 색인에 비트(0 또는 1)를 응답한다. |
bitInvert | 비트가 수신자의 보수인 Integer를 응답한다. |
highBit | 수신자의 이진 표현에서 가장 높은 비트의 색인을 응답한다. |
bitShift: anInteger | 값이 (2의 보수 표현) 수신자의 (2의 보수 표현) 값으로, 인자 anInteger가 나타내는 비트수만큼 왼쪽으로 이동되어 있는 Integer를 응답한다. 음의 인자는 오른쪽으로 이동한다. 0은 왼쪽 시프트에서 오른쪽으로부터 이동한다. 부호 비트는 오른쪽 시프트에서 확장된다. |
Integer 인스턴스 프로토콜 |
몇 가지 예를 들자면 다음과 같다. Integer를 출력하기 위한 기본 기수는 10이다.
표현식 | 결과 |
2r111000111000111 | 29127 |
2r101010101010101 | 21845 |
2r101000101000101 | 20805 |
2r000111000111000 | 3640 |
29127 allMask: 20805 | true |
29127 allMask: 21845 | false |
29127 anyMask: 21845 | true |
29127 noMask: 3640 | true |
29127 bitAnd: 3640 | 0 |
29127 bitOr: 3640 | 32767 |
32767 radix: 2 | 2r111111111111111 |
29127 bitOr: 21845 | 30167 |
30167 radix: 2 | 2r111010111010111 |
3640 bitShift: 1 | 7280 |
Random 클래스: 난수 발생기
많은 애플리케이션에서는 난수를 선택하도록 요구한다. 난수는 가령 통계학 애플리케이션과 데이터 암호 알고리즘에 유용하다. Random 클래스는 난수 발생기로, 표준 Smalltalk-80 시스템에 포함되어 있다. 이는 0.0과 1.0은 포함하지 않고 그 사이에서 간격에 걸쳐 균일하게 분포되는 난수의 시퀀스를 간단히 얻는 방법을 제공한다.
Random 클래스의 인스턴스는 다음 난수가 생성되는 시드를 유지한다. 시드는 의사 난수 방식으로 초기화된다. 새로운 난수가 생성될 때마다 Random의 인스턴스에 next 메시지가 전송된다.
난수 발생기는 아래와 같은 표현식으로 생성할 수 있다.
rand ← Random new
이후 새로운 난수가 필요할 때마다 아래의 표현식을 평가할 수 있다.
rand next
그에 대한 응답은 0.0과 1.0 사이의 숫자가 (Float) 된다.
next의 구현은 Knuth, Volume 1 [D. E. Knuth, The Art of Computer Programming: Fundamental Algorithms, Volume 1, Reading, Mass: Addison Wesley, 1968]에 표시된 바와 같이 Lehmer의 선형 합동 방식을 기반으로 한다.
next
| temp |
"Lehmer's linear congruential method with modulus m = 2 raisedTo: 16, a = 27181 odd, and 5 = a \\ 8, c = 13849 odd, and c / m approximately 0.21132"
[seed ← 13849 + (27181 * seed) bitAnd: 8r177777.
temp ← seed / 65536.0.
temp = 0] whileTrue.
↑temp
난수의 anInteger 개수만큼 OrderedCollection 를 얻기 위해서는 next: anInteger 메시지를, 그리고 다음 난수가 aNumber와 동일한지 결정하기 위해서는 nextMatchFor: aNumber 메시지를 Random 클래스의 인스턴스로 전송하는 수도 있다.
난수 발생기 rand를 이용해 1, ..., 10까지 10개 정수 중 하나를 선택하길 원한다고 가정하자. 평가해야 할 표현식은 아래와 같다.
(rand next * 10) truncated + 1
즉 아래의 결과가 나온다.
표현식 | 결과 |
rand next | a random number between 0 and 1 |
rand next * 10 | a random number between 0 and 10 |
(rand next * 10) truncated | an integer > = 0 and < = 9 |
(rand next * 10) truncated + 1 | an integer > = 1 and < = 10 |