erlang 공부 : A Short Visit to Common Data Structures

    참고

     


    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"]}

    위 코드에서처럼 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는 다음 전략으로 사용할 것을 권장하는데 가독성과 기능 확장에 유연하기 때문이다. 

    1. Record는 특정 모듈 내에서만 사용하도록 한다.
    2. 다른 모듈에서 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의 필드에 접근이 필요할 경우 한번 감싼 메서드를 통해서 접근하도록 한다. 

     

     

     

     

     

     

     

     

     

     

    댓글

    Designed by JB FACTORY