Perlで、$ variableが定義されていて、長さがゼロ以外の文字列を含んでいるかどうかを簡潔に確認するにはどうすればよいですか?


83

私は現在、次のPerlを使用して、変数が定義され、テキストが含まれているかどうかを確認しています。defined「初期化されていない値」の警告を回避するために、最初に確認する必要があります。

if (defined $name && length $name > 0) {
    # do something with $name
}

これを書くためのより良い(おそらくより簡潔な)方法はありますか?

回答:


78

定義のチェックがよく見られるので、undef値を使用するための警告に対処する必要はありません(Perl 5.10では問題のある変数が表示されます)。

 Use of uninitialized value $name in ...

したがって、この警告を回避するために、人々はあらゆる種類のコードを思い付き、そのコードは、風船ガムやダクトテープではなく、ソリューションの重要な部分のように見え始めます。場合によっては、回避しようとしている警告を明示的にオフにして、何をしているかを示す方がよい場合があります。

 {
 no warnings 'uninitialized';

 if( length $name ) {
      ...
      }
 }

それ以外の場合は、データの代わりにある種のnull値を使用してください。5.10の定義された、またはオペレータ、あなたが与えることができますlength(定義されており、バックゼロの長さを与える)明示的な空の文字列を代わりに警告をトリガーする変数の:

 use 5.010;

 if( length( $name // '' ) ) {
      ...
      }

Perl 5.12ではlength、未定義の値でもundefinedが返されるため、少し簡単です。それは少しばかげているように見えるかもしれませんが、それは私が望んでいたかもしれない数学者を喜ばせます。それは警告を発行しません、それがこの質問が存在する理由です。

use 5.012;
use warnings;

my $name;

if( length $name ) { # no warning
    ...
    }

4
また、v5.12以降でlength undefは、警告して0を返す代わりに、undefを返します。ブールコンテキストでは、undefは0と同じようにfalseであるため、v5.12以降をターゲットにしている場合は、次のように記述できますif (length $name) { ... }
rjbs

24

mobruleが示すように、少し節約するために代わりに以下を使用できます。

if (defined $name && $name ne '') {
    # do something with $name
}

定義されたチェックを破棄して、さらに短いものを取得できます。例:

if ($name ne '') {
    # do something with $name
}

ただし、が$name定義されていない場合、ロジックフローは意図したとおりに機能しますが、使用しているwarnings(そして使用する必要がある)場合は、次の警告が表示されます。

文字列neでの初期化されていない値の使用

したがって、$name定義されていない可能性がある場合は、その警告を回避するために、何よりもまず定義を確認する必要があります。SinanÜnürが指摘しているように、Scalar :: MoreUtilsを使用すると、次のempty()方法で、箱から出してすぐにそれを実行するコードを取得できます(定義をチェックしてから長さがゼロをチェックします)。

use Scalar::MoreUtils qw(empty);
if(not empty($name)) {
    # do something with $name 
}

17

まず、length常に負でない数を返すので、

if ( length $name )

そして

if ( length $name > 0 )

同等です。

未定義の値を空の文字列に置き換えても問題が//=ない場合は、LHSが定義されていない限り、RHSをLHSに割り当てるPerl5.10の演算子を使用できます。

#!/usr/bin/perl

use feature qw( say );
use strict; use warnings;

my $name;

say 'nonempty' if length($name //= '');
say "'$name'";

初期化されていない変数に関する警告がないことに注意してください。 $name未定義の場合は空の文字列が割り当てられるため。

ただし、5.10のインストールに依存したくない場合は、Scalar :: MoreUtilsが提供する関数を使用してください。たとえば、上記は次のように書くことができます。

#!/usr/bin/perl

use strict; use warnings;

use Scalar::MoreUtils qw( define );

my $name;

print "nonempty\n" if length($name = define $name);
print "'$name'\n";

壊したくない場合は$name、を使用しますdefault


「// =」の言及の+1(それがシナンの答えになることをどうやって知ったのか:)
DVK

4
この場合、副作用としてデータが変更されるため、// =は使用しません。代わりに、少し短いを使用してくださいlength( $name // '' )
brian d foy 2009

@brian d'foy関数で何が行われているかによると思います。
シナンÜnür

+1 //and//=演算子は、おそらく存在する中で最も有用な特殊演算子です。
クリス・ルッツ

1
@rjbsが私の回答で指摘したように、v5.12以降lengthでは、数値ではないものを返すことができるようになりました(ただし、NaNではありません;)
brian d foy 2015

6

私は変数があるかどうかを気にしない場合にはundef、または等しい''、私は通常のようにそれを要約します:

$name = "" unless defined $name;
if($name ne '') {
  # do something with $name
}

Perl 5.10では、これを短縮し$name //= "";て、Sinanが投稿したものとまったく同じにすることができます。
クリス・ルッツ

そして、perl 5.10を持っていなくても、書くことができます$name ||= "";
RET

1
@RET:使用できません|| 文字列「0」を「」に置き換えるため、ここでは演算子。それが定義されているかどうかを確認する必要がありますが、真実ではありません。
brian d foy 2009

クリス、RET:うん、わかってる。私は、特にジェシカが違いに関係していなかった場合ことを示唆しようとしていたundef""、彼女はただ、他の1つを変更し、単一のテストを使用する必要があります。これは、投稿された他のソリューションの方がはるかに優れている一般的なケースでは機能しませんが、この特定のケースでは、きちんとしたコードになります。これを明確にするために、答えを言い換える必要がありますか?
Gaurav

1

あなたは言えた

 $name ne ""

の代わりに

 length $name > 0

7
これでも警告が表示されます。人々が最初に定義をチェックする理由は、「初期化されていない値」の警告を回避するためです。
brian d foy 2009

1

シンプルでエレガントな方法で繰り返し行うことが常に可能であるとは限りません。

多くのプロジェクトに複製される共通のコードがある場合は、いつも行うことを実行してください。

CPANを検索してください。誰かがすでにあなたのコードを持っている可能性があります。この問題については、Scalar :: MoreUtilsを見つけました。

CPANで気に入ったものが見つからない場合は、モジュールを作成し、コードをサブルーチンに入れます。

package My::String::Util;
use strict;
use warnings;
our @ISA = qw( Exporter );
our @EXPORT = ();
our @EXPORT_OK = qw( is_nonempty);

use Carp  qw(croak);

sub is_nonempty ($) {
    croak "is_nonempty() requires an argument" 
        unless @_ == 1;

    no warnings 'uninitialized';

    return( defined $_[0] and length $_[0] != 0 );
}

1;

=head1 BOILERPLATE POD

blah blah blah

=head3 is_nonempty

Returns true if the argument is defined and has non-zero length.    

More boilerplate POD.

=cut

次に、コードでそれを呼び出します。

use My::String::Util qw( is_nonempty );

if ( is_nonempty $name ) {
    # do something with $name
}

または、プロトタイプに反対し、追加の親に反対しない場合は、モジュール内のプロトタイプをスキップして、次のように呼び出しますis_nonempty($name)


2
これは、ハンマーを使ってハエを殺すようなものではありませんか?
Zoran Simic

4
@Zoranいいえ。このようにコードを因数分解すると、複雑な状態がさまざまな場所で複製されます。それは、ピンプリックを使って象を殺すようなものです。@daotoad:の使用を強調するために、回答を短くする必要があると思いますScalar::MoreUtils
シナンÜnür

@Zoran:Scalar :: MoreUtilsは、依存関係のない非常に軽量なモジュールです。そのセマンティクスもよく知られています。CPANにアレルギーがない限り、CPANの使用を避ける理由はあまりありません。
Adam Bellaire

1
@クリス・ルッツ、ええ、私はすべきではありません。しかし、プロトタイプは半壊しています。プロトタイプの施行を破る簡単な方法があります。たとえば、くだらないチュートリアルや古いチュートリアルでは、&関数を呼び出すときにsigilの使用を引き続き推奨しています。ですから、私はすべての作業をプロトタイプに頼らない傾向があります。エラーメッセージに「本当に意味がない限り、サブコールで&sigilの使用をやめる」を追加できると思います。
daotoad 2009

1
プロトタイプをperlコンパイラへのヒントとして考える方が簡単なので、何かを解析する方法を知っています。それらは引数を検証するためにそこにありません。彼らは人々の期待の点で壊れているかもしれませんが、非常に多くのことがあります。:)
brian d foy 2009

1

優れたライブラリType :: Tinyは、Perlコードに型チェックを組み込むためのフレームワークを提供します。ここで示すのは、氷山の最も薄い先端であり、Type :: Tinyを最も単純で手動の方法で使用しています。

詳細については、Type :: Tiny :: Manualを確認してください。

use Types::Common::String qw< NonEmptyStr >;

if ( NonEmptyStr->check($name) ) {
    # Do something here.
}

NonEmptyStr->($name);  # Throw an exception if validation fails

-2

どうですか

if (length ($name || '')) {
  # do something with $name
}

これは元のバージョンとまったく同じではありません。次の場合にもfalseが返されるためです。 $name数値0または文字列のが、'0'それ以外の場合はすべて同じように動作します。

perl 5.10(またはそれ以降)では、適切なアプローチは、代わりに定義済みまたは演算子を使用することです。

use feature ':5.10';
if (length ($name // '')) {
  # do something with $name
}

これは$name、trueかどうかではなく、定義されているかどうかに基づいて長さを取得する対象を決定するため、0 /'0'はこれらのケースを正しく処理しますが、多くの人が利用できるよりも新しいバージョンのperlが必要です。


2
なぜそれが壊れていると言うだけで壊れた解決策で先導するのですか?
brian d foy 2009

なぜなら、私も述べたように、5.10は「多くの人が利用できるよりも新しいバージョンのperl」だからです。YMMVですが、「これは99%のソリューションであり、使用できることはわかっていますが、使用できる可能性のある、使用できない可能性のある優れたソリューションがあります」は、「これが完璧なソリューションですが、おそらく可能です」よりも優れているように思えます。使用しないので、フォールバックとしておそらくうまくいく代替案があります。」
Dave Sherohman 2009

1
以前のperlでも、壊れたソリューションの代わりに実用的なソリューションを使用できます。
brian d foy 2009

-3
if($ name)
{{
    #sinceundefと ''はどちらもfalseと評価されます 
    #これは、文字列が定義されていて空でない場合にのみ機能するはずです...
    #$ name = "0"のような何かが間違っていると予想している場合を除きます。
    #noticeただし、$ name = "00"はfalseではありません
}

1
残念ながら、$ name = 0の場合、これはfalseになります。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.