How to use a constants value as a hash key - raku

Is there a simple way to use the value of a defined constant as a hash / pair key in Perl6?
For instance :
constant KEY = "a string";
my %h = ( KEY => "a value" );
This will creatre a key of "KEY" not "a string".
I can do :
my %h = ( "{KEY}" => "a value" );
But that seems a bit clunky. I was wondering if there was a better way?

The most convenient options would be to either:
Declare the constant with a sigil (such as constant $KEY = "a string";), thus avoiding the problem in the first place
Wrap the left hand side in parentheses (like (KEY) => "a value"), so it won't be treated as a literal
Write it instead as pair(KEY, "a value")
Also, note that:
my %h = ( "{KEY}" => "a value" );
Is a useless use of parentheses, and that:
my %h = KEY, "a value";
Will also work, since non-Pairs in the list of values to assign to the hash will be paired up. It loses the visual pairing, however, so one of the previously suggested options is perhaps better.

Related

Can't get key of object that is numeric

I'm working with an API that returns an array of objects. I can get all the keys, but two of those have numbers as keys, and I cannot get it. Give me an error.
I really dont know why I can not get it those keys.
Is there something different due to are numbers?
BTW Im using axios.
If you're using dot notation, you should change to bracket notation to access properties start by a number.
The code below uses dot notation, it throws an error
const test = {"1h" : "test value"};
console.log(test.1h); // error
Why :
In the object.property syntax, the property must be a valid JavaScript
identifier.
An identifier is a sequence of characters in the code that identifies a variable, function, or property.
In JavaScript, identifiers are case-sensitive and can contain Unicode letters, $, _, and digits (0-9), but may not start with a digit.
The code below uses bracket notation, works fine
const test = {"1h" : "test value"};
console.log(test["1h"]); // works
Why :
In the object[property_name] syntax, the property_name is just a
string or Symbol. So, it can be any string, including '1foo', '!bar!',
or even ' ' (a space).
Check out the document here

Put named capture from regex in Subset into a variable in the signature

Consider
subset MySubset of Str where * ~~ /^ \d $<interesting> = ( \d+ ) $/;
Now I want to use the subset as a Type in my signature, but put the captured part(s) into a variable via unpacking, kinda like
sub f( MySubset $( :$interesting ) )
{
say $interesting;
}
f( "12345678" ); # should say 2345678
That's not working of course. Is it even possible to do this?
Subsignature unpacking is about turning a value into a Capture and matching against that.
class Point {
has ( $.x, $.y );
}
my ( :$x, :$y ) := Point.new( x => 3, y => 4 ).Capture;
say "[$x,$y]"; # [3,4]
Since a Str doesn't have a public attribute named $.interesting, it won't match.
A subset is just extra code to check a value more completely than you could otherwise do. It does not turn the value into a new type.
It would be more likely to work if you used $<interesting>.
sub f( MySubset )
{
say $<interesting>;
}
Of course since blocks get their own $/, this also does not work.
While it might be nice to pass information from a subset to a signature, I am not aware of anyway to do it.
As a side note, where already does smart matching so it is an incredibly bad idea to use ~~ inside of it.
This is basically how your subset works:
"12345678" ~~ ( * ~~ /…/ )
In this particular case you could just use .substr
sub f( MySubset $_ ) {
.substr(1)
}
I can't figure out a way with a subset type, however there is a way - with a little...creativity - to do a match and unpack it in the signature.
Match inherits from Capture, so having one be unpacked in a signature is straightforward - if only we can arrange for there to be a parameter that contains the Match we wish to unpack. One way to do that is to introduce a further parameter with a default. We can't really stop anyone passing to it - though we can make it a pain to do so by using the anonymous named parameter. Thus, if we write this:
sub foo($value, :$ (:$col, :$row) = $value.match(/^$<col>=[<:L>+]$<row>=[\d+]$/)) {
say $col;
say $row;
}
And call it as foo("AB23"), the output is:
「AB」
「23」
Finally, we may factor the rule out to a named token, achieving:
‌‌my token colrow { ^$<col>=[<:L>+]$<row>=[\d+]$ }
sub foo($value, :$ (:$col, :$row) = $value.match(&colrow)) {
say $col;
say $row;
}
I'm pretty sure wheres (and subsets) just answer True/False. Brad concurs.
There are essentially always metaprogramming answers to questions but I presume you don't mean that (and almost never dig that deep anyway).
So here are a couple ways to get something approaching what you seem to be after.
A (dubious due to MONKEYing) solution based on Brad's insights:
use MONKEY;
augment class Str {
method MyMatch { self ~~ / ^ \d $<interesting> = ( \d+ ) $ / }
}
class MyMatch is Match {}
sub f( MyMatch() $foo (:$interesting) ) { say ~$interesting }
f( "12345678" ); # 2345678
The bad news is that the sub dispatch works even if the string doesn't match. The doc makes it clear that the coercer method (method MyMatch in the above) cannot currently signal failure:
The method is assumed to return the correct type — no additional checks on the result are currently performed.
One can hope that one day augmenting a class will be an officially respectable thing to do (rather than requiring a use MONKEY...) and that coercing can signal failure. At that point I think this might be a decent solution.
A variant on the above that binds to $/ so you can use $<interesting>:
use MONKEY;
augment class Str {
method MyMatch { self ~~ / ^ \d $<interesting> = ( \d+ ) $ / }
}
class MyMatch is Match {}
sub f( MyMatch() $/ ) { say ~$<interesting> }
f( "12345678" ); # 2345678
Another way that avoids MONKEYing around is to use a subset as you suggest but separate the regex and subset:
my regex Regex { ^ \d $<interesting> = ( \d+ ) $ }
subset Subset of Str where &Regex;
sub f( Subset $foo ; $interesting = ~($foo ~~ &Regex)<interesting> )
{
say $interesting;
}
f( "12345678" ); # 2345678
Notes:
The regex parses the input value at least twice. First in the Subset to decide whether the call dispatches to the sub. But the result of the match is thrown away -- the value arrives as a string. Then the regex matches again so the match can be deconstructed. With current Rakudo, if the sub were a multi, it would be even worse -- the regex would be used three times because Rakudo currently does both a trial bind as part of deciding which multi to match, and then does another bind for the actual call.
Parameters can be set to values based on previous parameters. I've done that with $interesting. A signature can have parameters that are part of dispatch decisions, and others that are not. These are separated by a semi-colon. I've combined these two features to create another variable, thinking you might think that a positive thing. Your comment suggest you don't, which is more than reasonable. :)

What is the difference between ', ` and |, and when should they be used?

I've seen strings written like in these three ways:
lv_str = 'test'
lv_str2 = `test`
lv_str3 = |test|
The only thing I've notice so far is that ' trims whitespaces sometimes, while ` preserves them.
I just recently found | - don't know much about it yet.
Can someone explain, or post a good link here when which of these ways is used best and if there are even more ways?
|...| denotes ABAP string templates.
With string templates we can create a character string using texts, embedded expressions and control characters.
ABAP Docu
Examples
Use ' to define character-typed literals and non-integer numbers:
CONSTANTS some_chars TYPE char30 VALUE 'ABC'.
CONSTANTS some_number TYPE fltp VALUE '0.78'.
Use ` to define string-typed literals:
CONSTANTS some_constant TYPE string VALUE `ABC`.
Use | to assemble text:
DATA(message) = |Received HTTP code { status_code } with message { text }|.
This is an exhaustive list of the ways ABAP lets you define character sequences.
To answer the "when should they be used" part of the question:
` and | are useful if trailing spaces are needed (they are ignored with ', cf this blog post for more information, be careful SCN renders today the quotes badly so the post is confusing) :
DATA(arrival) = `Hello ` && `world`.
DATA(departure) = |Good | && |bye|.
Use string templates (|) rather than the combination of ` and && for an easier reading (it remains very subjective, I tend to prefer |; with my keyboard, | is easier to obtain too) :
DATA(arrival) = `Dear ` && mother_name && `, thank you!`.
DATA(departure) = |Bye { mother_name }, thank you!|.
Sometimes you don't have the choice: if a String data object is expected at a given position then you must use ` or |. There are many other cases.
In all other cases, I prefer to use ' (probably because I obtain it even more easily with my keyboard than |).
Although the other answers are helpful they do not mention the most important difference between 'and `.
A character chain defined with a single quote will be defined as type C with exactly the length of the chain even including white spaces at the beginning and the end of the character sequence.
So this one 'TEST' will get exactly the type C LENGTH 4.
wherever such a construct `TEST` will evaluate always to type string.
This is very important for example in such a case.
REPORT zutest3.
DATA i TYPE i VALUE 2.
DATA(l_test1) = COND #( WHEN i = 1 THEN 'ACT3' ELSE 'ACTA4').
DATA(l_test2) = COND #( WHEN i = 1 THEN `ACT3` ELSE `ACTA4`).
WRITE l_test1.
WRITE l_test2.

List repetition (xx) without evaluation?

The list repetition operator (xx) evaluates the list every time it is repeated. For example,
my #input = get() xx 5;
will evaluate to the first 5 lines of STDIN. Is there any way I can repeat just the value of the element 5 times, rather than evaluating it each time? Currently, I've been assigning it to a variable, then repeating it, but it seems a bit cumbersome that way.
my $firstLine = get();
my #firstlineRepeated = $firstLine xx 5;
Is there a prefix or something that lets me do it in one statement?
Using given to contextualize it into $_ is one fairly neat way:
my #input = ($_ xx 5 given get());
say #input;
That, when I type hello, gives:
[hello hello hello hello hello]
Since given simply contextualizes, rather than doing any kind of definedness or truth test, it's a bit safer as a general pattern than andthen.
You could try use the andthen operator:
my #input = (get() andthen $_ xx 5);
From the documentation:
The andthen operator returns Empty upon encountering the first
undefined argument, otherwise the last argument. Last argument is
returned as-is, without being checked for definedness at all.
Short-circuits. The result of the left side is bound to $_ for the
right side, or passed as arguments if the right side is a Callable,
whose count must be 0 or 1.
Using phrase ENTER works too
my #input = ENTER { get() } xx 5;

Using a hash with object keys in Perl 6

I'm trying to make a Hash with non-string keys, in my case arrays or lists.
> my %sum := :{(1, 3, 5) => 9, (2, 4, 6) => 12}
{(1 3 5) => 9, (2 4 6) => 12}
Now, I don't understand the following.
How to retrieve an existing element?
> %sum{(1, 3, 5)}
((Any) (Any) (Any))
> %sum{1, 3, 5}
((Any) (Any) (Any))
How to add a new element?
> %sum{2, 4} = 6
(6 (Any))
Several things are going on here: first of all, if you use (1,2,3) as a key, Rakudo Perl 6 will consider this to be a slice of 3 keys: 1, 2 and 3. Since neither of these exist in the object hash, you get ((Any) (Any) (Any)).
So you need to indicate that you want the list to be seen as single key of which you want the value. You can do this with $(), so %sum{$(1,3,5)}. This however does not give you the intended result. The reason behind that is the following:
> say (1,2,3).WHICH eq (1,2,3).WHICH
False
Object hashes internally key the object to its .WHICH value. At the moment, Lists are not considered value types, so each List has a different .WHICH. Which makes them unfit to be used as keys in object hashes, or in other cases where they are used by default (e.g. .unique and Sets, Bags and Mixes).
I'm actually working on making this the above eq return True before long: this should make it to the 2018.01 compiler release, on which also a Rakudo Star release will be based.
BTW, any time you're using object hashes and integer values, you will probably be better of using Bags. Alas not yet in this case either for the above reason.
You could actually make this work by using augment class List and adding a .WHICH method on that, but I would recommend against that as it will interfere with any future fixes.
Elizabeth's answer is solid, but until that feature is created, I don't see why you can't create a Key class to use as the hash key, which will have an explicit hash function which is based on its values rather than its location in memory. This hash function, used for both placement in the list and equality testing, is .WHICH. This function must return an ObjAt object, which is basically just a string.
class Key does Positional {
has Int #.list handles <elems AT-POS EXISTS-POS ASSIGN-POS BIND-POS push>;
method new(*#list) { self.bless(:#list); }
method WHICH() { ObjAt.new(#!list.join('|')); }
}
my %hsh{Key};
%hsh{Key.new(1, 3)} = 'result';
say %hsh{Key.new(1, 3)}; # output: result
Note that I only allowed the key to contain Int. This is an easy way of being fairly confident no element's string value contains the '|' character, which could make two keys look the same despite having different elements. However, this is not hardened against naughty users--4 but role :: { method Str() { '|' } } is an Int that stringifies to the illegal value. You can make the code stronger if you use .WHICH recursively, but I'll leave that as an exercise.
This Key class is also a little fancier than you strictly need. It would be enough to have a #.list member and define .WHICH. I defined AT-POS and friends so the Key can be indexed, pushed to, and otherwise treated as an Array.