How can I reference an unnamed argument of a when expression? - switch-statement

I have a when expression that looks something like this:
when(foo.toString()){
"" ->'A'
"HELLO" ->'B'
"GOODBYE"->'C'
else ->foo.toString()[0]//problematic method call duplication
}
Now, I don't want to call foo.toString() twice, but I also want this to remain a single expression. Is there a convenient way for me to access the value I passed into the when expression in its else block, such as the it or this# syntax found elsewhere in the language?
I'm currently using the following work-around:
with(foo.toString()){
when(this){
"" ->'A'
"HELLO" ->'B'
"GOODBYE"->'C'
else ->this[0]
}
}
But this introduces another block, and is less readable than I'd like. Is there a better solution?

For the when block there's no variable specified, but you can use the let() function for a similar behavior which might be a little better than your workaround, but behaving the same.
foo.toString().let{
when(it){
"" ->'A'
"HELLO" ->'B'
"GOODBYE"->'C'
else ->it[0]
}
}

Related

what's the proper way to allow users to provide a string "mangler" as a regex/proc/expr/

In my Tcl/Tk project, i need to allow my users to mangle a string in a well-defined way.
The idea is, to allow people to declare a "string mangling" proc/expr/function/... in a configuration file, which then gets applied to the strings in question.
I'm a bit worried on how to properly implement that.
Possibilities I have considered so far:
regular expressions
That was my first thought, but there's two caveats:
search/replace with regular expressions in Tcl seems to be awkward. at least with regsub i need to pass the match and replacement parts separately (as opposed to how e.g. sed allows me to pass a single complicated string that does everything for me); there are sed implementations for Tcl, but they look naive and might break rather sooner than later
also regexes can be awkward by themselves; using them to mangle complicated strings is often more complicated than it should be
procs?
Since the target platform is Tcl anyhow, why not use the power of Tcl to do string mangling?
The "function" should have a single input and produce a single output, and ideally it the user should be nudged into doing it right (e.g. not being able to define a proc that requires two arguments) and it be (nigh) impossible to create side-effects (like changing the state of the application).
A simplistic approach would be to use proc mymangler s $body (with $body being the string defined by the user), but there are so many things that can go wrong:
$body assuming a different arg-name (e.g. $x instead of $s)
$body not returning anything
$body changing variables,... in the environment
expressions look more like it (always returning things, not allowing to modify the environment easily), but i cannot make them work on strings, and there's no way to pass a variable without agreeing its name.
So, the best I've come up with so far is:
set userfun {return $s} # user-defined string
proc mymangler s ${userfun}
set output [mymangler $input]
Are there better ways to achieve user-defined string-manglers in Tcl?
You can use apply -- the user provides a 2-element list: the second element is the "proc body", the code that does the mangling; the first element is the variable name to hold the string, this variable is used in the body.
For example:
set userfun {{str} {string reverse $str}}
set input "some string"
set result [apply $userfun $input] ;# => "gnirts emos"
Of course the code you get from the user is any arbitrary Tcl code. You can run it in a safe interpreter:
set userfun {{str} {exec some malicious code; return [string reverse $str]}}
try {
set interp [safe::interpCreate]
set result [$interp eval [list apply $userfun $input]]
puts "mangled string is: $result"
safe::interpDelete $interp
} on error e {
error "Error: $e"
}
results in
Error: invalid command name "exec"
Notes:
a standard Tcl command is used, apply
the user must specify the variable name used in the body.
this scheme does protect the environment:
set userfun {{str} {set ::env(SOME_VAR) "safe slave"; return $str$str}}
set env(SOME_VAR) "main"
puts $env(SOME_VAR)
try {
set interp [safe::interpCreate]
set result [$interp eval [list apply $userfun $input]]
puts "mangled string is: $result"
safe::interpDelete $interp
} on error e {
error "Error: $e"
}
puts $env(SOME_VAR)
outputs
main
mangled string is: some stringsome string
main
if the user does not return a value, then the mangled string is simply the empty string.
The "simplistic" approach is like foreach in that it requires the user to supply a variable name and a script to evaluate that uses that variable, and is a good approach. If you don't want it affecting the rest of the program, run it in a separate interpreter:
set x 0
proc mymangler {name body} {
set i [interp create -safe]
set s "some string to change"
try {
# Build the lambda used by apply here instead of making
# the user do it.
$i eval [list apply [list $name $body] $s]
} on error e {
return $e
} finally {
interp delete $i
}
}
puts [mymangler s { set x 1; string toupper $s }]
puts $x
outputs
SOME STRING TO CHANGE
0
If the person calling this says to use s as a variable and then uses something else in the body, it's on them. Same with providing a script that doesn't return anything.
I'd generally allow the user to specify a command prefix as a Tcl list (most simple command names are trivially suitable for this), which you would then apply to the argument by doing:
set mangled [{*}$commandPrefix $valueToMangle]
This lets people provide pretty much anything they want, especially as they can use apply and a lambda term to mangle things as required. Of course, if you're in a procedure then you're probably actually better off doing:
set mangled [uplevel 1 [list {*}$commandPrefix $valueToMangle]]
so that you're running in the caller's context (change 1 to #0 to use the global context instead) which can help protect your procedure against accidental changes and make using upvar within the mangler easier.
If the source of the mangling prefix is untrusted (what that means depends greatly on your application and deployment) then you can run the mangling code in a separate interpreter:
# Make the safe evaluation context; this is *expensive*
set context [interp create -safe]
# You might want to let them define extra procedures too
# interp invokehidden $context source /the/users/file.tcl
# Use the context
try {
set mangled [interp eval $context [list {*}$commandPrefix $valueToMangle]]
} on error {msg} {
# User supplied something bad; error message in $msg
}
There's various ways to support users specifying the transformation, but if you can expose the fact that you're working with Tcl to them then that's probably easiest and most flexible.

how to use ternary operator in mustache template

I have a scenario where I am getting {{isdone}} value with Boolean data.
I want to be printed as "pending" for false value and "Done" for true.
I'm using below code, Which isn't working.
{{isdone}} == false ? "pending" : "Done"
Use the ^ block for else.
You can (now) use the ^ block for an else or false condition. Something like this should work:
{{#isdone}}Done{{/isdone}}{{^isdone}}pending{{/isdone}}
Or as a more readable multi-line block of code:
{{#isdone}}
Done
{{/isdone}}
{{^isdone}}
pending
{{/isdone}}
As long as you have control of your context data, correct way is to pass another variable, that will already contain pending or Done beforehand.
If you don't have control over the data, then maybe moustache isn't good for you as you may need template engine that can have some more logic in it to transform data a bit.
You might want to register a ternary helper for that
Handlebars.registerHelper("ternary", function (condition, trueValue, falseValue, options) {
return condition ? trueValue : falseValue;
});
and then in your templates use it like
{{ternary isdone "Done" "pending"}}

How to check if the first variable passed into a method is a string. Perl

I have no idea how to check for this. My method(if condition in method) should only work (execute) if the first argument passed in is a string. I know how to check other types, but I can't seem to find anything for checking for a string.
For a hash I would do something like;
if(ref eq 'HASH') {...}
If someone could provide a simple example I'm sure I would be able to apply it to what I'm doing. I will put up the code for the method and an explanation for the whole operational details of the method if needed.
Added Information
This is a method for handling different types of errors in the software, here are the 3 possible input formats:
$class->new("error string message")
$class->new("error string message", code => "UNABLE_TO_PING_SWITCH_ERROR")
$class->new("error string message", code => "UNABLE_TO_PING_SWITCH_ERROR", switch_ip => $ip3, timeout => $timeout)
There will always be an error message string first.
With the 1st case there is also a hashref to an error hash structure that is located in a library,
this method new will go into a template processing if the word "code" exists as an arg. where the longer detailed error message is constructed. (I already have the logic for this).
But I have to add logic so that the error message string is added to the hash, so the output is one hash, and not strings.
The second case is very similar to the first, where there are parameters eg. switch_ip , which are inserted into the string using a similar template processing logic, (already have this too).
So I think the first and second cases can be handled in the same way, but I'm not sure, so separated them in this question.
The last case is just can error message string by itself, which at the minute I just insert it into a one key message hash { message => "error string}.
So after all that how should I be checking or dividing up these error cases, At the minute my idea for the ones with code , is to dump the arguments into a hash and just use something like:
if(exists($param{code}) { doTemplateProcess()...}
I need to ensure that there is a string passed in first though. Which was my original question. Does any of my context information help? I hope I didn't go off the topic of my question, if so I'll open this a new question. Thanks.
Error hash - located in Type.pm
use constant ERROR_CODE => {
UNABLE_TO_PING_SWITCH_ERROR => {
category => 'Connection Error:',
template => 'Could not ping switch %s in %s minutes',
tt => {template => 'disabled'},
fatal => 1,
wiki_page => www.error-solution.com/,
},
}
From comments:
These will be called in the software's code like so
ASC::Builder::Error->new(
"Phase x this occured because y was happening:",
code => UNABLE_TO_PING_SWITCH_ERROR,
switch_ip => $ip3,
timeout => 30,
);
Putting the wisdom of your particular problem aside and channeling Jeff Foxworthy:
If you have a scalar and it's not a reference, you might have a string.
If your non-reference scalar doesn't look like a number, it might be a string.
If your non-reference scalar looks like a number, it can still be a string.
If your non-reference scalar has a different string and number value, it might be a dualvar.
You know that your argument list is just that: a list. A list is a collection of scalar values. A scalar can be a reference or not a reference. I think you're looking for the not a reference case:
die "You can't do that" if ref $first_argument;
Past that, you'd have to do fancier things to determine if it's the sort of value that you want. This might also mean that you reject objects that pretend to be strings through overloading and whatnot.
Perhaps you can make the first argument part of the key-value pairs that you pass. You can then access that key to extract the value and delete it before you use the remaining pairs.
You may easily check only whether the error string is a simple scalar value or a reference. You would do that with ref, but you must consider what you want to do if the first parameter isn't a string
You should write your constructor in the ASC::Builder::Error package along these lines
sub new {
my $class = shift;
my ($error, %options) = #_;
die if ref $error;
bless { string => $error }, $class;
}
This example simply dies, and so kills the program, if it is called with anything other than a simple string or number as the first parameter
You may call it as
ASC::Builder::Error->new('error')
or
ASC::Builder::Error->new(42)
and all will be well. If you try
ASC::Builder::Error->new('message', 'code')
then you will see a warning
Odd number of elements in hash assignment
And you may make that warning fatal
If there is anything more then you should explain
Supporting all of the following is simple:
$class->new("s")
$class->new("s", code => "s")
$class->new("s", code => "s", switch_ip => "s", timeout => "s")
All you need is the following:
sub new {
my ($class, $msg, %opts) = #_;
...
}
You can checks such as the following to examine what the called provided:
if (exists($opts{code}))
if (defined($opts{code}))
if ($opts{code})
Despite saying that the string will always be provided, you now ask how to check if was provided. As such, you are probably trying to perform validation rather than polymorphism. You shouldn't waste your time doing this.
Let's look at the hash reference example you gave. ref($arg) eq 'HASH' is wrong. That returns false for some hash references, and it returns false for some things that act like a reference to a hash. The following is a more proper check:
eval { %$arg; 1 }
The equivalent for strings would be the following:
eval { "$arg"; 1 }
Unfortunately, it will always return true! Every value can act as a string. That means the best thing you can do is simply to check if any argument is provided.
use Carp qw( croak );
croak("usage") if !#_;
It's rare for Perl subs to perform argument validation. Not only is it tricky, it's also expensive. It also provides very little benefits. Bad or missing arguments usually results in exceptions or warnings shortly after.
You might see suggestions to use croak("usage") if ref($arg); (or worse, die if ref($arg);), but keep in mind that those will cause the rejection of perfectly fine objects that overload stringification (which is somewhat common), and they will fail to detect the problem with ASC::Builder::Error->new(code => ...) because code produces a string. Again, performing type-based argument validation is an expensive and buggy practice in Perl.

IIF and LEFT functions

Could somebody tell me what I'm doing wrong in this query to get a calculated column?
IIf(Left([VIN Number],2)="1F" OR "2F" OR "3F" OR "1L” OR “2L” OR “NM”,”Food”,"Comp")
It's giving me error saying "you may have entered an operand without an operator"
You can't have a compound condition like ...
Something = "a" OR "b"
You would have to repeat the Something = after OR ...
Something = "a" OR Something = "b"
For your query's IIf() expression, an In() list containing the match values would be more concise ...
IIf(Left([VIN Number],2) IN ("1F", "2F", "3F", "1L", "2L", "NM"), "Food", "Comp")
Beware your code sample includes typesetting quotes (“ and ”). Make sure you use plain quotes (") in your real code.
The OR operator doesn't work how you expect it to. It requires statements that resolve to TRUE or FALSE on either side of it, so you can't check if something is equal to one of many things like this.
You either need to do:
Iif(Left([VIN Number],2)="1F" OR Left([VIN Number],2)="2F" OR Left([VIN Number],2)="3F"...., "Food", "Comp")
or you need to do:
Iif(Left([VIN Number],2) In("1F","2F", "3F"....), "Food", "Comp")

What do empty square brackets after a variable name mean in Groovy?

I'm fairly new to groovy, looking at some existing code, and I see this:
def timestamp = event.timestamp[]
I don't understand what the empty square brackets are doing on this line. Note that the timestamp being def'd here should receive a long value.
In this code, event is defined somewhere else in our huge code base, so I'm not sure what it is. I thought it was a map, but when I wrote some separate test code using this notation on a map, the square brackets result in an empty value being assigned to timestamp. In the code above, however, the brackets are necessary to get correct (non-null) values.
Some quick Googling didn't help much (hard to search on "[]").
EDIT: Turns out event and event.timestamp are both zero.core.groovysupport.GCAccessor objects, and as the answer below says, the [] must be calling getAt() on these objects and returning a value (in this case, a long).
The square brackets will invoke the underlying getAt(Object) method of that object, so that line is probably invoking that one.
I made a small script:
class A {
def getAt(p) {
println "getAt: $p"
p
}
}
def a = new A()
b = a[]
println b.getClass()
And it returned the value passed as a parameter. In this case, an ArrayList. Maybe that timestamp object has some metaprogramming on it. What does def timestamp contains after running the code?
Also check your groovy version.
Empty list, found this. Somewhat related/possibly helpful question here.
Not at a computer, but that looks like it's calling the method event.timestamp and passing an empty list as a parameter.
The same as:
def timestamp = event.timestamp( [] )

Resources