(질문)perl 속의 map 함수.

doodoo의 이미지

생뚱맞게 간만에 와서 또 질문하나만 하게 됩니다.

인터넷에서 찿아낸 파일 썪어주는 함수 입니다.
-- 애들 뽀로로 치로 같은것 틀어 줄때 재생순서를 썪어놓기 위해 찿은건데요.

근데...이해가 안되서 ㅠㅠ

#!/usr/bin/perl
#
use strict;
use warnings;
use Data::Dumper;
use feature "say";
 
$| = 1;
 
sub shuffle(@) {
    my @a = \(@_);
    my $n;
    my $i = @_;
    map {
        $n = rand($i--);
        (${$a[$n]}, $a[$n] = $a[$i])[0];
    } @_;
}
 
my @a = shuffle(@ARGV);
foreach (@a) {
    #`/usr/bin/mplayer "$_"`;
    say 'Play.. ', $_;
    sleep 1.5;
}

문제는 저 shuffle 함수 안의 map 함수입니다.
$n 을 찍어보면 0.8233103 등등의 정수가 아닌 값으로 나오구. $i 는 배열의 역 순서 인것을 알겠는데
$a[$n] = $a[$i] 이 부분이 당췌 이해가 안됨니다...

perl -d t.pl aaa bbb ccc ddd 해서 값을 하나 찍다보면

  DB<56> p Dumper (\@a)
$VAR1 = [
          \'ddd',
          $VAR1->[0],
          \'ccc',
          $VAR1->[0]
        ];

등으로 나와서 더더욱 헤메게 되네요....

혹시 해석좀 해 주실분 계시면 미리 감사드립니다.

익명 사용자의 이미지

rand() 함수에서 나온 $n 값은 array 인덱스로 사용될때 정수값만 사용되요.
따라서 인덱스값이 중복될수가 있기때문에 같은 인덱스가 나왔을때 동일한 값이
출력되지 않게 미리 $a[$n] = $a[$i] 로 설정해주는 것이에요

doodoo의 이미지

같은 인덱스값이 될수 있다는 것은 이해 합니다.
그것을 회피하기 위해 $a[$n] = $a[$i] 로 한다는것이 이해가 되지 않아서요.

이미 위에 적은것 처럼

$VAR1 = [
          \'ddd',
          $VAR1->[0],
          \'ccc',
          $VAR1->[0]
        ];

몇번 map 루프를 돌다보면 $a[1], $a[3] 이 같은 값('aaa')을 가리키고 있고
$a[1] 을('bbb') 잃어버린것 처럼 보이는데요.
익명 사용자의 이미지

@a 어레이를 \(@_) 로 초기화 했기 때문에 $a[0], $a[1] ... 값들은 포인터가되요.
만약에 A, B, C, D, E 5 개의 item 을 전달했다면 ${$a[0]} 를 해야 "A" 가 나오는거에요.
(${$a[$n]}, $a[$n] = $a[$i])[0]; 이 문장은 (...)[0] 에의해 반환값은 첫번째 원소인 ${$a[$n]} 가 되고,
$n = rand($i--) 함수에서 나온값이 2.xxx 라면 $n 값은 2 가 되므로 ${$a[2]} 값인 "C" 가 반환됩니다.
그다음 $a[2] = $a[$i] 에의해서 $a[2] 에는 다른값의 포인터가 설정됩니다.
따라서 다음에도 $n 값이 2 가 나오게되면 이번에는 "C" 가 아니라 다른값이 출력되겠죠.

doodoo의 이미지

답변감사합니다.
읽고도 이해가 잘 안되는...제 머리가 더더욱 나쁘다는걸 또한번 경험하게 되네요.ㅠㅠ
(${$a[$n]}, $a[$n] = $a[$i])[0] 문에서 $a[$n] = $a[$i] 이 먼저 실행되는겁니까?
아니면 ( list1, list2)[0] 이 먼저 수행되는 겁니까?

또한 map 문이 수행될때 @a 전체를 반환해야 하는것으로 이해했는데.
실제 실행을 해보니
${$a[$n]} 을 한번에 한번씩 총 네번 반환하는 것 같아서
my @a = shuffle(@ARGV); 문장이 수행되면 @a 에는 한개의 리스트가 들어가는게 되지 않나요?
근데 실제 프린트 해보면 다르다는....

sub shuffle(@) {
    my @a = \(@_);
    my $n;
    my $i = @_;
    map {
        $n = rand($i--);
        print '\@a ' . Dumper(\@a);
        print '$n '. $n .' $i '. $i . ' ${$a[$n]} ' . Dumper(${$a[$n]});
        (${$a[$n]}, $a[$n] = $a[$i])[0];
    } @_;
}

doosoon@DESKTOP-32HGO6D ~ $ ./s.pl V1 V2 V3 V4
\@a $VAR1 = [
          \'V1',
          \'V2',
          \'V3',
          \'V4'
        ];
$n 3.33734999440277 $i 3 ${$a[$n]} $VAR1 = 'V4';
\@a $VAR1 = [
          \'V1',
          \'V2',
          \'V3',
          \'V4'
        ];
$n 1.69977569630501 $i 2 ${$a[$n]} $VAR1 = 'V2';
\@a $VAR1 = [
          \'V1',
          \'V3',
          $VAR1->[1],
          \'V4'
        ];
$n 1.18841278141578 $i 1 ${$a[$n]} $VAR1 = 'V3';
\@a $VAR1 = [
          \'V1',
          \'V3',
          $VAR1->[1],
          \'V4'
        ];
$n 0.566870889716434 $i 0 ${$a[$n]} $VAR1 = 'V1';
Play.. V4
Play.. V2
Play.. V3
Play.. V1

슬슬 머리가 아파오는....ㅠ
raymundo의 이미지

셔플을 저렇게 구현했어야만 했나...라는 생각이 듭니다만 신기하긴 하네요.

일단 중간에 쓰신 @a 원소 여러 개가 같은 값을 갖게 되는 건 문제가 안 됩니다.

처음에 원소가 4개라면 (a b c d)
그 중에 하나 랜덤하게 골라서($n = 1 이라면 b) b를 제일 앞에 두고, 제일 끝의 d를 그 자리에 복사(a d c d)
그 다음은 처음 3개 중에서만 하나 고르고($n = 0 이라면 a) a를 그 다음에 두고, 3개 중 마지막 c를 그 자리에 복사(c d c d)

이런 식이니 @a 에 중복 원소가 생기는 건 아무 상관이 없고요.

마지막에 쓰신 map은 한번에 리스트를 반환하는 것 맞습니다. @a 의 원소를 하나씩 블록 안에 넣어서 실행한 결과를 차례대로 새 리스트에 담아서 반환하죠.

그 다음 실행 순서 얘기인데,

(${$a[$n]}, $a[$n] = $a[$i])[0] 문에서 $a[$n] = $a[$i] 이 먼저 실행되는겁니까?

리스트 컨텍스트에서 쉼표는 좌에서 우로 평가한다고 되어 있습니다.
https://perldoc.perl.org/perlop#Comma-Operator

제가 예로 든 첫번째 줄의 경우라면

# $n=1, $i는 4에서 시작해서 -- 때문에 3이 되었음
 
(${$a[$n]}, $a[$n] = $a[$i])[0]
 ^^^^^^^^^ 여기를 먼저 평가
 ${$a[1] }
 ${\'b'}
   b
            ^^^^^^^^^^^^^^^ 그 다음 여기를 평가
            $a[1] = $a[3]   이제 $a[1]은 'd'의 레퍼런스
 
( b       , \'d'          )    원소 두 개짜리 리스트가 만들어졌고
                           [0] 그 중 첫번째 원소
=> b 

위와 같이 진행되었겠네요.

그런데 뭐가 문제냐하면, "왜 굳이 레퍼런스를 썼을까, 그냥 값을 복사해도 되었을 텐데" 싶어서 고쳤는데, 그건 예상대로 동작하지가 않더라고요.

sub shuffle(@) {
    # my @a = \(@_);
    my @a = (@_);    # 원소의 레퍼런스가 아니라 값을 복사
    my $n;
    my $i = @_;
    map {
        $n = rand($i--);
        # (${$a[$n]}, $a[$n] = $a[$i])[0];
        ($a[$n], $a[$n] = $a[$i])[0];     # 디레퍼런스하지 않고 $a[$n]을 그냥 사용
    } @_;
}

위 코드는 결과물에 중복된 값이 있더라고요. 어떤 값은 누락되었고.

그래서 간단한 형태로 다시 테스트했는데

  my $v = 1;
  my $n = ( $v, $v = 2 )[0];
  say "n = $n";  # ?????
  say "v = $v";  # 2

여기서 n이 당연히 1일 거라고 생각했는데 2가 나오더군요. 이 부분은 다시 좀 더 알아봐야겠습니다.

좋은 하루 되세요!

doodoo의 이미지

처음에 원소가 4개라면 (a b c d)
그 중에 하나 랜덤하게 골라서($n = 1 이라면 b) b를 제일 앞에 두고, 제일 끝의 d를 그 자리에 복사(a d c d)
그 다음은 처음 3개 중에서만 하나 고르고($n = 0 이라면 a) a를 그 다음에 두고, 3개 중 마지막 c를 그 자리에 복사(c d c d)
 
이런 식이니 @a 에 중복 원소가 생기는 건 아무 상관이 없고요.

여기서 b를 제일 앞에두고... 라는게 어떤 의미인지 잘 모르겠습니다.
my @a = \(@_); 이 문장이 끝날때
@a = (\'a', \'b', \'c', \'d') 아니였나요? 그런데...앞에둔다는게 어떤 뜻인지?

바보같이 질문만 계속하게 되네요...

혹시 펄 디버거 잘 쓰시는 분 있으실까요?

raymundo의 이미지

아, 실제 코드 얘기가 아니라 저 셔플 함수가 논리적으로 그런 식으로 동작한다는 말이었습니다.

                                    @a                map 내부에서 생성하는 반환값 리스트
                                    \a \b \c \d
처음 4개 중 랜덤으로 하나 고름(b)                     b  (이걸 b를 제일 앞에 둔다고 표현했음)
4번째 원소를 방금 고른 자리에 복사  \a \d \c \d
처음 3개 중 랜덤으로 하나 고름(a)                     b a
3번째 원소를 방금 고른 자리에 복사  \c \d \c \d
처음 2개 중 랜덤으로 하나 고름(c)                     b a c
2번째 원소를 방금 고른 자리에 복사  \d \d \c \d
처음 1개 중 랜덤으로 고름(d)                          b a c d
1번째 원소를 방금 고른 자리에 복사  \d \d \c \d

이런 식이니까, 실행 도중에 @a의 내부를 들여다봤을 때 중복된 값이 있는 건 아무 문제가 없다는 말이었습니다.

좋은 하루 되세요!

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.