17

私はperlを学んでおり、shiftを使用してサブルーチン引数を解凍することが一般的で受け入れられている方法であることを理解しています。@_また、デフォルトの配列を使用するために関数の引数を省略することが一般的で許容できる方法であることも理解しています。

これらの2つのことを考慮すると、引数なしでサブルーチンを呼び出すと、@_変更できます(Shiftを使用している場合は変更されます)。これは、デフォルトの引数を使用して別のサブルーチンを呼び出すこと、または実際には、@_この後に配列を使用することは、悪い習慣と見なされることを意味しますか?この例を考えてみましょう。

sub total { # calculate sum of all arguments
    my $running_sum;
    # take arguments one by one and sum them together
    while (@_) {
       $running_sum += shift;
    }
    $running_sum;
}

sub avg { calculate the mean of given arguments
    if (@_ == 0) { return }
    my $sum = &total; # gets the correct answer, but changes @_
    $sum / @_ # causes division by zero, since @_ is now empty
}

私の直感では、サブルーチンが実際に渡された引数を変更することになっていない限り、shiftを使用して引数を解凍することは実際には悪い習慣ですが、Stack Overflowを含む複数の場所で、これは悪い習慣ではないことを読みました。

したがって、問題は、シフトの使用が一般的な方法である場合、サブルーチンの副作用として(&total引用された例のサブルーチンのように)、渡された引数リストが変更される可能性があると常に想定する必要がありますか?引数を値で渡す方法があるので、引数リストが変更されないことを確認できるので、(&avg引用符で囲まれたテキストのサブルーチンのように)再度使用できますか?

4

7 に答える 7

20

一般にshift、引数からの ing&は問題ありません。関数を呼び出すために sigil を使用することは問題です。(おそらく遭遇することのない非常に特殊な状況を除きます。)

あなたのコードは書き直される可能性があるtotalのでshift@_. for ループを使用すると、より効率的になる場合もあります。

sub total {
  my $total = 0;
   $total += $_ for @_;
  $total;
}

sumまたは、次の関数を使用できますList::Util

use List::Util qw(sum);

sub avg { @_ ? sum(@_) / @_ : 0 }

オブジェクト指向 Perlshiftでの抽出を除いて、使用はそれほど一般的ではありません。$selfしかし、常にのように関数を呼び出すので、引数配列が sであるかどうかは問題foo( ... )ではありません。 (関数について注意する価値があるのは、 の要素に代入するかどうかだけです。これは、引数として指定した変数のエイリアスであるためです。 の要素に代入することは通常悪いことです。)foo shiftshift
@_@_

の実装を変更できない場合でもtotal、引数リストは配列のコピーであるため、明示的な引数リストを使用してサブルーチンを呼び出すことは安全です。

(a) —同一の を&total呼び出し、プロトタイプをオーバーライドします。 (b) —のコピーを呼び出します。 (c) —のコピーで呼び出し、プロトタイプをオーバーライドします。total@_
total(@_)total@_
&total(@_)total@_

フォーム (b) が標準です。フォーム (c) は、サブがプロトタイプを持っている (そしてプロトタイプを使用しない) 同じパッケージ内のサブの非常にまれなケースを除いて、見るべきではありません。貧弱なデザインの証。形式 (a) は、テール コール ( ) またはその他の形式の最適化に対してのみ有効です (時期尚早の最適化はすべての悪の根源です)。@_ = (...); goto &foo

于 2012-12-31T13:01:03.627 に答える
3

本当に正当な理由がない限り、このような呼び方は避けてください&func;。他の人も同じことをしていると信じてください。

呼び出し先による変更を防ぐには、 or@_を実行するだけです。&func()func

于 2012-12-31T18:26:04.727 に答える
2

Perl は少し緩すぎる場合があり、入力パラメーターにアクセスする方法が複数あると、臭くて一貫性のないコードになる可能性があります。より良い答えが必要な場合は、独自の基準を押し付けてみてください。

ここに私が使用して見たいくつかの方法があります

シフティ

sub login
{
    my $user = shift;
    my $passphrase = shift;
    # Validate authentication    
    return 0;
}

拡大する@_

sub login
{
    my ($user, $passphrase) = @_;
     # Validate authentication   
    return 0;
}

明示的なインデックス作成

sub login 
{
    my user = $_[0];
    my user = $_[1];
    # Validate authentication
    return 0;
}

関数プロトタイプでパラメーターを強制します (ただし、これは一般的ではありません)。

sub login($$)
{
    my ($user, $passphrase) = @_;   
    # Validate authentication
    return 0;
}

残念ながら、独自の複雑な入力検証/汚染チェックを実行する必要があります。つまり、次のようになります。

return unless defined $user;
return unless defined $passphrase;

またはさらに良いことに、もう少し有益です

unless (defined($user) && defined($passphrase)) {
    carp "Input error: user or passphrase not defined";
    return -1;
}

Perldoc perlsubは、本当に最初の寄港地であるべきです。

お役に立てれば!

于 2012-12-31T11:13:45.810 に答える
2

慎重に使用することが重要な例をいくつか示し@_ます。

1. Hash-y 引数

キーと値のペアのリストを取得できる関数を作成したい場合がありますが、1 つが最も一般的な用途であり、キーを必要とせずに利用できるようにしたい場合があります。例えば

sub get_temp {
  my $location = @_ % 2 ? shift : undef;
  my %options = @_;
  $location ||= $options{location};
  ...
}

したがって、奇数の引数で関数を呼び出す場合、最初は場所です。これによりget_temp('Chicago')、 またはget_temp('New York', unit => 'C')または さえも可能になりますget_temp( unit => 'K', location => 'Nome, Ak')。これは、ユーザーにとってより便利な API かもしれません。奇数引数をシフトすることにより、偶数@_リストになり、ハッシュに割り当てることができます。

2.発送

メソッドを名前でディスパッチできるようにしたいクラスがあるとしましょう (おそらく AUTOLOAD が便利かもしれません。手でロールします)。おそらく、これは引数がメソッドであるコマンド ライン スクリプトです。この場合、「クリーン」と「ダーティ」の 2 つのディスパッチ メソッドを定義します。フラグを指定して呼び出すと-c、クリーンなフラグが取得されます。これらのメソッドは、名前でメソッドを見つけて呼び出します。違いはやり方です。汚れたものはスタック トレースに残ります。クリーンなものはより包丁である必要がありますが、スタック トレースに含まれずにディスパッチされます。deathその痕跡を私たちに与えるメソッドを作ります。

#!/usr/bin/env perl

use strict;
use warnings;

package Unusual;

use Carp;

sub new { 
  my $class = shift;
  return bless { @_ }, $class;
}

sub dispatch_dirty { 
  my $self = shift;
  my $name = shift;
  my $method = $self->can($name) or confess "No method named $name";
  $self->$method(@_);
}

sub dispatch_clean { 
  my $self = shift;
  my $name = shift;
  my $method = $self->can($name) or confess "No method named $name";
  unshift @_, $self;
  goto $method;
}

sub death { 
  my ($self, $message) = @_;
  $message ||= 'died';
  confess "$self->{name}: $message";
}

package main;

use Getopt::Long;
GetOptions 
  'clean'  => \my $clean,
  'name=s' => \(my $name = 'Robot');

my $obj = Unusual->new(name => $name);
if ($clean) {
  $obj->dispatch_clean(@ARGV);
} else {
  $obj->dispatch_dirty(@ARGV);
}

だから今./test.pl、死のメソッドを呼び出すために呼び出すと

$ ./test.pl death Goodbye
Robot: Goodbye at ./test.pl line 32
    Unusual::death('Unusual=HASH(0xa0f7188)', 'Goodbye') called at ./test.pl line 19
    Unusual::dispatch_dirty('Unusual=HASH(0xa0f7188)', 'death', 'Goodbye') called at ./test.pl line 46

dispatch_dirtyしかし、トレースでわかります。代わりに呼び出す場合./test.pl -cは、クリーンなディスパッチャーを使用して取得します

$ ./test.pl -c death Adios
Robot: Adios at ./test.pl line 33
    Unusual::death('Unusual=HASH(0x9427188)', 'Adios') called at ./test.pl line 44

ここで重要なのはgoto(邪悪な goto ではなく) サブルーチン参照を取得し、現在の を使用して実行をその参照に即座に切り替えること@_です。これがunshift @_, $self、呼び出し元が新しいメソッドの準備ができるようにする必要がある理由です。

于 2012-12-31T15:59:45.210 に答える
0

私はhttp://perldoc.perl.org/perlsub.htmlで次の宝石を見つけました:

「はい、@ _の可視性に関係する未解決の問題がまだあります。私は今のところその質問を無視しています。(しかし、@ _を字句スコープにすると、それらの匿名サブルーチンはクロージャのように機能する可能性があることに注意してください...(ねえ、これは少しLispishに聞こえますか?(気にしないでください。)))」

あなたはそれらの問題の1つに遭遇したかもしれません:-(

OTOHアモンはおそらく正しい->+1

于 2012-12-31T13:17:57.630 に答える
0

簡単な例を試しました:

#!/usr/bin/perl

use strict;

sub total {

    my $sum = 0;
    while(@_) {
        $sum = $sum + shift;
    }
    return $sum;
 }

sub total1 {

my ($a, $aa, $aaa) = @_;

return ($a + $aa + $aaa);
}

my $s;
$s = total(10, 20, 30);
print $s;
$s = total1(10, 20, 30);
print "\n$s";

両方の print ステートメントの答えは 60 でした。

しかし、個人的には、議論は次のように受け入れられるべきだと感じています。

 my (arguments, @garb) = @_;

後であらゆる種類の問題を回避するために。

于 2012-12-31T13:02:56.230 に答える
0

参照:

sub refWay{
    my ($refToArray,$secondParam,$thirdParam) = @_;
    #work here
}

refWay(\@array, 'a','b');

ハッシュウェイ:

sub hashWay{
   my $refToHash = shift; #(if pass ref to hash)
   #and i know, that:
   return undef unless exists $refToHash->{'user'};
   return undef unless exists $refToHash->{'password'};   

   #or the same in loop:
   for (qw(user password etc)){
       return undef unless exists $refToHash->{$_};
   }
}

hashWay({'user'=>YourName, 'password'=>YourPassword});
于 2012-12-31T12:20:01.390 に答える