2009-02-25 5 views
9

커맨드 라인 인수에 의존하는 펄 함수 ("차이"라고 함)를 정의하고 싶습니다. 다음 코드는 작동하지 않습니다어떻게 Perl 서브 루틴을 조건부로 정의 할 수 있습니까?

if ("square" eq $ARGV[0]) {sub difference {return ($_[0] - $_[1]) ** 2}} 
elsif ("constant" eq $ARGV[0]) {sub difference {return 1}} 

이 조건이 무시 않은 것 같습니다, 따라서 "차이"기능은 $ ARGV [0]의 값에 관계없이 두 번째 정의를 가져옵니다.

sub difference { 
    if ("square" eq $ARGV[0]) {return ($_[0] - $_[1]) ** 2} 
    elsif ("constant" eq $ARGV[0]) {return 1} 
} 

을하지만 정말 내 의도가 아니다 - 나는 조건이 필요하지 않습니다 실행시마다 평가하기 :

나는 함수에 조건을 바꾸어 코드가 작동 할 수 있습니다. 함수의 정의에 영향을 줄 수있는 방법이 필요합니다.

내 질문은 :

  1. 왜 첫 번째 구조는 작동하지 않는 이유는 무엇입니까?
  2. 오류가 발생하지 않는 이유는 무엇입니까?
  3. 조건부로 Perl에서 함수를 정의 할 수있는 방법이 있습니까?
+0

당신이 그것을하는 동안, 당신은 A) 당신이 이것을하기 전에 잘못된 인수가있을 경우 사망하거나, B) 첫 번째 두 문장 중 어느 것도 작동하지 않을 때 세 번째 정의를 가지길 원할 수 있습니다. 전화를 걸면 차이가 생길 수 있습니다. –

+1

"엄격한 사용; 경고 'all';" 이들은 당신이 만나는 종류의 문제들입니다. – JDrago

답변

24

다른 사용자가 이미 요청한 구문을 제시했지만, 사용자가 정의를 조작하지 않고도 자유롭게 참조를 조작 할 수 있도록보다 명시적인 서브 루틴 참조를 사용하는 것이 좋습니다.

sub square_difference { return ($_[0] - $_[1]) ** 2 } 
sub constant_difference { return 1 } 

my %lookup = (
    'square' => \&square_difference, 
    'constant' => \&constant_difference, 
); 

my $difference = $lookup{$ARGV[0]} || die "USAGE: $0 square|constant\n"; 
print &$difference(4, 1), "\n"; 

그것은 동일한 기본 접근 방식,하지만이 구문을 사용하면보다 편리하게 각을 더 추가 할 때 약간의 서브 루틴에 인수를 매핑 할 것이라고 생각 예를 들면 다음과 같습니다. 당신이 그런 종류의 일을하는 경우 이것은 Strategy Pattern의 변형입니다.

+0

필자는 이것이 더 나은 (보다 견고한) 방법이라는 것에 동의한다. (비록 constant_difference를 기본값으로 사용하지 않고 죽는 것이 IMHO가 더 좋을지라도). –

+0

저는 Leon Timmermans와 동의합니다 : 디폴트로 가정하는 것은 매우 나쁠 수 있습니다. 구성 파일에서 기본값을 읽으려는 것이 좋지 않을 수도 있지만 그렇지 않으면 지정하지 않으면 죽을 것입니다. –

+0

나는 네가 명명 된 서브 루틴을 만들고 그 서브 루틴을 참조하기를 좋아하지만. 익명의 서브 루틴을 사용하는 것보다 안전합니다. 특히 나중에 덮어 쓰려는 경우 더욱 그렇습니다. –

11

나는 개인적으로이 많이하지 않은,하지만 당신은 서브 루틴 유지하기 위해 변수를 사용할 수 있습니다

my $difference; 
if ("square" eq $ARGV[0]) {$difference = sub {return ($_[0] - $_[1]) ** 2}} 
elsif ("constant" eq $ARGV[0]) {$difference = sub {return 1}} 

전화로 :

&{ $difference }(args); 

또는를 :

&$difference(args); 

또는 Leon Timmermans가 제안한대로 :

$difference->(args); 

설명의 비트 -이 $difference라는 변수를 선언하고, 당신의 조건에 따라, 익명 서브 루틴에 참조를 개최 설정합니다. 따라서 서브 루틴을 호출하기 위해서는 역 참조$difference을 서브 루틴 (따라서 앞에 &)으로 사용해야합니다.

편집 : 코드 테스트 및 작동됩니다.

하나 더 편집 :

예수님, 내가 그렇게 use 보내고 strict 그리고 나는 그들이 선택하고 있다는 사실조차 잊게 warnings 사용 해요.

하지만 진지하게. 항상 use strict;use warnings;입니다. 이렇게하면 이런 일을 포착하고 잘못된 점을 설명하는 유용한 오류 메시지를 얻을 수 있습니다. 내 인생에서 디버거를 사용하지 않았기 때문에 strictwarnings이 있습니다. 이는 오류 검사 메시지가 얼마나 좋은지를 보여줍니다. 그들은 이런 종류의 모든 것을 잡을 것이며 심지어 에 대한 유용한 메시지를 제공합니다.

그래서 제발, 당신은 상관없이 (이 난독 화하지 않는 이상) 얼마나 작은 무언가를 쓸 때마다, 항상 use strict;use warnings;. 이 같이 달성 될 수있다 수행 할 작업을

+0

더 나은 구문은 $ difference -> (args) –

+0

입니다.이 접근 방식이 가장 좋았습니다. 네이티브 참조 표기법을 사용하며 다른 해시 테이블을 필요로하지 않습니다. 사실이 접근법이나 해시 테이블 참조 방법을 사용하면 함수 사용자에게 마법을 숨길 수 있습니다. 예를 들어 하나의 추가 기능 레이어를 추가하면됩니다. 이름이 간단하고 쉬운 함수에서 $ difference -> (args)를 감싸십시오 : sub diff (args) { $ difference -> (args); } 이제 주 프로그램에서 화살표 (또는 대체) 표기법을 사용할 필요가 없습니다. – bearvarine

16

:

if ($ARGV[0] eq 'square') { 
    *difference = sub { return ($_[0] - $_[1]) ** 2 }; 
} 
elsif ($ARGV[0] eq 'constant') { 
    *difference = sub { return 1 }; 
} 
+0

타입 글로브를 사용하는 것은 약간 힘든 일 아닌가요? 대신 코드 차이를 유지하기 위해 $ 차이를 사용할 수 있습니다. 당신은 $ difference (arg1, arg2)를 사용하여 그것을 호출 할 것이다. –

+1

typeglob이이를 수행하는 방법입니다. BEGIN 블록에 넣을 것입니다. (실제로, 나는 "문제"가 braindead이고, 이런 식으로 조건 적으로 컴파일하지 않을 것이라고 생각한다.) 그러나 – jrockway

+0

@jrockway - 왜 이것을 BEGIN 블록에 넣을 까? 어떤 특별한 이유로 그것이 필요한가, 아니면 단지 당신의 개인적인 스타일인가? –

6

잠수정은 컴파일 타임에 정의되어 있습니다 -> 당신이 "사용 경고"을 사용했다면, 당신은 서브 루틴의 재정에 대한 오류 메시지를 본 것입니다.

+0

예.하지만 런타임에 서브 루틴을 정의 할 수있는 방법이 있습니다. 게시 된 두 답변을 참조하십시오. –

+0

흠, 제 편집이 잘못되었습니다 ... 런타임에 할당 할 수 있습니다 - 실제로 제안 된 것입니다. 그러나 본질적으로, subs는 컴파일 타임에 정의됩니다 ("eval"옵션은 무시합니다). – Mathew

+0

아.+1 "사용 경고". 나는 "strict"와 "warnings"이 선택적이라는 사실을 잊어 버리고있다. –

-2

또 다른 방법 : 코드 작업을 만드는 방법에 대한 모든 제안

my $diffmode; 
BEGIN { $diffmode = $ARGV[0] } 
sub difference { 
    if ($diffmode eq 'square') { ($_[0] - $_[1]) ** 2 } 
    elsif ($diffmode eq 'constant') { 1 } 
    else { "It don't make no never mind" } 
} 
+0

$ diffmode가 컴파일 타임에 알려지기 때문에 컴파일 타임에 최적화 될 수 있습니까? 아니면 그의 답변에있는 것과 똑같습니까? –

+0

아니요, 최적화되지 않았지만 변경되지 않은 전세계 체류에 의존하지 않습니다. – ysth

0

감사합니다. 완벽을 기하기 위해 제 질문에 대한 높은 대답을 드리겠습니다.

  1. 함수는 컴파일시 정의되지만 조건 및/또는 명령 줄 인수는 런타임에 평가되므로 첫 번째 구문은 작동하지 않습니다. 조건이 평가 될 때까지 명명 된 함수가 이미 정의되었습니다.

  2. 컴파일러는 "경고 사용"이라는 경고를 표시하지만 프로그래머가 1을 인식하지 못하는 경우에는 유용하지 않습니다 .--) 의미있는 경고를 내리는 것이 어려울 때 if 문 내부에서 함수를 정의하는 것이 의미가 있습니다 Leon Timmermans의 제안처럼 if 문에서 함수를 사용하여 작업을 수행하는 경우에도 마찬가지입니다. 원래 코드는 if 문으로 컴파일되고 컴파일러는 이에 대해 경고하지 않습니다.

  3. 엄밀히 말하면 함수를 조건부로 정의 할 수는 없지만 참조 (rbright) 또는 별칭 (Leon Timmermans)을 함수에 조건부로 정의 할 수 있습니다. 공감대는 별칭보다 참조가 더 나은 것처럼 보이지만 왜 나는 확실하지 않습니다.

약 1 참고 : 실제로 이와 같은 문제가 발생할 때까지는 평가 순서가 명확하지 않습니다. 컴파일 할 때마다 조건을 평가할 펄을 안전하게 구상 할 수있을 것입니다. 분명히 다음 코드는 재정의 서브 루틴에 대한 경고를하기 때문에 펄은 이것을하지 않습니다.

use warnings ; 
if (1) {sub jack {}} else {sub jack {}} 
+0

if (1)의 경우 문자열 조건부 * – ysth

+0

없이 함수를 조건부로 정의 할 수 없습니다. perl은 else를 최적화하지만 실행에만 영향을줍니다. 두 번째 하위 정의가 이미 발생했습니다 – ysth

+0

Leon의 코드가 실제로 별칭을 만들지 않습니다. 일반 선언과 마찬가지로 심볼 테이블에 명명 된 하위를 설치합니다. 참조는 일반적으로 선호되지만 예외가 있습니다. 예 : 필자는 빠른 (XS) 구현과 느린 (순수한 Perl) 구현에서 폴백 (fallback)을 선택하기 위해 typeglob을 사용했습니다. –

1

다른 답변은 코드 참조 또는 별칭을 사용하여 정확합니다. 그러나 앨리어스 예제는 yicky 타입 글롭 구문을 도입하고 엄격한 것을 잊어 버립니다.

Alias은 엄격하게 유지하면서 참조를 제공하는 데 필요한 모든 마법을 감쌀 수있는 잊혀진 모듈입니다.

use strict; 
use Alias; 

my $difference_method = $ARGV[0]; 
if("square" eq $difference_method) { 
    alias difference => sub { return ($_[0] - $_[1]) ** 2 }; 
} 
elsif("constant" eq $difference_method) { 
    alias difference => sub { return 1 }; 
} 
else { 
    die "Unknown difference method $difference_method"; 
} 

그리고 지금은 difference($a, $b)입니다.

자신의 코드 내에 difference()으로 전화하면됩니다. 당신은 함수로서 그것을 export하지 않을 것입니다, 나는 단지 코드 레퍼런스를 사용하고 aliasing을 잊어 버릴 것입니다.

my $difference_method = $ARGV[0]; 

my $Difference; 
if("square" eq $difference_method) { 
    $Difference => sub { return ($_[0] - $_[1]) ** 2 }; 
} 
elsif("constant" eq $difference_method) { 
    $Difference => sub { return 1 }; 
} 
else { 
    die "Unknown difference method $difference_method"; 
} 

$Difference->($a, $b); 

코드를 조건부로 변경하면 코드 범하기가 어려워지고 유연성이 떨어지며 모든 글로벌 동작이 변경됩니다. 당신은 문제가 있다면

my $Difference_Method = $ARGV[0]; 

sub difference { 
    if($Difference_Method eq 'square') { 
     return ($_[0] - $_[1]) ** 2; 
    } 
    elsif($Difference_Method eq 'constant') { 
     return 1; 
    } 
    else { 
     die "Unknown difference method $Difference_Method"; 
    } 
} 

양식의 서브 루틴을 할 때마다 ...

sub foo { 
    if($Global) { 
     ...do this... 
    } 
    else { 
     ...do that... 
    } 
} 

: 방금이 최적화 있다는 것을 깨닫게 때 더 분명해진다.

앨리어싱은 클로저를 사용하여 런타임에 유사한 함수를 생성하는 데 가장 유용합니다. &을 직접 붙여 넣기 만하면됩니다. 그러나 그것은 또 다른 질문입니다.

+0

조건부로 변경되는 기능에 문제가있는 이유는 무엇입니까? 내 프로그램은 데이터 요소 행렬에 대해 (큰) 함수를 계산하고 데이터의 특성에 따라 점 간의 차이를 처리하는 방법이 결정됩니다. 차이를 계산하는 (작은) 함수를 조작하는 것에 대해 명확하지 않은 것은 무엇입니까? – Ron

+1

전역 변수와 동일한 문제가 있습니다. 코드의 어떤 부분이라도 difference()를 호출 할 수 있습니다. 다음 사람은 차이() 변화를 인식하지 못할 수도 있습니다. 어휘 코드 ref $ 차이를 사용하면 효과를 코드 블록으로 제한하고 변경 될 수 있음을 분명히 알 수 있습니다. 좁은 범위는 단순한 코드를 의미합니다. – Schwern

관련 문제