Как можно написать условное выражение, основанное на определенности нескольких переменных? - PullRequest
7 голосов
/ 18 июня 2020

Время от времени я обнаруживаю, что мне нужно писать условные выражения с двумя или тремя ветвями: как минимум, одна для случаев, когда определены все две или более переменных, и одна для случаев, когда одна из них или ни одна из них не определены. Иногда эта последняя ветвь должна быть двумя отдельными ветвями. Какие короткие и приятные способы написать это?

Ответы [ 3 ]

10 голосов
/ 18 июня 2020

with, without и orwith - это условные выражения, которые работают аналогично if, unless и elsif, за исключением того, что они проверяют определенность своего аргумента, а не его истинность. Соединения становятся многопоточными при использовании в сочетании с ними, поэтому подобное условие можно записать так:

my Str $foo = '' if 2.rand.floor;
my Str $bar = '' if 2.rand.floor;
with $foo & $bar {
    say 'yes';
} orwith $foo | $bar {
    say 'maybe';
} else {
    say 'no';
}

Или для произвольного количества переменных:

my Str @strings = do 2.rand.floor ?? '' !! Nil for ^3;
with all @strings {
    say 'yes';
} orwith any @strings {
    say 'maybe';
} else {
    say 'no';
}
6 голосов
/ 18 июня 2020

with и др. Великолепны, и я мог бы написать цепочку with оператор, особенно если сопоставление конкретных комбинаций переменных было ad ho c или я думал, что это могло бы стать таким, учитывая ожидаемую эволюцию кода.

Но, учитывая ваш буквальный вопрос и сценарий, который у вас есть в вашем ответе, я бы гораздо более вероятно go с given / when вместо:

my ($foo, $bar, $baz) = (Mu, 42).pick xx Inf;

given $foo, $bar, $baz {
  when .all.defined { say 'yes'   }
  when .any.defined { say 'maybe' }
  default           { say 'no'    }
}
5 голосов
/ 23 июня 2020

Вы можете создать комбинированное значение и использовать подпись как when условное.

(:(…) - синтаксис для литерала подписи.)

given ($foo,$bar,$baz) {
   when :( Any:D, Any:D, Any:D ) { 'all defined' }
   when :( Any:D, Any:D, Any:U ) { '$baz undefined' }
   when :( Any:D, Any:_, Any:D ) { '$bar not checked' }

   default { … }
}

Вы могли создайте лексические константы, чтобы сделать его короче.

my constant D = Any:D; # defined
my constant U = Any:U; # undefined
my constant _ = Any:_; # either

given ($foo,$bar,$baz) {
   when :(D,D,D) { 'all defined' }
   when :(D,D,U) { '$baz undefined' }
   when :(D,_,D) { '$bar not checked' }

   default { … }
}

Или, если вы хотите, чтобы он был более наглядным:

my constant term:<▣> = Any:D; # defined
my constant term:<□> = Any:U; # undefined
my constant term:<▨> = Any:_; # either

given ($foo,$bar,$baz) {
   when :(▣,▣,▣) { 'all defined' }
   when :(▣,▣,□) { '$baz undefined' }
   when :(▣,▨,▣) { '$bar not checked' }

   default { … }
}

Вы можете создать подпрограмму, которая дает подпрограмму для использования вместо нее.

## something like this should work
## but something is weird with the returns constraint
#sub prefix:<d> ( $_ ) {
#  my constant %lookup = (
#    '▣' => Parameter.new( type => Any:D ),
#    '□' => Parameter.new( type => Any:U ),
#    '▨' => Parameter.new( type => Any:_ ),
#  );
#  Signature.new: params => %lookup{.comb}
#}

sub prefix:<d> ( $_ ) {
  my constant %lookup = (
    '▣' => 'Any:D',
    '□' => 'Any:U',
    '▨' => 'Any:_',
  );
  use MONKEY-SEE-NO-EVAL;
  EVAL ":(%lookup{.comb}.join(","))"
}

given ($foo,$bar,$baz) {
   when d'▣▣▣' { 'all defined' }
   when d'▣▣□' { '$baz undefined' }
   when d'▣▨▣' { '$bar not checked' }

   default { … }
}
...