레코드
레코드 (Records)
지금까지 만난 모든 타입은 값이 분해될 수 없는 타입이었어요. 각 인스턴스가 단일 데이터 조각을 나타냈죠. 이제 첫 번째 합성(composite) 타입 종류인 레코드(record) 를 볼 거예요. 레코드는 다른 타입의 인스턴스들로 값을 구성할 수 있게 해줘요. 각 인스턴스에는 이름이 주어지고, 이름과 특정 타입의 인스턴스의 쌍을 필드(field) 또는 컴포넌트(component) 라고 해요.
출처: 레코드 문서
본문
레코드 타입 선언 (Record type declaration)
간단한 레코드 선언 예시:
type Date is record
-- The following declarations are
-- components of the record
Day : Integer range 1 .. 31;
Month : Months;
-- You can add custom constraints
-- on fields
Year : Integer range 1 .. 3000;
end record;
필드는 변수 선언과 아주 비슷해 보이지만, 레코드 정의 안에 있다는 점이 달라요. 그리고 변수 선언과 마찬가지로 필드의 하위 타입을 지정할 때 추가 제약을 지정할 수 있어요.
type Date is record
Day : Integer range 1 .. 31;
Month : Months := January;
-- This component has a default value
Year : Integer range 1 .. 3000 := 2012;
-- ^^^^
-- Default value
end record;
레코드 컴포넌트는 기본값을 가질 수 있어요. 레코드 타입의 변수가 선언되면 기본 초기화가 있는 필드는 자동으로 이 값으로 설정돼요. 그 값은 컴포넌트 타입의 어떤 표현식이든 될 수 있고 런타임에 계산될 수도 있어요.
이 장의 나머지 절에서는 레코드 타입을 사용하는 방법을 볼 거예요. 레코드에 대해 더 자세히는 다른 장에서 다룰게요.
애그리게이트 (Aggregates)
-- Positional components
Ada_Birthday : Date := (10, December, 1815);
-- Named components
Leap_Day_2020 : Date := (Day => 29,
Month => February,
Year => 2020);
-- ^ By name
레코드는 위에 나온 것처럼 값을 표현하는 편리한 표기법을 가지고 있어요. 이 표기법을 애그리게이트(aggregate) 표기법 이라고 하고, 리터럴을 애그리게이트 라고 해요. 이것들은 과정 전반에 걸쳐 볼 다양한 문맥에서 사용될 수 있으며, 그중 하나가 레코드 초기화예요.
애그리게이트는 쉼표로 구분되고 괄호로 감싼 값들의 목록이에요. 레코드 값이 기대되는 어떤 문맥에서든 허용돼요.
컴포넌트 값은 Ada_Birthday 예시처럼 위치로 지정하거나, Leap_Day_2020처럼 이름으로 지정할 수 있어요. 위치와 이름 값의 혼합은 허용되지만, 이름 뒤에 위치 표기법은 사용할 수 없어요.
컴포넌트 선택 (Component selection)
레코드 인스턴스의 컴포넌트에 접근하려면 컴포넌트 선택(component selection) 이라는 연산을 사용해요. 이것은 점 표기법으로 이루어져요. 예를 들어 위 Date 레코드 타입의 변수 Some_Day를 선언했다면 Some_Day.Year라고 써서 Year 컴포넌트에 접근할 수 있어요.
예시를 볼게요:
with Ada.Text_IO; use Ada.Text_IO;
procedure Record_Selection is
type Months is
(January, February, March, April,
May, June, July, August, September,
October, November, December);
type Date is record
Day : Integer range 1 .. 31;
Month : Months;
Year : Integer range 1 .. 3000 := 2032;
end record;
procedure Display_Date (D : Date) is
begin
Put_Line ("Day:" & Integer'Image (D.Day)
& ", Month: "
& Months'Image (D.Month)
& ", Year:"
& Integer'Image (D.Year));
end Display_Date;
Some_Day : Date := (1, January, 2000);
begin
Display_Date (Some_Day);
Put_Line ("Changing year...");
Some_Day.Year := 2001;
Display_Date (Some_Day);
end Record_Selection;
이 예시에서 D.Year나 Some_Day.Year 같은 표현식에서 점 표기법을 사용해 그 컴포넌트에 저장된 정보를 접근하고, 대입에서 그 정보를 수정할 수 있음을 볼 수 있어요. 구체적으로, Put_Line 호출에서 D.Year를 사용하면 그 컴포넌트에 저장된 정보를 검색하는 것이고, Some_Day.Year := 2001이라고 쓰면 Some_Day의 Year 컴포넌트에 이전에 저장된 정보를 덮어쓰는 것이에요.
이름 변경 (Renaming)
이전 장에서 서브프로그램과 패키지 이름 변경을 다뤘어요. 레코드 컴포넌트도 이름을 바꿀 수 있어요. 점 표기법으로 전체 컴포넌트 선택을 쓰는 대신, 같은 컴포넌트에 접근할 수 있는 별칭을 선언할 수 있어요. 이는 예를 들어 서브프로그램 구현을 단순화하는 데 유용해요.
변수 선언에서 renames 키워드를 사용해 레코드 컴포넌트의 이름을 바꿀 수 있어요. 예를 들어:
Some_Day : Date;
Y : Integer renames Some_Day.Year;
여기서 Y는 별칭이어서, Y를 쓸 때마다 실제로는 Some_Day의 Year 컴포넌트를 사용하는 것이에요.
완전한 예시를 볼게요:
package Dates is
type Months is
(January, February, March, April,
May, June, July, August, September,
October, November, December);
type Date is record
Day : Integer range 1 .. 31;
Month : Months;
Year : Integer range 1 .. 3000 := 2032;
end record;
procedure Increase_Month
(Some_Day : in out Date);
procedure Display_Month
(Some_Day : Date);
end Dates;
with Ada.Text_IO; use Ada.Text_IO;
package body Dates is
procedure Increase_Month
(Some_Day : in out Date)
is
-- Renaming components from
-- the Date record
M : Months renames Some_Day.Month;
Y : Integer renames Some_Day.Year;
-- Renaming function (for Months
-- enumeration)
function Next (M : Months)
return Months
renames Months'Succ;
begin
if M = December then
M := January;
Y := Y + 1;
else
M := Next (M);
end if;
end Increase_Month;
procedure Display_Month
(Some_Day : Date)
is
-- Renaming components from
-- the Date record
M : Months renames Some_Day.Month;
Y : Integer renames Some_Day.Year;
begin
Put_Line ("Month: "
& Months'Image (M)
& ", Year:"
& Integer'Image (Y));
end Display_Month;
end Dates;
with Ada.Text_IO; use Ada.Text_IO;
with Dates; use Dates;
procedure Main is
D : Date := (1, January, 2000);
begin
Display_Month (D);
Put_Line ("Increasing month...");
Increase_Month (D);
Display_Month (D);
end Main;
Increase_Month 프로시저 구현에서 Date 레코드의 두 컴포넌트에 이름 변경을 적용해요. 그리고 다음 연산에서 Some_Day.Month와 Some_Day.Year를 직접 사용하는 대신, 이름 변경된 버전 M과 Y를 사용해요.
위 예시에서 다음 달을 주는 함수 Months'Succ도 Next로 이름을 바꾼다는 점에 주의하세요.