2017-02-11 3 views
1

У меня есть список значений смешанных типов (строки и числа):Динамически выбрать оператор равенства в Perl

my @list = (123, 'foo', 34.5, 'bar', '67baz'); 

Для каждого элемента в этом списке, мне нужно сравнить с входом в равной степени случайного типа (строка или число), чтобы сделать что-то:

my $input = 345; 
foreach my $elem (@list) { 
    if ($input == $elem) { 
     # do something 
    } else { 
     # do something else 
    } 
} 

Сравнение (==) будет работать нормально, когда оба операнда имеют одинаковый тип (цифры в данном случае), но поднимет предупреждение и/или производить неправильный результат, когда типы являются несоответствиями (для этого фрагмента кода это может быть s tring vs. number, или оба могут быть строками, поэтому вместо этого нужно использовать eq).

Я хотел бы получить информацию о ваших предпочтительных способах решения этой проблемы сравнения. Или, может быть, есть модуль, который динамически выбирает (и выполняет) правильный тип сравнения, учитывая тип операндов.

Мне известны методы, такие как использование looks_like_number() от Scalar::Util, чтобы проверить, с какими значениями вы работаете, но подумал, что есть лучший подход, чтобы выяснить, как делать сравнения. Заранее спасибо.

+0

Вам нужно '345' для соответствия' 345.0' и '3.45e2'? Если нет, вам нужно просто использовать 'eq' все время. – Borodin

ответ

3

Во многих языках тип операндов определяет операцию. В Perl оператор определяет операцию, и операнды будут принудительно введены в правильный тип. Таким образом, Perl не рассматривает четыре сохраненных в виде строки, отличной от четырех, сохраненных как целое число; они оба четверо. И если у вас есть код, который пытается выяснить, является ли скаляр числом или строкой, у вас есть код ошибки [1].

Если вы настаиваете, то вам нужно придумать определение того, что вы считаете числом в отличие от строки. Если вы выберете «это число, если Perl может использовать его как число и строку в противном случае», то looks_like_number действительно поможет. Вы также можете выбрать решение, основанное на том, как значение хранится в скаляре, но тогда вы должны решить, что делать, когда скаляр имеет как целое число, так и строку в пределах [2].

С другой стороны, если вы пытаетесь написать общий код, то вы можете заставить функцию использовать функцию сравнения (например, sort).

sub find(&@) { 
    my $cb = shift; 
    for (@_) { 
     return 1 if $cb->(); 
    } 

    return 0; 
} 


if (find { $_ == $num_to_find } @nums) { 
    print("Found $num_to_find\n"); 
} else { 
    print("Didn't find $num_to_find\n"); 
} 

find { $_ eq $string_to_find } @strings 

find { $_->year == $year_to_find } @date_time_objects; 

  1. Вот почему smartmatch оператор считается багги и должен быть изменен, прежде чем она перестает быть экспериментальным.

  2. perl -le'open(my $fh, "<", "non-existent"); print 0+$!; print "".$!;'

-4

smartmatch operator позволит вам написать

no if $] >= 5.018, warnings => qw(experimental::smartmatch); 

my $input = 123; # also works with 34.5, 'foo', etc. 
foreach my $elem (@list) { 
    if ($elem ~~ $input) { 
     # do something 
    } else { 
     # do something else 
    } 
} 

Идея была взята из Perl 6, и был реализован в 5.10. Начиная с Perl 5.18, были дискуссии о том, как развивать функцию, чтобы лучше соответствовать Perl 5, поэтому интерфейс был понижен до «экспериментального» состояния, ожидающего изменений.

+2

Это довольно обманчиво. Они не пытаются превратить его в нечто лучшее; это глючит, и они пытаются это исправить. Хуже того, вы специально используете его для поведения, которое должно быть исправлено. Он не оставит экспериментальный статус без первого взлома кода. – ikegami

3

Вы можете использовать Sort::Naturally, который импортирует оператор ncmp, который проводит сравнение «Natural».

Для проверки равенства вы могли бы сделать это

use Sort::Naturally; 
if (ncmp($lval, $rval) == 0) { ... } 

Вот некоторые примеры того, как ncmp лечит некоторые пары.

ncmp(1, '1') == 0 
ncmp('a', 'a') == 0 
ncmp('a', 'b') == -1 
ncmp('a', 'A') == -1 
ncmp(2, 1)  == 1 
ncmp(1, 'a') == -1 
ncmp('a', 1) == 1 

В основном ...

0 равенство
1 означает lval менее, что rval
-1 означает lval больше чем rval

0

использования Perl eval строка, которая является своего рода программы mini-perl в вашей программе (вы можете сделать это легко на языках сценариев, Perl eval кроме того, гораздо мощнее, чем все остальное, но с большой силой приходит большая ответственность ...):

# get the var guiding the dynamic operator 
    my $issues_order_by_attribute= $ENV{ 'issues_order_by_attribute' } || 'prio' ; 
    my $operator = '<=>' ; # initialize a default operator 
    # use Scalar::Util looks_like_number method to decide for operator 
    $operator = 'cmp' unless (looks_like_number($issues_order_by_attribute)) ; 
    # use eval string within the code to interpolate the operator 
    # to sort the hash ref of hash refs by the set attribute to order by 
    foreach my $issue_id (
     eval 'sort {  ' . 
      '$hsr2->{$a}->{ $issues_order_by_attribute }' . $operator . '$hsr2->{$b}->{ $issues_order_by_attribute }' . 
      '} keys (%$hsr2)') { 
     # Action !!! 
     my $row = $hsr2->{ $issue_id } ; 
    } 
Смежные вопросы