2017-12-08 3 views
5
sub foo { 
    my @return_value = (1, 2); 
} 
my @receiver = foo(); 

이 할당은 perl의 다른 할당과 동일합니까? 배열은 메모리에 복제됩니까? 나는이 서브 루틴에 의한 배열 유지가 일회용이므로 복제가 완전히 중복 됨으로 인해 이러한 이유가 의심 스럽다. 최적화를 위해 배열을 @receiver에 '연결'하는 것이 좋습니다. 덧붙여서 나는 비슷한 질문을했지만 Perl: function returns reference or copy?하지만 내가 원하는 것을 얻지 못했습니다.perl에서 서브 루틴의 반환 값을 변수에 할당 할 때 메모리에 복제 된 데이터가 있습니까?

저는 Perl5에 대해 말하고 있습니다.

ps. perl에 관한 그런 종류의 주제에 관한 서적이나 자료?

답변

3

명시 적 리턴을 지정하지 않으면 서브 루틴은 마지막 조작의 결과를 리턴합니다.

@return_value@receiver과 별도로 생성되며 서브 루틴 종료시 범위를 벗어나면 값이 복사되고 @return_value에 의해 사용 된 메모리가 해제됩니다.

예 - 사용 된 메모리가 복제됩니다. 당신이 당신을 알고하지 않으면 '이 보통하지만 조기 최적화 될 것

#!/usr/bin/env perl 
use strict; 
use warnings; 

use Data::Dumper; 

sub foo { 
    my $anon_array_ref = [ 1, 2 ]; 
    return $anon_array_ref; 
} 

my $results_from_foo = foo(); 

print Dumper $results_from_foo; 

: 당신이 필사적으로이를 방지하려면

, 당신은 주위에 대한 참조'패스 '를 한 번에 익명의 배열을 만들 수 있습니다 정말 큰 데이터 구조를 다룰 수 있습니다.

참고 - 수행 한 작업을 명확히하는 것이 좋습니다. 할당 후에 하위에 명시 적으로 return;을 포함해야합니다.

+0

* "Perl 이외의 언어에 대해서는"return ""*'return'이 포함되어야합니다! 나는 그것이 서브 루틴의 마지막 문장에서 리턴 될리스트를 평가하는 것이 더 명확하고 (그리고 간략하게) 생각한다. 이전 perls에서는'return'을 추가하기 위해 작업 속도가 느려졌습니다.'return 1; 대신 모듈의 끝에'1;'을 넣으면됩니다. – Borodin

+3

'perl (6th) p. 어떤 프로그래머들은 반환 값이있을 때마다 반환 값을 사용하는 것을 반환 값이라고 문서화하는 수단으로 사용하기를 좋아합니다. 당신은 정말로 그것을 필요로하지 않지만, 아무 것도 아프지 않습니다. 그러나 많은 펄 프로그래머들은 단지 7 글자의 타이핑이라고 생각합니다. _ :) 나는 펄 책을 좋아합니다. :) – jm666

6

:lvalue 하위에서 반환 된 스칼라는 복사되지 않습니다.

XS 하위에 의해 반환 된 스칼라는 복사되지 않습니다.

function (이름 지정된 연산자)에 의해 반환 된 스칼라는 복사되지 않습니다.

다른 하위에서 반환 된 스칼라가 복사됩니다.

하지만 과제가 시작되기 전에. 반환 된 값을 변수에 할당하면 해당 값을 복사하게됩니다 (일반 Perl 하위의 경우 다시).

my $y = sub { $x }->(); 사본 $x을 두 번 의미합니다.

그러나 이는 최적화 때문에 중요하지 않습니다.


복사되지 않은 예부터 시작해 보겠습니다.

$ perl -le' 
    sub f :lvalue { my $x = 123; print \$x; $x } 
    my $r = \f(); 
    print $r; 
' 
SCALAR(0x465eb48) # $x 
SCALAR(0x465eb48) # The scalar on the stack 

하지만 당신은 :lvalue ...

$ perl -le' 
    sub f { my $x = 123; print \$x; $x } 
    my $r = \f(); 
    print $r; 
' 
SCALAR(0x17d0918) # $x 
SCALAR(0x17b1ec0) # The scalar on the stack 

더 나쁜, 하나는 보통 그래서 두 번째 복사본이 발생, 변수에 스칼라를 할당에 의해 다음 제거합니다.

$ perl -le' 
    sub f { my $x = 123; print \$x; $x } 
    my $r = \f(); # \ 
    print $r;  # > my $y = f(); 
    my $y = $$r; #/
    print \$y; 
' 
SCALAR(0x1802958) # $x 
SCALAR(0x17e3eb0) # The scalar on the stack 
SCALAR(0x18028f8) # $y 

더하기 쪽에서는 문자열 복사 비용을 최소화하도록 최적화되어 있습니다.

XS 하위 및 함수 (명명 된 연산자)는 일반적으로 필사적 ("TEMP") 스칼라를 반환합니다. 이것들은 사형 선상에있는 스칼라들입니다. 그것들에 대한 참조를 요구하지 않으면 자동으로 파괴됩니다.

이전 버전의 Perl (< 5.20)에서는 필사적 문자열을 다른 스칼라에 할당하면 문자열 버퍼의 소유권이 이전되어 문자열 버퍼를 복사하지 않아도됩니다. 예를 들어 my $y = lc($x);lc으로 생성 된 문자열을 복사하지 않습니다. 단순히 문자열 포인터가 복사됩니다. 펄의 최신 버전 (≥ 5.20)에서

$ perl -MDevel::Peek -e'my $s = "abc"; Dump($s); $s = lc($s); Dump($s);' 
SV = PV(0x1705840) at 0x1723768 
    REFCNT = 1 
    FLAGS = (PADMY,POK,IsCOW,pPOK) 
    PV = 0x172d4c0 "abc"\0 
    CUR = 3 
    LEN = 10 
    COW_REFCNT = 1 
SV = PV(0x1705840) at 0x1723768 
    REFCNT = 1 
    FLAGS = (PADMY,POK,pPOK) 
    PV = 0x1730070 "abc"\0  <-- Note the change of address from stealing 
    CUR = 3      the buffer from the scalar returned by lc. 
    LEN = 10 

, 할당 연산자 절대 [1] 복사 문자열 버퍼. 대신 최신 버전의 Perl은 COW (Copy-On-Write) 메커니즘을 사용합니다.

$ perl -MDevel::Peek -e'my $x = "abc"; my $y = $x; Dump($x); Dump($y);' 
SV = PV(0x26b0530) at 0x26ce230 
    REFCNT = 1 
    FLAGS = (POK,IsCOW,pPOK) 
    PV = 0x26d68a0 "abc"\0   <----+ 
    CUR = 3        | 
    LEN = 10        | 
    COW_REFCNT = 2       +-- Same buffer (0x26d68a0) 
SV = PV(0x26b05c0) at 0x26ce248   | 
    REFCNT = 1        | 
    FLAGS = (POK,IsCOW,pPOK)    | 
    PV = 0x26d68a0 "abc"\0   <----+ 
    CUR = 3 
    LEN = 10 
    COW_REFCNT = 2 

좋아 지금까지, 나는 스칼라에 대해 이야기했습니다. 자, 서브 및 함수는 스칼라 만 반환 할 수 있기 때문에 [2]입니다. 당신의 예에서

@return_value에 할당 된 스칼라는 [3]가, 복사, 다음 과제로 @receiver로 두 번째를 복사 반환됩니다.

배열에 대한 참조를 반환하면이 모든 현상을 피할 수 있습니다.

sub f { my @fizbobs = ...; \@fizbobs } 
my $fizbobs = f(); 

유일하게 정의되지 않은 스칼라 인 참조가 복사됩니다.


  1. 좋아, 아마 결코. COW 수를 유지하려면 문자열 버퍼에 사용 가능한 바이트가 있어야한다고 생각합니다.

  2. 목록 컨텍스트에서 0 개 또는 1 개를 반환 할 수 있지만 스칼라 만 반환 할 수 있습니다.

  3. 마지막 하위 연산자는 목록 할당 연산자입니다. 목록 컨텍스트에서 목록 할당 연산자는 왼쪽 측면 (LHS)이 평가하는 스칼라를 반환합니다. 자세한 내용은 Scalar vs List Assignment Operator을 참조하십시오.

+3

그래서, 이것은 내가 (아직) 펄을 전혀 모르는 ** ** **을 보장하는 대답입니다.:) Hovever, 나는 그들이 깊은 것들 (몇 년 후)을 이해하는 것을 도울 수 있기 때문에 여기에 그 답이있는 것을 좋아한다. :) – kobame

+1

@amon, 스택은 AV를 포함 할 수있다 (예 :'push'는 스택의 첫 번째 항목을 기대한다. 유명인이 되십시오), 잠수정은 그들을 돌려 보낼 수 없습니다. '@receiver'와'@ return_value'는 적어도 여분의 일을하지 않는 한 같은 배열이 아닙니다 : perl -e 'use feature qw (declared_refs refaliasing say); 내 \ @ 아 = 서브 {내 @ 아; \ @a라고 말하면; \ @a} ->(); \ @a; ''라고 말하십시오. – ikegami

관련 문제