참고
Record
% record 정의하기
-record(robot,
{name, % 기본값 undefined
type=industrial, % 기본값 industrial
hobbies, % 기본값 undefiend
details=[]} % 기본값 []
).
% record 생성하기
first_robot() ->
#robot{name="Mechatron", type=handmade, details=["Moved by a small man inside]}.
% record = Named tuple
#robot{name="Mechatron", type=handmade, details=["Moved by a small man inside]}.
% tuple
{robot, "Mechatron", handmade, undefiend, ["Moved by a small man inside"]}
- Records는 C의 구조체와 유사한 형태로 동작한다.
- Record에서 필드에 기본값을 설정하지 않으면, undefined가 기본값이 됨.
- erlang에서는 이름을 가진 tuple로 볼 수 있다.
위 코드에서처럼 erlang의 tuple을 조작한 것이 erlang의 record다. tuple에서 순서를 바꿀 수 없듯이, record 역시 순서를 바꿀 수 없다.
Record의 필드에 접근하기
% record 선언
17> Crusher = #robot{name="Crusher", hobbies=["Crushing people", "petting cats"]}.
#robot{name = "Crusher",type = industrial,
hobbies = ["Crushing people","petting cats"],
details = []}
% record의 특정 필드로 접근
18> Crusher#robot.hobbies.
["Crushing people","petting cats"]
- erlang은 NamedTuple처럼 특정 필드에 이름으로 접근할 수 있다.
- 접근할 때는 #robot을 이용해 어떤 구조체인지 알려줘야하고, ".<필드 이름>"으로 접근할 수 있다.
Record를 이용한 패턴 매칭
-record(user, {id, name, group, age}).
admin_panel(#user{name=Name, group=admin}) ->
io:format("~p is allowed ~n", [Name]);
admin_panel(#user{name=Name}) ->
io:format("~p is not allowed ~n", [Name]).
- 위는 group이 admin에 패턴매칭 되는 경우 allowed를 출력하고, 패턴 매칭되지 않는 경우에는 not allowed를 출력하도록 했다.
-record(user, {id, name, group, age}).
should_adult(U = #user{}) when U#user.age > 19 ->
io:format("This is adult. ~n");
should_adult(_) ->
io:format("This is not adult. ~n").
- 위는 함수의 Guard문 안에서 record의 필드에 직접 접근하도록 작성했다.
- 만약 Guard문 내에서 직접 패턴매칭된 결과를 사용하고 싶다면 #user{age=Age} when Age > 19 형식으로 사용하면 된다.
-record(robot,
{
name,
type=industrial,
hobbies,
details = []
}
).
should_no_detail_robot(Robot = #robot{details=[]}) ->
io:format("~p has no details at all.~n", [Robot]);
should_no_detail_robot(Robot) ->
io:format("~p has details. ~n",[Robot]).
robot record의 details 필드가 []인 경우에만 패턴 매칭되도록 해서 출력하도록 했다.
Record의 업데이트
-record(user, {id, name, group, age}).
update_user(User = #user{}, Name) ->
User#user{name=Name}.
- erlang에서 모든 값들은 기본적으로 불변이다. 따라서 Record를 업데이트 한다는 것은 필드값이 바뀐 새로운 Record를 만든다는 것을 의미한다.
위 코드를 얼랭 쉘에서 실행하면 다음 결과를 받아볼 수 있다.
% User 생성
1> U = #user{name="frist"}.
#user{id = undefined,name = "frist",group = undefined,
age = undefined}
% User name 업데이트. (First -> updated_name으로 변경 완료)
2> a_records:update_user(U, "updated_name").
#user{id = undefined,name = "updated_name",
group = undefined,age = undefined}
Header를 이용한 Record 재사용
% 헤더 파일에 record 정의
% record_header.hrl
-record(test_record, {name, age}).
-record(first_record, {name, age}).
% 헤더 파일을 include 해서 사용함.
% other_module.erl
-include("record_header.hrl").
is_included() ->
#test_record(name="Hello").
여러 erlang 모듈에서 공통적으로 사용해야하는 Record라면 Header 파일을 이용해 각 모듈에서 Record를 공통으로 재사용할 수 있게 할 수 있다. 이렇게 만들지 않으면 특정 Record에 필드가 하나 추가되는 경우, Record가 정의된 모든 코드를 찾아서 수정해야한다.
그러나 기본적으로는 Record는 다음 전략으로 사용할 것을 권장하는데 가독성과 기능 확장에 유연하기 때문이다.
- Record는 특정 모듈 내에서만 사용하도록 한다.
- 다른 모듈에서 Record의 내부 필드로 직접 접근하지 못하도록 하고 함수를 통해서 접근하도록 한다.
이 경우 변경지점도 굉장히 적어지고 캡슐화도 되기 때문에 코드를 수정하는데 더 좋은 방향이다. 그 예시는 아래와 같다.
% test1.erl
-record(test2, {name}).
get_test2() ->
#test2{name="test2"}.
get_name(T = #test2{}) ->
T#test2.name.
% test2.erl
test_erlang_import() ->
T = test1:get_test2(),
test1:get_name(T).
- test1.erl에 정의된 Record를 test2.erl에서 사용하더라도 직접적으로 Record의 필드에 접근하지 않도록 한다.
- Record의 필드에 접근이 필요할 경우 한번 감싼 메서드를 통해서 접근하도록 한다.
'프로그래밍 언어 > erlang' 카테고리의 다른 글
erlang 공부 : 컴파일 하기 (0) | 2023.12.13 |
---|---|
erlang 공부 : Designing a Concurrent Application (0) | 2023.12.13 |
erlang 공부 : Errors and Processes (0) | 2023.12.10 |
erlang 공부 : more on multiprocessing (1) | 2023.12.10 |
erlang 공부 : erlang Shell (0) | 2023.12.10 |