2011-08-19 3 views
1

필자는 일련의 erlang 레코드에서 깊이 중첩 된 값을 증가시킬 필요가 있다고 생각했습니다. 명부 작성으로 이것을하는 나의 첫번째 시도는 암울한 실패이었다. 원래 목록에는 대상 값이없는 레코드가 여러 개 포함되어 있습니다. 레코드를 포함하고있는 레코드가 어떤 수준에서는 정의되지 않았기 때문입니다.Erlang에서는 깊이 중첩 된 레코드의 조작을 어떻게 "이해할 수 있습니까?"

목록을 사용하면 쉽게 처리 할 수 ​​있습니다. 파티션은 실제로 증가가 필요한 항목 만 필터링하여 필터링하지만 그와 같은 간단한 작업을 수행 할 수있는 목록 이해가 부족합니다.

아래의 코드 샘플은 컴파일되지 않을 것입니다. 단순히 달성하려고 시도한 것을 보여주기위한 것입니다. 나는 내 원래의 문제를 설명하기 위해 섹션은 "정의되지 않은 경우 (ㅋ)"를 넣어 : 내가 함께 무엇을 시작했다

-record(l3, {key, value}). 
-record(l2, {foo, bar, a_thing_of_type_l3}). 
-record(l1, {foo, bar, a_thing_of_type_l2}). 

increment_values_recursive([], Acc 
increment_values_recursive([L1 | L1s], Acc) -> 
    case L1#l1.a_thing_of_type_l2 of 
     undefined -> NewRecord = L1; 
     L2  -> 
      case L2#l2.a_thing_of_type_l3 of 
       undefined -> NewRecord = L2; 
       {Key, Value} -> 
        NewRecord = L1#l1{l2 = L2#l2{l3 = {Key, Value + 1}}} 
      end 
    end, 

    increment_values_recursive(L1s, [NewRecord | Acc]). 

increment_values(L1s) -> 
    lists:reverse(increment_values_recursive(L1s, [])). 

........ 

NewList = increment_values(OldList). 

을하지만, 나는이 때 목록을 처리 할 지능형리스트를보고 드리겠습니다 정의되지 않은 멤버를 확인할 필요가 없었습니다. 정말 이런 식으로 뭔가 :

increment_values_recursive([], Acc 
increment_values_recursive([L1 | L1s], Acc) -> 
    %I'm VERY SURE that this doesn't actually compile: 
    #l1{l2 = #l2{l3 = #l3{_Key, Value} = L3} = L2} = L1, 
    %same here: 
    NewRecord = L1#l1{l2=L2#l2{l3=L3#l3{value = Value+1}}}, 
    increment_values_recursive(L1s, [NewRecord | Acc]). 

increment_values(L1s) -> 
    lists:reverse(increment_values_recursive(L1s, [])). 

AKA는 :

typedef struct { int key, value; } l3; 
typedef struct { int foo, bar; l3 m_l3 } l2; 
typedef struct { int foo, bar; l2 m_l2 } l1; 

for (int i=0; i<NUM_IN_LIST; i++) 
{ 
    objs[i].m_l2.m_l3.value++; 
} 

답변

1

쓸 수있는 시간이 없었어 싶은 것들 중 하나입니다. @Peer Stritzinger는 좋은 대답을했지만 여기는 깨끗한 목록으로 이해할 수 있습니다.

-record(l3, {key, value}). 
-record(l2, {foo=foo, bar=bar, al3}). 
-record(l1, {foo=foo, bar=bar, al2}). 

increment(#l1{al2 = Al2}=L1) -> L1#l1{al2 = increment(Al2)}; 
increment(#l2{al3 = Al3}=L2) -> L2#l2{al3 = increment(Al3)}; 
increment(#l3{value = V}=L3) -> L3#l3{value = V + 1}. 

test() -> 
    List = 
    [ #l1{al2=#l2{al3=#l3{key=0, value = 100}}} 
    , #l1{al2=#l2{al3=#l3{key=1, value = 200}}} 
    , #l1{al2=#l2{al3=#l3{key=2, value = 300}}} 
    , #l1{al2=#l2{al3=#l3{key=3, value = 400}}}], 
    [increment(L) || L <- List]. 
2

이것은 파괴적인 돌연변이와 언어 것보다 waaaay를 지저분하지만, 그것은 확실히 가능하다. 여기에 흙이다 :

increment(Records) -> 
    [L1#l1{l2 = (L1#l1.l2)#l2{l3 = ((L1#l1.l2)#l2.l3)#l3{value = ((L1#l1.l2)#l2.l3)#l3.value + 1}}} || L1 <- Records]. 

당신이 볼 수 있듯이,이 지옥 같은 추악한; 또한,이 이해가 무엇을하고 있는지 즉시 이해하는 것은 어렵습니다. 무슨 일이 일어나고 있는지 파악하는 것은 간단 합니다만, 이런 식으로 편지를 쓰는 가게의 모든 사람들과 이야기를 나눌 수 있습니다. Erlang 컴파일러와 런타임은 이러한 종류의 패턴을 최적화하는 데 아주 능숙합니다.

+1

@Peer와 같이 도우미 기능을 사용하면 Stritzinger가 매우 쉽게 읽을 수 있습니다. 함수를 정의하지 않으려면 대신 재미를 사용하십시오. 이 경우 나는 축적하고 뒤집을 필요가 없다. – rvirding

+0

당신은 절대적으로 정확합니다. 도우미 기능 또는 2 가지를 사용하면 이것은 사소하고 읽기 쉽습니다. 나는 내가 쓰여진 것보다 더 많은 것을 읽었다 고 생각한다. 여기서는 바인딩 및 함수 호출을 최소화하는 것이 목표라고 가정했습니다. – Ben

3

목록 이해를 사용할 수 있으며 중첩이없는 레코드를 걸러 낼 필요조차 없습니다.

가독성 문제를 피하기 위해 레코드 정의가 짧아졌습니다.

-record(l3, {key, value}). 
-record(l2, {foo, bar, al3}). 
-record(l1, {foo, bar, al2}). 

는 값을 증가하는 도우미 함수를 정의

inc_value(#l1{al2=#l2{al3=#l3{value=Value}=L3}=L2}=L1) -> 
    L1#l1{al2=L2#l2{al3=L3#l3{value=Value+1}}}; 
inc_value(R) -> 
    R. 

참고 자체에 패턴과 일치하지 않는 다른 물건을 매핑 마지막 절을.

1> R=#l1{foo=1, bar=2}. 
#l1{foo = 1,bar = 2,al2 = undefined} 

이 정의 된 전체 중첩이없는 기록이다 :

이 밖으로 시도 예제 레코드를 정의 할 수 있습니다.

2> R1=#l1{foo=1, bar=2, al2=#l2{foo=3, bar=4, al3=#l3{key=mykey, value=10}}}. 
#l1{foo = 1,bar = 2, 
    al2 = #l2{foo = 3,bar = 4, 
       al3 = #l3{key = mykey,value = 10}}} 

전체 구조를 가진 또 다른 하나.

는 도우미 함수를 사용 해보세요 :

4> inc_value(R). 
#l1{foo = 1,bar = 2,al2 = undefined} 

그것은 혼자 완전히 중첩되지 기록을 남긴다.

3> inc_value(R1). 
#l1{foo = 1,bar = 2, 
    al2 = #l2{foo = 3,bar = 4, 
       al3 = #l3{key = mykey,value = 11}}} 

완전히 중첩 된 레코드를 증가시킵니다.

이제 지능형리스트는 간단하고 읽기 :

5> [ inc_value(X) || X <- [R, R1] ]. 
[#l1{foo = 1,bar = 2,al2 = undefined}, 
#l1{foo = 1,bar = 2, 
    al2 = #l2{foo = 3,bar = 4, 
       al3 = #l3{key = mykey,value = 11}}}] 
0

가장 좋은 해결책은 함수형 프로그래밍에서 렌즈의 개념으로보고 아마. A 렌즈은 기록의 돌연변이를위한 기능적인 getter 및 setter입니다. 올바르게 완료하면 고차원 렌즈를 쓸 수 있습니다 원시 렌즈.

결과적으로 용도에 맞게 뮤 테이터를 구성한 다음 모든 레코드를 통해 뮤 테이타를 이해할 수 있습니다.

그것은 내가 얼랑에 대한 몇 가지 일을 작성하지만, 정말 그것은 보이는 것처럼 단단하지 않다 :

관련 문제