(질문)perl 속의 map 함수.
글쓴이: doodoo / 작성시간: 수, 2021/01/20 - 7:14오후
생뚱맞게 간만에 와서 또 질문하나만 하게 됩니다.
인터넷에서 찿아낸 파일 썪어주는 함수 입니다.
-- 애들 뽀로로 치로 같은것 틀어 줄때 재생순서를 썪어놓기 위해 찿은건데요.
근데...이해가 안되서 ㅠㅠ
#!/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] ];
등으로 나와서 더더욱 헤메게 되네요....
혹시 해석좀 해 주실분 계시면 미리 감사드립니다.
Forums:
rand() 함수에서 나온 $n 값은 array
rand() 함수에서 나온 $n 값은 array 인덱스로 사용될때 정수값만 사용되요.
따라서 인덱스값이 중복될수가 있기때문에 같은 인덱스가 나왔을때 동일한 값이
출력되지 않게 미리 $a[$n] = $a[$i] 로 설정해주는 것이에요
같은 인덱스값이 될수 있다는 것은 이해 합니다.
같은 인덱스값이 될수 있다는 것은 이해 합니다.
그것을 회피하기 위해 $a[$n] = $a[$i] 로 한다는것이 이해가 되지 않아서요.
이미 위에 적은것 처럼
몇번 map 루프를 돌다보면 $a[1], $a[3] 이 같은 값('aaa')을 가리키고 있고
$a[1] 을('bbb') 잃어버린것 처럼 보이는데요.
@a 어레이를 \(@_) 로 초기화 했기 때문에 $a
@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" 가 아니라 다른값이 출력되겠죠.
답변감사합니다.
답변감사합니다.
읽고도 이해가 잘 안되는...제 머리가 더더욱 나쁘다는걸 또한번 경험하게 되네요.ㅠㅠ
(${$a[$n]}, $a[$n] = $a[$i])[0] 문에서 $a[$n] = $a[$i] 이 먼저 실행되는겁니까?
아니면 ( list1, list2)[0] 이 먼저 수행되는 겁니까?
또한 map 문이 수행될때 @a 전체를 반환해야 하는것으로 이해했는데.
실제 실행을 해보니
${$a[$n]} 을 한번에 한번씩 총 네번 반환하는 것 같아서
my @a = shuffle(@ARGV); 문장이 수행되면 @a 에는 한개의 리스트가 들어가는게 되지 않나요?
근데 실제 프린트 해보면 다르다는....
슬슬 머리가 아파오는....ㅠ
셔플을 저렇게 구현했어야만 했나...라는 생각이
셔플을 저렇게 구현했어야만 했나...라는 생각이 듭니다만 신기하긴 하네요.
일단 중간에 쓰신 @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 의 원소를 하나씩 블록 안에 넣어서 실행한 결과를 차례대로 새 리스트에 담아서 반환하죠.
그 다음 실행 순서 얘기인데,
리스트 컨텍스트에서 쉼표는 좌에서 우로 평가한다고 되어 있습니다.
https://perldoc.perl.org/perlop#Comma-Operator
제가 예로 든 첫번째 줄의 경우라면
위와 같이 진행되었겠네요.
그런데 뭐가 문제냐하면, "왜 굳이 레퍼런스를 썼을까, 그냥 값을 복사해도 되었을 텐데" 싶어서 고쳤는데, 그건 예상대로 동작하지가 않더라고요.
위 코드는 결과물에 중복된 값이 있더라고요. 어떤 값은 누락되었고.
그래서 간단한 형태로 다시 테스트했는데
여기서 n이 당연히 1일 거라고 생각했는데 2가 나오더군요. 이 부분은 다시 좀 더 알아봐야겠습니다.
좋은 하루 되세요!
처음에 원소가 4개라면 (a b c d)
여기서 b를 제일 앞에두고... 라는게 어떤 의미인지 잘 모르겠습니다.
my @a = \(@_); 이 문장이 끝날때
@a = (\'a', \'b', \'c', \'d') 아니였나요? 그런데...앞에둔다는게 어떤 뜻인지?
바보같이 질문만 계속하게 되네요...
혹시 펄 디버거 잘 쓰시는 분 있으실까요?
아, 실제 코드 얘기가 아니라 저 셔플 함수가
아, 실제 코드 얘기가 아니라 저 셔플 함수가 논리적으로 그런 식으로 동작한다는 말이었습니다.
이런 식이니까, 실행 도중에 @a의 내부를 들여다봤을 때 중복된 값이 있는 건 아무 문제가 없다는 말이었습니다.
좋은 하루 되세요!
댓글 달기