6

これは、Perl の動作方法と、このようなことでどこまで行けるかについてのオタクの好奇心からだけではないかと思っています。

3 つのコンテキストのそれぞれで異なる動作をするように記述された関数がいくつかあります。

非常に単純な例として、次のコードを使用します。

use 5.012;

say context();
say scalar context();

sub context {
    if (wantarray) {
        return 'list';
    } elsif (defined wantarray) {
        return 'scalar';
    } else {
        return 'void'; # Destined to be discarded
    }
}

出力:

list
scalar

が呼び出された後sayに出力する 3 分の 1 を誘発する方法を考えられますか?voidcontext()

無効なコンテキストはおそらく実際には何も返さない/割り当てていないことを意味するため、これはかなりの矛盾であることを理解しています。しかし、Perl の動作について読んだことから理解できるように、何も返されないということではなく、void コンテキストで実行された後に戻り値が破棄されるということです。

だから、私は疑問に思っています:関数の呼び出しの瞬間に実際にリストまたはスカラーコンテキストにいるときに、リストまたはスカラーコンテキストを強制できるのと同じ方法で無効コンテキストを強制する方法はありますか?

4

3 に答える 3

10
sub void(&) { $_[0]->(); () }

say        context();
say scalar context();
say void { context() };

より高度なコードにより、より優れた構文が得られます。

use syntax qw( void );

say        context();
say scalar context();
say void   context();

余談ですが、以下はscalar、コンパイル時のディレクティブほど関数ではないことを示しています。

$ diff -u0 \
   <( perl -MO=Concise,-exec -Msyntax=void -E'say        f()' 2>&1 ) \
   <( perl -MO=Concise,-exec -Msyntax=void -E'say scalar f()' 2>&1 )
--- /dev/fd/63  2014-08-17 12:34:29.124827443 -0700
+++ /dev/fd/62  2014-08-17 12:34:29.128827401 -0700
@@ -7 +7 @@
-6  <1> entersub[t6] lKS/TARG    <-- "l" for list context
+6  <1> entersub[t7] sKS/TARG    <-- "s" for scalar context

同じことがuse syntax qw( void )'sにも当てはまりvoidます:

$ diff -u0 \
   <( perl -MO=Concise,-exec -Msyntax=void -E'say        f()' 2>&1 ) \
   <( perl -MO=Concise,-exec -Msyntax=void -E'say void   f()' 2>&1 )
--- /dev/fd/63  2014-08-17 12:34:41.952692723 -0700
+++ /dev/fd/62  2014-08-17 12:34:41.952692723 -0700
@@ -7 +7 @@
-6  <1> entersub[t6] lKS/TARG    <-- "l" for list context
+6  <1> entersub[t6] vKS/TARG    <-- "v" for void context

仕組みuse syntax qw( void );_

実際の作業はSyntax::Feature::VoidVoid.xsによって行われ、その主要な行は次のとおりです。

STATIC OP* parse_void(pTHX_ GV* namegv, SV* psobj, U32* flagsp) {
    return op_contextualize(parse_termexpr(0), G_VOID);
}

STATIC OP* ck_void(pTHX_ OP* o, GV* namegv, SV* ckobj) {
    return remove_sub_call(o);
}

BOOT: {
    const char voidname[] = "Syntax::Feature::Void::void";
    CV* const voidcv = get_cvn_flags(voidname, sizeof(voidname)-1, GV_ADD);
    cv_set_call_parser(voidcv, parse_void, &PL_sv_undef);
    cv_set_call_checker(voidcv, ck_void, &PL_sv_undef);
}
  1. voidを使用して sub を宣言しget_cvnます。(サブルーチンが定義されることはありません。) のコードはVoid.pm、サブルーチンを呼び出し元のレキシカル スコープにエクスポートします。

  2. voidを使用してユーザー定義の構文に従うように呼び出すことを Perl に伝えますcv_set_call_parser

  3. voidを使用してコンパイルした後、呼び出しを操作する必要があることを Perl に伝えますcv_set_call_checker

  4. Perl が の呼び出しに遭遇するとvoid、ユーザー定義パーサーは用語 using を抽出し、用語parse_termexprのコンテキストを using に変更しvoidますop_contextualize

  5. その後、チェッカーはvoid引数 (項) を残しながら、オペコード ツリーから呼び出しを削除します。

于 2014-08-17T15:54:59.410 に答える
2

関数の戻りコードが確実に使用されていないことを確認する必要があります。

context();
1;

もちろんreturn 'void'、 return ( !defined wantarray) として何も必要としない場合は意味がありません。この戻り値は使用されないためです。

于 2014-08-17T13:21:15.987 に答える
1

あなたが実際に求めていること

引用man perldata

use warningsプラグマまたは Perl の-wコマンドライン オプションを使用すると、「void コンテキスト」での定数または関数の無駄な使用に関する警告が表示される場合があります。"fred";無効なコンテキストは、またはのみを含むステートメントなど、値が破棄されたことを意味しますgetpwuid(0);。リストコンテキストで呼び出されているかどうかを気にする関数のスカラーコンテキストとしてカウントされます。

ユーザー定義のサブルーチンは、void、スカラー、またはリストのコンテキストで呼び出されているかどうかを気にすることを選択できます。ただし、ほとんどのサブルーチンは気にする必要はありません。これは、スカラーとリストの両方が自動的にリストに補間されるためです。関数の呼び出しコンテキストを動的に識別する方法については、wantarrayを参照してください。

したがって、呼び出しがリストまたはスカラーコンテキストで実行された場合、関数呼び出しの値を完全に破棄できるかどうかを尋ねています。

答えはイエスです!

スカラー コンテキストでは、リストの最後の要素のみが使用され、他の要素は void コンテキストで評価されます。さらに真実:「リスト」は実際にはリストではありませんでした。説明については、 man perlopのスカラー コンテキストでのコンマ演算子の動作の詳細を参照してください。また、 man perlfuncの説明セクションの最後の近くで別の言葉で説明されています。そして最後にperldoc -f scalarこれも言及します。

リストのコンテキストでは、そのような直接的な方法はありません。上記と同じトリックを使用して任意のスカラー (できれば 0) を取得し、リストの内容に影響を与えないようにそれを取り除く必要があります。空のリストの繰り返しが探しているものです。(ちなみに、繰り返し演算子は 2 番目のオペランドをスカラー コンテキストで評価します。)

sub test_context() {
    wantarray and die "list\n";
    defined wantarray and die "scalar\n";
    die "void\n";
}
$\ = "\n"; # to make output prettier
### Uncomment the one you want to test.
# -- Somewhat canonical examples of contexts
#[test_context]; # list (+ warning of class 'void')
#print test_context; # list
#scalar(test_context); # scalar (forces scalar context anywhere)
#my $x = test_context; # scalar
#test_context; # void
#
# -- Examples of forcing void context
# Replace test_context with a fixed scalar and try again to see that even if
# the function returned a value, it would get discarded. Ignore the 'void' warning.
#print my $x = (test_context, 42);
#print '^', () x (test_context, 0), '$';

警告: いいえvoid()

voidのような使い方の関数を作成することはできませんscalar

sub void {
    ();
}
print void(test_context);

これは、 prototypetestで特に指定しない限り、関数パラメーターは常にリスト コンテキストで評価されるため、リスト コンテキストで呼び出されることになります。また、プロトタイプは無効なコンテキストを強制できません。

このようなことは、Perl の構文を変更することによってのみ実装できます。これは可能ですが、非常に複雑です。

デフォルトの Perl 構文で得られる最良の概算は、ikegami's answerに示されています。

どうしてそんなものを欲しがるの?

この質問は、純粋な怠惰な好奇心と、おそらく Perl のコンテキストをよりよく理解したいという欲求から生じたものだと思います。私の意見では、それは実際にはまったく役に立ちません。の例がperldoc -f wantarray示すように、void コンテキストを表す未定義の戻り値は、計算を高速化するために使用されることを意図しています。

return unless defined wantarray; # don't bother doing more
my @a = complex_calculation();
return wantarray ? @a : "@a";
于 2014-08-17T15:14:25.480 に答える