Switch statement using variables in the case condition - maya

Is it possible to create Switch statements that use variables in the case condition, in Maya Mel Script language?
Something along the lines (stupid example for the sake of explanation):
$val1 = "foo";
$val2 = "bar";
// imagine $input as an argument of some proc
switch ($input)
{
case $val1:
print "Input is 'foo'";
break;
case $val2:
print "Input is 'bar'";
break;
}
P.s. I tried that and it didn't work, but you might know of another option.
Thanks a lot

You can't use variables as case values directly, but you can build a string containing the code that you want to execute, with variables replaced with their values, and pass that string to the eval function:
$val1 = "foo";
$val2 = "bar";
// imagine $input as an argument of some proc
string $cmd;
$cmd = "switch (\"" + $input + "\")";
$cmd += "{";
$cmd += "case \"" + $val1 + "\":";
$cmd += " print \"Input is 'foo'\";";
$cmd += " break;";
$cmd += "case \"" + $val2 + "\":";
$cmd += " print \"Input is 'bar'\";";
$cmd += " break;";
$cmd += "}";
eval $cmd;

Related

Simplify long if-else chain with regex by comparing with array

for (my $j = 0; $j <100000; $j++){
my $outcome = rand();
for (my $k = 0; $k < #cum_sum; $k++){
if ($cum_sum[$k] >= $outcome){
if ($keys[$k] =~ m/\"|\/|\<|\>|\\|\`|\~|\#|\#|\$|\%|\^|\*|[0-9]/) {
print $out "";
}
if ($keys[$k] =~ m/\s/){
print $out " ";
}
elsif ($keys[$k] =~ m/\&/){
print $out " and ";
}
elsif ($keys[$k] =~ m/\!/){
print $out "! ";
}
elsif ($keys[$k] =~ m/\:/){
print $out ": ";
}
elsif ($keys[$k] =~ m/\'/){
print $out "' ";
}
elsif ($keys[$k] =~ m/\./){
print $out ". ";
}
elsif ($keys[$k] =~ m/\;/){
print $out "; ";
}
elsif ($keys[$k] =~ m/\?/){
print $out "? ";
}
elsif ($keys[$k] =~ m/\,/){
print $out ", ";
}
else {
print $out "$keys[$k]";
}
last;
}
}
# print "$outcome\n";
}
I mostly need help with simplifying the long chain of elsif statements I have.
The logic in the outer for loops works.
#keys is an array of two character (digrams) strings.
I am trying to make the elsif statements more efficient by comparing the digrams from #key to an array of the punctuation marks #punctuation = qw(! : " ' ; ? , .)
Then, if the digram does contain one of the punctuation marks in the punctuation array, the digram gets changed to "punctuation_mark " e.g. "a!" -> "! "
The end result would be that I do not use regexes for any of #punctuation.
However, I am not sure on how to implement this change.
Thank you!
In order to simplify your code, it may have sense to use the Tie::RegexpHash CPAN module. The main idea is build a hash with regexpr as keys, so you easily find the related values by matching:
use Tie::RegexpHash;
my $rehash = Tie::RegexpHash->new();
$rehash->add( qr/\s/, " " );
$rehash->add( qr/\&/, " and " );
#...
my $value = $rehash->match( "&" ); # $value <-- " and "
Use alternation, just like in the first regex, but capture the match and use $1 to replace the word
my $re_punct = join '|', map { quotemeta } qw(& ! : ' . ; ? ,); #'
for my $j (0..99_999) {
my $outcome = rand();
for my $k (0..$#cum_sum) {
...
if ($keys[$k] =~ /($re_punct)/) {
if ($1 eq '&') { $keys[$k] = " and " }
else { $keys[$k] = "$1 " }
}
...
}
}
Comments
quotemeta escapes by \ all "ASCII non-"word" characters"
syntax: $#ary is the index of the last element in #ary, just right for looping over array index
for my $i ($beg .. $end) is much clearer than the equivalent C-style for loop†
The presented logic leaves a question: what if both characters are punctuation?
Note on your idea to do it "By Comparing With Array"
You could use List::MoreUtils::first_value, for instance. It would go like
use List::MoreUtils qw(first_value);
my #punc = map { quotemeta } qw(& ! : ' . ; ? ,); #'
foreach my $word (#words) {
if (my $match = first_value { $word =~ /$_/ } #punct) {
$word = $match;
}
}
The first_value (or firstval) returns the first element of #punct for which the block returns true, and undef if none do. The $word aliases the currently processed element of #words and changing it changes the array element; so you get your replacement.
However, you still have to deal with regex and escape (at least some of) punctuation. Thus I see no advantage in going to this trouble; the "straight-up" regex is far clearer in this case.
† Even compiled languages evolve this way. The C++11 introduced the range-based for loop
for (auto var: container) ... // (really, const auto&), or auto&, or auto&&
and a standard reference linked above says
Used as a more readable equivalent to the traditional for loop [...]
In Perl this is how things are done; just use it.
So, looking at it you have two cases:
One where you replace a set of values, with 'the value and a space'. And another where you replace with something different.
So how about creating a lookup table for each, and processing just two regexes:
#!/usr/bin/env perl.
use strict;
use warnings;
use Data::Dumper;
my %replace = (
'"' => "",
'/' => "",
'&' => " and ",
);
my #add_space = ( ',', '?', ';', '.', "'", ':', '!' );
my $search = join ( "|", map { quotemeta } keys %replace );
$search = qr/($search)/;
my $add_space_after = join "|", map {quotemeta} #add_space;
$add_space_after = qr/($add_space_after)/;
while ( <DATA> ) {
s/\s/ /g;
s/$search/$replace{$1}/;
s/$add_space_after/$1 /;
print;
}
__DATA__
Work:Work
cookies&milk;wordhere
why?are;you/so "sad"
This gives us what you want I think, and - hopefully - keeps the code pretty conscise.
Of importance is the quotemeta function here, because it ensures your metachars are escaped before regexing them.
Note - the only one of your examples this doesn't handle is the \s to " ". But that's IMO probably best to write separately for clarity, as obviously youc can't quotemeta it.

strange script issue in Powershell

I am new to PowerShell, but not scripting.
Why does this script:
$usr = "john.doe"
$usrname = $usr -split ".", 0, "simplematch"
$fullname = upperInitial($usrname[0]) + upperInitial($usrname[1])
write-host "Hello $fullname"
function upperInitial($upperInitialString) {
return $upperInitialString.substring(0, 1).toupper() + $upperInitialString.substring(1).tolower()
}
return me just 'Hello John' and not 'Hello John Doe'?
It's not treating the second call of the upperInitial function as a function, it's treating it as a paramter of the first call to the function I think.
Either of these work:
$fullname = "$(upperInitial($usrname[0])) $(upperInitial($usrname[1]))"
write-host "Hello $fullname"
The above uses the subexpression operator $() to execute the functions within a double quoted string.
$fullname = (upperInitial($usrname[0])) + ' ' + (upperInitial($usrname[1]))
write-host "Hello $fullname"
This one combines the result of the two functions as you had intended, although I also added a space character because otherwise it was JohnDoe.

How to provide default output

I'm trying to get the following Perl program to print a default value, if no result is returned from a query. I am not sure how to do that.
When I run the script, I get the error: Can't call method "country_name" on an undefined value. Are there Any ideas on how I can change the program to print a default value, if no results are returned?
#!/usr/bin/perl update IP addresses with country
use strict;
use warnings;
use Net::IPInfoDB;
my $psql = "/usr/local/pgsql/current/bin/psql";
my $db = 'cpi';
my $args = "-U postgres -qc";
my $date = `/bin/date +\%y\%m\%d%H`;
my $reportfile = "/tmp/multiiplogins-$date";
my $sendmail = "/usr/sbin/sendmail -t -fcpi\#user.com";
my $mailsubject = "Login Report";
my $mailto = 'email#user.com';
my $query = "SELECT userid, login, email, logins, ips FROM (SELECT userid,login,email, count(userid) AS logins, count(ipaddr) AS ips FROM (SELECT l.userid, u.login, u.email, l.ipaddr FROM synloginaccess l, synusers u$
my $query2 = "SELECT l.userid, login, email, ipaddr FROM synloginaccess l, synusers u where l.accesstime > (now() - interval '24 hours') and l.type=2 and l.userid=u.userid ORDER BY l.userid;";
open (REPORT, ">$reportfile");
my $command = qq/$psql $db $args "$query"/;
my $command2 = qq/$psql $db $args "$query2"/;
my $result = `$command`;
my $result2 = `$command2`;
my $g = Net::IPInfoDB->new;
$g->key("api_key");
#we split $login into an array, line-by-line
my #lines = split("\n", $result2);
for my $line (#lines) {
#now we iterate through every line one-by-one
if ($line =~ /(?<ip>\d+\.\d+\.\d+\.\d+)/) {
my $city = $g->get_city("$1");
my $addr = $g->get_country("$1");
print "$line " . "| " . "\t" . $city->city_name . ", " . $addr->country_name ."\n";
print REPORT "$line " . "| " . "\t" . $city->city_name . ", ". $addr->country_name ."\n";
}
else {
print "$line \n ";
}
}
close REPORT;
mailReport();
sub mailReport{
#mail it
open(MAIL, "|$sendmail");
print MAIL "To: $mailto\n";
print MAIL "Subject: $mailsubject\n";
print MAIL "\n";
open (INFILE, "$reportfile");
my #contents = <INFILE>;
my $line;`
$addr ? $addr->country_name : "default"

Dynamically punctuating and grammatically correcting a string via a Foreach loop in PowerShell

I have a simple section in a PowerShell script that goes through each array in a list and grabs the data (found at [3] of the current array), using it to determine if another part of the array (found at [0]) should be added to the end of a string.
$String = "There is"
$Objects | Foreach-Object{
if ($_[3] -match "YES")
{$String += ", a " + $_[0]}
}
This works fine and dandy, resulting in a $String of something like
"There is, a car, a airplane, a truck"
But unfortunately this doesn't really make sense grammatically for what I want. I'm aware that I could either fix the string after it's been created, or include lines in the foreach/if statement that determine which characters to add. This would need to be:
$String += " a " + $_[0] - for the first match.
$String += ", a " + $_[0] - for following matches.
$String += " and a " + $_[0] + " here." - for the last match.
Furthermore I need to determine whether to use " a " if $_[0] starts with a consonant, or " an " if $_[0] starts with a vowel. All in all, I'd like the output to be
"There is a car, an airplane and a truck here."
Thanks!
Try something like this:
$vehicles = $Objects | ? { $_[3] -match 'yes' } | % { $_[0] }
$String = 'There is'
for ($i = 0; $i -lt $vehicles.Length; $i++) {
switch ($i) {
0 { $String += ' a' }
($vehicles.Length-1) { $String += ' and a' }
default { $String += ', a' }
}
if ($vehicles[$i] -match '^[aeiou]') { $String += 'n' }
$String += ' ' + $vehicles[$i]
}
$String += ' here.'

Adding substrings to array, and the deleting that substring from the parent string

I cannot figure out how to do this. My current code is as follows:
[string]$strTest = Read-Host "Please input an IP host address in full dotted decimal.`n(Example: 192.168.004.214)"
[array]$arrTest = 0
$str = $strTest
$fs = "."
$index = $str.indexof($fs)
$count = 1
do{
$chunk = $str.substring(0,$index)
$arrTest += $chunk
$count++
$str = $str -replace ($chunk + ".", "")
}
until($count -eq 4)
$arrTest
I want this to give me an array that's populated with each octet of the IP address, but I'm getting a strange result. Running this in ISE gives me:
0
123
123
123
And I have no idea why.
If you want an array with the octets, just split the string:
$a = $str.Split('.')
You just messed up parenthesis
$str = $str -replace ($chunk + "."), ""

Resources