I'm looking for a simpler solution.
I have a list of prefixes with corresponding suffixes and a list of roots.
my #prefixes = 'A'..'E';
my #suffixes = 'a'..'e';
my #roots = 1, 2;
I would like to make all the possible 'words': A1a, B1b...A2a...E2e.
my #words;
for #roots -> $r {
for #prefixes.kv -> $i, $p {
my $s = #suffixes[$i];
my $word = [~] $p, $r, $s;
#words.push: $word;
}
}
say #words; # [A1a B1b C1c D1d E1e A2a B2b C2c D2d E2e]
I suppose that it is possible to do it much easier using something like zip or cross, but can't figure out how...
My solution would be:
say #roots.map: |(#prefixes >>~>> * <<~<< #postfixes);
Create a WhateverCode for metaopping concatenation, slipping the result to get a Seq with only scalar values at the end.
A few more ways to write it:
say #roots X[&join] (#prefixes Z #suffixes);
say #roots.map({ |(#prefixes Z #suffixes)».join($_) });
say #roots.map({ (#prefixes X~ $_) Z~ #suffixes }).flat;
say (|#prefixes xx *) Z~ (#roots X~ #suffixes);
my #formats = (#prefixes Z #suffixes).flat.map(* ~ '%s' ~ *);
say #formats X[&sprintf] #roots;
(Note: This one prints them in a different order.)
say do for #roots -> $root {
|do for (#prefixes Z #suffixes) -> [$prefix, $suffix] {
$prefix ~ $root ~ $suffix
}
}
Related
In Ruby I can group together some lines of code like so with a begin block:
x = begin
puts "Hi!"
a = 2
b = 3
a + b
end
puts x # 5
it's immediately evaluated and its value is the last value of the block (a + b here) (Javascripters do a similar thing with IIFEs)
What are the ways to do this in Raku? Is there anything smoother than:
my $x = ({
say "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
})();
say $x; # 5
Insert a do in front of the block. This tells Raku to:
Immediately do whatever follows the do on its right hand side;
Return the value to the do's left hand side:
my $x = do {
put "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
}
That said, one rarely needs to use do.
Instead, there are many other IIFE forms in Raku that just work naturally without fuss. I'll mention just two because they're used extensively in Raku code:
with whatever { .foo } else { .bar }
You might think I'm being silly, but those are two IIFEs. They form lexical scopes, have parameter lists, bind from arguments, the works. Loads of Raku constructs work like that.
In the above case where I haven't written an explicit parameter list, this isn't obvious. The fact that .foo is called on whatever if whatever is defined, and .bar is called on it if it isn't, is both implicit and due to the particular IIFE calling behavior of with.
See also if, while, given, and many, many more.
What's going on becomes more obvious if you introduce an explicit parameter list with ->:
for whatever -> $a, $b { say $a + $b }
That iterates whatever, binding two consecutive elements from it to $a and $b, until whatever is empty. If it has an odd number of elements, one might write:
for whatever -> $a, $b? { say $a + $b }
And so on.
Bottom line: a huge number of occurrences of {...} in Raku are IIFEs, even if they don't look like it. But if they're immediately after an =, Raku defaults to assuming you want to assign the lambda rather than immediately executing it, so you need to insert a do in that particular case.
Welcome to Raku!
my $x = BEGIN {
say "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
}
I guess the common ancestry of Raku and Ruby shows :-)
Also note that to create a constant, you can also use constant:
my constant $x = do {
say "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
}
If you can have a single statement, you can leave off the braces:
my $x = BEGIN 2 + 3;
or:
my constant $x = 2 + 3;
Regarding blocks: if they are in sink context (similar to "void" context in some languages), then they will execute just like that:
{
say "Executing block";
}
No need to explicitely call it: it will be called for you :-)
Is there a way to use eqv to lookup a hash value without looping over the key-value pairs when using object keys?
It is possible to use object keys in a hash by specifying the key's type at declaration:
class Foo { has $.bar };
my Foo $a .= new(:bar(1));
my %h{Foo} = $a => 'A', Foo.new(:bar(2)) => 'B';
However, key lookup uses the identity operator === which will return the value only if it is the same object and not an equivalent one:
my Foo $a-prime .= new(:bar(1));
say $a eqv $a-prime; # True
say $a === $a-prime; # False
say %h{$a}; # A
say %h{$a-prime}; # (Any)
Looking at the documentation for "===", the last line reveals that the operator is based on .WHICH and that "... all value types must override method WHICH." This is why if you create two separate items with the same string value, "===" returns True.
my $a = "Hello World";
my $b = join " ", "Hello", "World";
say $a === $b; # True even though different items - because same value
say $a.WHICH ; # "Str|Hello World"
say $b.WHICH ; # (same as above) which is why "===" returns True
So, instead of creating your own container type or using some of the hooks for subscripts, you could instead copy the way "value types" do it - i.e. butcher (some what) the idea of identity. The .WHICH method for strings shown above simply returns the type name and contents joined with a '|'. Why not do the same thing;
class Foo {
has $.bar;
multi method WHICH(Foo:D:) { "Foo|" ~ $!bar.Str }
}
my Foo $a .= new(:bar(1));
my %h{Foo} = $a => 'A', Foo.new(:bar(2)) => 'B';
my Foo $a-prime .= new(:bar(1));
say $a eqv $a-prime; # True
say $a === $a-prime; # True
say %h{$a}; # A
say %h{$a-prime}; # A
There is a small cost, of course - the concept of identity for the instances of this class is, well - let's say interesting. What are the repercussions? The only thing that comes immediately to mind, is if you plan to use some sort of object persistence framework, its going to squish different instances that now look the same into one (perhaps).
Different objects with the same attribute data are going to be indistinguishable - which is why I hesitated before posting this answer. OTOH, its your class, its your app so, depending on the size/importance etc, it may be a fine way to do it.
If you don't like the buildin postcircumfix, provide your own.
class Foo { has $.bar };
my Foo $a .= new(:bar(1));
my %h{Foo} = $a => 'A', Foo.new(:bar(2)) => 'B';
multi sub postcircumfix:<{ }>(\SELF, WhateverCode $c) is raw {
gather for SELF.keys -> $k {
take $k if $c($k)
}
}
dd %h;
dd %h{* eqv $a};
OUTPUT
Hash[Any,Foo] %h = (my Any %{Foo} = (Foo.new(bar => 1)) => "A", (Foo.new(bar => 2)) => "B")
(Foo.new(bar => 1),).Seq
In Ruby or Perl one can assign more than variable by using parentheses. For example (in Ruby):
(i,j) = [1,2]
(k,m) = foo() #foo returns a two element array
Can one accomplish the same in TCL, in elegant way? I mean I know that you can
do:
foreach varname { i j } val { 1 2 } { set $varname $val }
foreach varname { k m } val [ foo ] { set $varname $val }
But I was hoping for something shorter/ with less braces.
Since Tcl 8.5, you can do
lassign {1 2} i j
lassign [foo] k m
Note the somewhat unintuitive left-to-right order of value sources -> variables. It's not a unique design choice: e.g. scan and regexp use the same convention. I'm one of those who find it a little less readable, but once one has gotten used to it it's not really a problem.
If one really needs a Ruby-like syntax, it can easily be arranged:
proc mset {vars vals} {
uplevel 1 [list lassign $vals {*}$vars]
}
mset {i j} {1 2}
mset {k m} [foo]
Before Tcl 8.5 you can use
foreach { i j } { 1 2 } break
foreach { k m } [ foo ] break
which at least has fewer braces than in your example.
Documentation: break, foreach, lassign, list, proc, uplevel
I have a string which looks like that:
width=13
height=15
name=Mirek
I want to turn it into hash (using Perl 6). Now I do it like that:
my $text = "width=13\nheight=15\nname=Mirek";
my #lines = split("\n", $text);
my %params;
for #lines {
(my $k, my $v) = split('=', $_);
%params{$k} = $v;
}
say %params.perl;
But I feel there should exist more concise, more idiomatic way to do that. Is there any?
In Perl, there's generally more than one way to do it, and as your problem involves parsing, one solution is, of course, regexes:
my $text = "width=13\nheight=15\nname=Mirek";
$text ~~ / [(\w+) \= (\N+)]+ %% \n+ /;
my %params = $0>>.Str Z=> $1>>.Str;
Another useful tool for data extraction is comb(), which yields the following one-liner:
my %params = $text.comb(/\w+\=\N+/)>>.split("=").flat;
You can also write your original approach that way:
my %params = $text.split("\n")>>.split("=").flat;
or even simpler:
my %params = $text.lines>>.split("=").flat;
In fact, I'd probably go with that one as long as your data format does not become any more complex.
If you have more complex data format, you can use grammar.
grammar Conf {
rule TOP { ^ <pair> + $ }
rule pair {<key> '=' <value>}
token key { \w+ }
token value { \N+ }
token ws { \s* }
}
class ConfAct {
method TOP ($/) { make (%).push: $/.<pair>».made}
method pair ($/) { make $/.<key>.made => $/.<value>.made }
method key ($/) { make $/.lc }
method value ($/) { make $/.trim }
}
my $text = " width=13\n\theight = 15 \n\n nAme=Mirek";
dd Conf.parse($text, actions => ConfAct.new).made;
Why do I get this error-message?
#!perl6
use v6;
my #a = 1..3;
my #b = 7..10;
my #c = 'a'..'d';
for zip(#a;#b;#c) -> $nth_a, $nth_b, $nth_c { ... };
# Output:
# ===SORRY!===
# Unable to parse postcircumfix:sym<( )>, couldn't find final ')' at line 9
Rakudo doesn't implement the lol ("list of lists") form yet, and so cannot parse #a;#b;#c. For the same reason, zip doesn't have a form which takes three lists yet. Clearly the error message is less than awesome.
There isn't really a good workaround yet, but here's something that will get the job done:
sub zip3(#a, #b, #c) {
my $a-list = flat(#a.list);
my $b-list = flat(#b.list);
my $c-list = flat(#c.list);
my ($a, $b, $c);
gather while ?$a-list && ?$b-list && ?$c-list {
$a = $a-list.shift unless $a-list[0] ~~ ::Whatever;
$b = $b-list.shift unless $b-list[0] ~~ ::Whatever;
$c = $c-list.shift unless $c-list[0] ~~ ::Whatever;
take ($a, $b, $c);
}
}
for zip3(#a,#b,#c) -> $nth_a, $nth_b, $nth_c {
say $nth_a ~ $nth_b ~ $nth_c;
}
The multi-dimensional syntax (the use of ; inside parens) and zip across more than two lists both work, so the code originally posted now works (if you provide some real code rather than the { ... } stub block).