Development Tip

Perl : if (목록의 요소)

yourdevel 2020. 11. 3. 19:22
반응형

Perl : if (목록의 요소)


목록에 요소가 있는지 찾고 있습니다.

Python에는 in키워드 가 있으며 다음과 같은 작업을 수행합니다.

if element in list:
    doTask

전체 목록을 수동으로 반복하지 않고도 Perl에 동등한 것이 있습니까?


최신 정보:

스마트 매치 기능 제품군은 현재 실험적입니다.

v5.10.0에서 추가되고 v5.10.1에서 대폭 수정 된 스마트 매치는 일반적인 불만 사항이었습니다. 유용한 방법은 여러 가지가 있지만 Perl의 사용자와 구현 자 모두에게 문제가되고 혼란 스럽습니다. 문제를 가장 잘 해결하는 방법에 대한 많은 제안이있었습니다. 스마트 매치는 거의 확실히 미래에 변경되거나 사라질 것임이 분명합니다. 현재 동작에 의존하지 않는 것이 좋습니다.

이제 파서가 ~~, 주어진 또는 언제를 볼 때 경고가 발행됩니다.




Perl v5.10이 필요하지 않은 경우 다음 예제 중 하나를 사용할 수 있습니다.

  • 스마트 일치~~ 연산자.

    if( $element ~~ @list ){ ... }
    if( $element ~~ [ 1, 2, 3 ] ){ ... }
    
  • given/when 구문을 사용할 수도 있습니다 . 내부적으로 스마트 매치 기능을 사용합니다.

    given( $element ){
       when( @list ){ ... }
    }
    
  • for루프를 "토픽 라이저"로 사용할 수도 있습니다 (설정 됨 $_).

    for( @elements ){
       when( @list ){ ... }
    }
    

Perl 5.12에서 나올 한 가지는 when. 더 좋아 if하고 unless.

given( $element ){
  ... when @list;
}

이전 버전의 Perl에서 실행할 수 있어야하는 경우 몇 가지 옵션이 있습니다.

  • List :: Util :: first 를 사용하면 벗어날 수 있다고 생각할 수 있지만 문제를 일으키는 몇 가지 가장자리 조건이 있습니다.

    이 예에서 우리가 0. 불행히도이 코드는 failure매번 인쇄 됩니다.

    use List::Util qw'first';
    my $element = 0;
    if( first { $element eq $_ } 0..9 ){
      print "success\n";
    } else {
      print "failure\n";
    }
    

    의 반환 값이 first정의 되었는지 확인할 수 있지만 실제로 일치 undef가 성공하기를 원하면 실패합니다 .

  • grep그러나 안전하게 사용할 수 있습니다 .

    if( grep { $element eq $_ } 0..9 ){ ... }
    

    grep스칼라 컨텍스트에서 호출 되므로 안전 합니다. 배열은 스칼라 컨텍스트에서 호출 될 때 요소 수를 반환합니다. 그래서 이것은 우리가에 매치를 시도하더라도 계속 작동 할 것 undef입니다.

  • 둘러싸는 for루프를 사용할 수 있습니다 . last성공적인 경기에서 루프를 종료 하려면을 호출하십시오 . 그렇지 않으면 코드를 두 번 이상 실행하게 될 수 있습니다.

    for( @array ){
      if( $element eq $_ ){
        ...
        last;
      }
    }
    
  • for조건에 루프를 넣을 수 있습니다 if...

    if(
      do{
        my $match = 0;
        for( @list ){
          if( $element eq $_ ){
            $match = 1;
            last;
          }
        }
        $match; # the return value of the do block
      }
    ){
      ...
    }
    
  • ... for앞에 루프 를 두는 것이 더 명확 할 수 있습니다 if.

    my $match = 0;
    for( @list ){
      if( $_ eq $element ){
        $match = 1;
        last;
      }
    }
    
    if( $match ){ ... }
    
  • 문자열에 대해서만 일치하는 경우 해시를 사용할 수도 있습니다. 이 경우 프로그램의 속도를 높일 수 있습니다 @list하고 당신에 대해 일치에 가고, %hash여러 번. 특히 @array변경되지 않으면 %hash한 번만 로드 하면됩니다.

    my %hash = map { $_, 1 } @array;
    if( $hash{ $element } ){ ... }
    
  • 자신 만의 서브 루틴을 만들 수도 있습니다. 이것은 프로토 타입 을 사용하는 것이 유용한 경우 중 하나입니다 .

    sub in(&@){
      local $_;
      my $code = shift;
      for( @_ ){ # sets $_
        if( $code->() ){
          return 1;
        }
      }
      return 0;
    }
    
    if( in { $element eq $_ } @list ){ ... }
    

if( $element ~~ @list ){
   do_task
}

~~ 는 "스마트 매치 연산자"이며, 단순히 멤버십 감지를 나열하는 것 이상을 수행합니다.


목록 :: Util :: first

$foo = first { ($_ && $_ eq "value" } @list;    # first defined value in @list

또는 수동 롤링 유형의 경우 :

my $is_in_list = 0;
foreach my $elem (@list) {
    if ($elem && $elem eq $value_to_find) {
        $is_in_list = 1;
        last;
    }
}
if ($is_in_list) {
   ...

매우 긴 목록에서는 약간 다른 버전이 다소 빠를 수 있습니다.

my $is_in_list = 0;
for (my $i = 0; i < scalar(@list); ++$i) {
    if ($list[i] && $list[i] eq $value_to_find) {
        $is_in_list = 1;
        last;
    }
}
if ($is_in_list) {
   ...

이 작업을 여러 번 수행하려는 경우 조회 시간에 대한 공간을 절충 할 수 있습니다.

#!/usr/bin/perl

use strict; use warnings;

my @array = qw( one ten twenty one );
my %lookup = map { $_ => undef } @array;

for my $element ( qw( one two three ) ) {
    if ( exists $lookup{ $element }) {
        print "$element\n";
    }
}

요소가 나타나는 횟수 @array가 중요하지 않고의 내용 @array이 단순 스칼라 라고 가정합니다 .


목록 :: MoreUtils

perl> = 5.10에서는 다른 많은 사람들이 이미 말했듯이 스마트 매치 연산자가 확실히 가장 쉬운 방법입니다.

이전 버전의 perl에서는 대신 List :: MoreUtils :: any 를 제안 합니다 .

List::MoreUtils 핵심 모듈은 아니지만 (일부는 그래야한다고 말함) 매우 인기가 있으며 주요 펄 배포판에 포함되어 있습니다.

다음과 같은 장점이 있습니다.

  • (파이썬 에서처럼) 참 / 거짓을 반환 in하고 요소의 값은 반환 List::Util::first하지 않습니다 (위에서 언급 한 것처럼 테스트하기 어렵게 만듭니다).
  • 와 달리 grep테스트를 통과 한 첫 번째 요소에서 중지됩니다 (perl의 스마트 매치 오퍼레이터 단락 도 마찬가지입니다).
  • 모든 펄 버전에서 작동합니다 (적어도> = 5.00503).

다음을 포함하여 검색된 (스칼라) 값으로 작동하는 예는 다음과 같습니다 undef.

use List::MoreUtils qw(any);

my $value = 'test'; # or any other scalar
my @array = (1, 2, undef, 'test', 5, 6);

no warnings 'uninitialized';

if ( any { $_ eq $value } @array ) {
    print "$value present\n"
}

추신

(프로덕션 코드에서는 범위를 좁히는 것이 좋습니다 no warnings 'uninitialized').


TIMTOWTDI

sub is (&@) {
  my $test = shift;
  $test->() and return 1 for @_;
  0
}

sub in (@) {@_}

if( is {$_ eq "a"} in qw(d c b a) ) {
  print "Welcome in perl!\n";
}

grep 여기에 도움이된다

if (grep { $_ eq $element } @list) {
    ....
}

Probably Perl6::Junction is the clearest way to do. No XS dependencies, no mess and no new perl version required.

use Perl6::Junction qw/ any /;

if (any(@grant) eq 'su') {
    ...
}

This blog post discusses the best answers to this question.

As a short summary, if you can install CPAN modules then the best solutions are:

if any(@ingredients) eq 'flour';

or

if @ingredients->contains('flour');

However, a more usual idiom is:

if @any { $_ eq 'flour' } @ingredients

which i find less clear.

But please don't use the first() function! It doesn't express the intent of your code at all. Don't use the "Smart match" operator: it is broken. And don't use grep() nor the solution with a hash: they iterate through the whole list. While any() will stop as soon as it finds your value.

Check out the blog post for more details.

PS: i'm answering for people who will have the same question in the future.


Autoload 해킹 을 수행하면 Perl에서 유사한 구문을 수행 할 수 있습니다 .

자동로드를 처리 할 작은 패키지를 만듭니다.

package Autoloader;
use strict;
use warnings;

our $AUTOLOAD;

sub AUTOLOAD {
    my $self     = shift;
    my ($method) = (split(/::/, $AUTOLOAD))[-1];

    die "Object does not contain method '$method'" if not ref $self->{$method} eq 'CODE';

    goto &{$self->{$method}};
}

1;

그런 다음 다른 패키지 또는 기본 스크립트에는 메서드 호출을 시도 할 때 Autoload에 의해 처리되는 축복받은 객체를 반환하는 서브 루틴이 포함됩니다.

sub element {
    my $elem = shift;

    my $sub = {
        in => sub {
            return if not $_[0];

            # you could also implement this as any of the other suggested grep/first/any solutions already posted.
            my %hash; @hash{@_} = ();
            return (exists $hash{$elem}) ? 1 : ();
        }
    };

    bless($sub, 'Autoloader');
}

이것은 다음과 같은 사용법을 남깁니다.

doTask if element('something')->in(@array);

클로저와 그 인수를 재구성하는 경우 구문을 다른 방식으로 전환하여 다음과 같이 보이게 할 수 있습니다. 이는 autobox 스타일에 조금 더 가깝습니다.

doTask if search(@array)->contains('something');

이를 수행하는 기능 :

sub search {
    my @arr = @_;

    my $sub = {
        contains => sub {
            my $elem = shift or return;
            my %hash; @hash{@arr} = ();
            return (exists $hash{$elem}) ? 1 : ();
        }
    };

    bless($sub, 'Autoloader');
}

참고 URL : https://stackoverflow.com/questions/2383505/perl-if-element-in-list

반응형