문제

여러 개의 (키, 값) 쌍이 포함된 Perl 해시가 있는 경우 모든 키를 반복하는 데 선호되는 방법은 무엇입니까?사용한다고 들었는데 each 어떤 식으로든 의도하지 않은 부작용이 있을 수 있습니다.그러면 그게 사실인가요? 다음 두 가지 방법 중 하나가 가장 좋나요, 아니면 더 좋은 방법이 있나요?

# Method 1
while (my ($key, $value) = each(%hash)) {
    # Something
}

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}
도움이 되었습니까?

해결책

경험상 가장 중요한 것은 귀하의 필요에 가장 적합한 기능을 사용하는 것입니다.

열쇠만 원하고 그럴 계획이 없다면 읽다 값 중 하나를 사용하려면 키()를 사용하세요.

foreach my $key (keys %hash) { ... }

값만 원하면 value()를 사용하십시오.

foreach my $val (values %hash) { ... }

열쇠가 필요한 경우 그리고 값을 보려면 각각()을 사용하세요.

keys %hash; # reset the internal iterator so a prior each() doesn't affect the loop
while(my($k, $v) = each %hash) { ... }

어떤 방식으로든 해시 키를 변경하려는 경우 제외하고 반복 중에 현재 키를 삭제하려면 Each()를 사용하면 안 됩니다.예를 들어, 두 배의 값을 가진 새로운 대문자 키 세트를 생성하는 다음 코드는 키()를 사용하여 잘 작동합니다.

%h = (a => 1, b => 2);

foreach my $k (keys %h)
{
  $h{uc $k} = $h{$k} * 2;
}

예상되는 결과 해시를 생성합니다.

(a => 1, A => 2, b => 2, B => 4)

하지만 Each()를 사용하여 동일한 작업을 수행합니다.

%h = (a => 1, b => 2);

keys %h;
while(my($k, $v) = each %h)
{
  $h{uc $k} = $h{$k} * 2; # BAD IDEA!
}

예측하기 어려운 방식으로 잘못된 결과가 생성됩니다.예를 들어:

(a => 1, A => 2, b => 2, B => 8)

그러나 이는 안전합니다.

keys %h;
while(my($k, $v) = each %h)
{
  if(...)
  {
    delete $h{$k}; # This is safe
  }
}

이 모든 내용은 Perl 문서에 설명되어 있습니다.

% perldoc -f keys
% perldoc -f each

다른 팁

사용시 주의할 점 중 하나 each 해시에 "상태"를 추가하는 부작용이 있다는 것입니다 (해시는 "다음"키가 무엇인지 기억해야합니다).한 번에 전체 해시를 반복하는 위에 게시 된 스 니펫과 같은 코드를 사용하는 경우 일반적으로 문제가되지 않습니다.그러나 사용시 문제를 추적하기가 어려울 것입니다 (경험에서 말합니다.) each 다음과 같은 진술과 함께last 또는 return 에서 나가기 위해 while ... each 모든 키를 처리하기 전에 루프.

이 경우 해시는 이미 반환 한 키와 사용할 때를 기억합니다. each 다음에 (아마도 완전히 관련이없는 코드로),이 위치에서 계속 될 것입니다.

예:

my %hash = ( foo => 1, bar => 2, baz => 3, quux => 4 );

# find key 'baz'
while ( my ($k, $v) = each %hash ) {
    print "found key $k\n";
    last if $k eq 'baz'; # found it!
}

# later ...

print "the hash contains:\n";

# iterate over all keys:
while ( my ($k, $v) = each %hash ) {
    print "$k => $v\n";
}

이것은 다음을 인쇄합니다:

found key bar
found key baz
the hash contains:
quux => 4
foo => 1

"bar"와 baz" 키는 어떻게 되었나요?그들은 여전히 ​​거기에 있지만 두 번째는 있습니다 each 첫 번째 루프가 중단된 지점에서 시작하고 해시 끝에 도달하면 중지되므로 두 번째 루프에서는 이를 볼 수 없습니다.

장소는 each 범위가 지정되지 않은 실제 반복자라는 점이 문제를 일으킬 수 있습니다.예를 들어:

while ( my ($key,$val) = each %a_hash ) {
    print "$key => $val\n";
    last if $val; #exits loop when $val is true
}

# but "each" hasn't reset!!
while ( my ($key,$val) = each %a_hash ) {
    # continues where the last loop left off
    print "$key => $val\n";
}

그걸 확실히 해야 한다면 each 모든 키와 값을 얻으려면 다음을 사용해야 합니다. keys 또는 values 먼저 (반복자를 재설정하므로).참조 각각에 대한 문서.

Each 구문을 사용하면 전체 키 세트가 한 번에 생성되는 것을 방지할 수 있습니다.수백만 개의 행이 있는 데이터베이스에 연결된 해시를 사용하는 경우 이는 중요할 수 있습니다.전체 키 목록을 한꺼번에 생성하여 실제 메모리를 모두 소모하고 싶지는 않을 것입니다.이 경우 각각은 반복자 역할을 하는 반면 키는 루프가 시작되기 전에 실제로 전체 배열을 생성합니다.

따라서 "각"이 실제로 사용되는 유일한 장소는 해시가 매우 큰 경우(사용 가능한 메모리에 비해)입니다.이는 휴대용 데이터 수집 장치나 작은 메모리를 사용하는 장치를 프로그래밍하지 않는 한 해시 자체가 메모리 자체에 상주하지 않는 경우에만 발생할 가능성이 높습니다.

메모리가 문제가 되지 않는다면 일반적으로 맵 또는 키 패러다임이 더 널리 퍼져 있고 읽기 쉬운 패러다임입니다.

이 주제에 대한 몇 가지 기타 생각:

  1. 해시 반복자 자체에는 안전하지 않은 것이 없습니다.안전하지 않은 것은 해시를 반복하는 동안 해시의 키를 수정하는 것입니다.(값을 수정하는 것은 완벽하게 안전합니다.) 제가 생각할 수 있는 유일한 잠재적인 부작용은 values 별칭을 반환합니다. 이는 별칭을 수정하면 해시의 내용도 수정된다는 의미입니다.이는 의도적으로 설계된 것이지만 상황에 따라 원하는 결과가 아닐 수도 있습니다.
  2. 존스 수락된 답변 한 가지 예외를 제외하고는 좋습니다.해시를 반복하는 동안 키를 추가하는 것이 안전하지 않다는 것이 문서에 명확하게 나와 있습니다.일부 데이터 세트에서는 작동할 수 있지만 해시 순서에 따라 다른 데이터 세트에서는 실패할 수 있습니다.
  3. 이미 언급했듯이 에서 반환된 마지막 키를 삭제하는 것이 안전합니다. each.이것은 ~ 아니다 에 대한 사실 keys ~처럼 each 동안은 반복자입니다 keys 목록을 반환합니다.

저도 항상 2번 방법을 사용합니다.각각을 사용하는 유일한 이점은 해시 항목의 값을 읽기만 하는 경우(재할당하지 않고) 해시를 지속적으로 역참조하지 않는다는 것입니다.

이 부분에 물릴 수도 있지만 개인 취향이라고 생각합니다.문서에서 각각()이 키() 또는 값()과 다르다는 언급을 찾을 수 없습니다(명백한 "다른 것을 반환합니다"라는 답변 제외).실제로 문서에는 동일한 반복자를 사용하고 모두 복사본 대신 실제 목록 값을 반환하며 호출을 사용하여 반복하는 동안 해시를 수정하는 것은 좋지 않다고 명시되어 있습니다.

즉, 나는 거의 항상 키()를 사용합니다. 왜냐하면 해시 자체를 통해 키 값에 액세스하는 것이 일반적으로 자체 문서화에 더 가깝기 때문입니다.값이 큰 구조에 대한 참조이고 해시의 키가 이미 구조에 저장되어 있으면 키가 중복되어 필요하지 않을 때 때때로 value()를 사용합니다.나는 10년 동안 Perl 프로그래밍을 하면서 각각()을 2번 사용했다고 생각하는데 아마도 두 번 다 잘못된 선택이었을 것입니다 =)

나는 보통 사용한다 keys 그리고 마지막으로 다음을 사용하거나 사용법을 읽은 것이 언제인지 생각나지 않습니다. each.

잊지 마세요 map, 루프에서 수행 중인 작업에 따라 달라집니다!

map { print "$_ => $hash{$_}\n" } keys %hash;

나는 이렇게 말하고 싶습니다:

  1. 대부분의 사람들이 읽고 이해하기 가장 쉬운 것을 사용하십시오. (그러므로 일반적으로 키가 필요하다고 생각합니다.)
  2. 전체 코드 베이스를 통해 일관되게 결정한 내용을 사용하십시오.

이는 두 가지 주요 이점을 제공합니다.

  1. 함수/메소드로 리팩토링할 수 있도록 "공통" 코드를 찾는 것이 더 쉽습니다.
  2. 미래의 개발자가 유지 관리하기가 더 쉽습니다.

각각에 대해 키를 사용하는 것이 더 비싸다고 생각하지 않으므로 코드에서 동일한 것에 대해 두 개의 서로 다른 구성이 필요하지 않습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top