Create and populate an array of Raku objects? - raku

I chose to redesign a portion of the previous code of mine, in this case, a chessboard, in Perl 6. The first two classes went well (or at least worked, I know so little that I can't speak to their correctness), but I'm stuck with the third. Here is the code:
#!/home/hsmyers/rakudo741/bin/perl6
# board.p6 - Beginnings of a PGN toolset. And place to start learning
# Perl 6/Raku.
use v6d;
#!___________________________________________________________
constant $size = 4;
class Piece {
my Str #namesOfPieces[$size] = <
white-rook white-knight white-bishop white-queen
>;
my Str #abrevsOfPieces[$size] = <
R N B Q K B N R
>;
my Str #symbolsOfPieces[$size] = <
♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
>;
my Str #codeptsOfPieces[$size] = (
"\x2656", "\x2658", "\x2657", "\x2655",
);
has Str $.name;
has Str $.abrev;
has Str $.symbol;
has Uni $.codept;
submethod BUILD( :$i ) {
$!name = #namesOfPieces[$i];
$!abrev = #abrevsOfPieces[$i];
$!symbol = #symbolsOfPieces[$i];
$!codept = #codeptsOfPieces[$i].NFC;
}
}
class Square {
my Int #colors[$size] = <
1 0 1 0 1 0 1 0
>;
my Str #names[$size] = <
a1 b1 c1 d1 e1 f1 g1 h1
>;
has Int $.color;
has Int $.index;
has Str $.name;
has Piece $.piece;
submethod BUILD( :$i ) {
$!color = #colors[$i];
$!index = $i;
$!name = #names[$i];
$!piece = Piece.new(:i($i));
}
}
class Board is Array {
}
my $p = Piece.new(:i(0));
$p.say;
my $s = Square.new(:i(0));
$s.say;
#!___________________________________________________________
my #b := Board.new(
Square.new(:i(0)),
Square.new(:i(1)),
Square.new(:i(2))
);
say #b;
say #b.WHAT;
When run at the cli, results in:
Piece.new(name => "white-rook", abrev => "R", symbol => "♖", codept => Uni.new(0x2656).NFC)
Square.new(color => IntStr.new(1, "1"), index => 0, name => "a1", piece => Piece.new(name => "white-
rook", abrev => "R", symbol => "♖", codept => Uni.new(0x2656).NFC))
[Square.new(color => IntStr.new(1, "1"), index => 0, name => "a1", piece => Piece.new(name =>
"white-rook", abrev => "R", symbol => "♖", codept => Uni.new(0x2656).NFC)) Square.new(color =>
IntStr.new(0, "0"), index => 1, name => "b1", piece => Piece.new(name => "white-knight", abrev =>
"N", symbol => "♘", codept => Uni.new(0x2658).NFC)) Square.new(color => IntStr.new(1, "1"), index =>
2, name => "c1", piece => Piece.new(name => "white-bishop", abrev => "B", symbol => "♗", codept =>
Uni.new(0x2657).NFC))]
(Board)
The Board class (empty as it is) is all that is left from my attempts so far. Amazingly (at least to me), it provides a degree of workability. It has variously had a "new" and a "BUILD," neither provided a working solution. The current approach doesn't work, considering that the actual count will be 64 and not 4.
My current notion is that I need to build an array of 64 Squares, which in turn will create the necessary pieces. I've tried to add to self with nothing working. Suggestions?

Inheriting from Array is probably not the best design choice here; it reveals and commits to the underlying representation of the Board, which will present refactoring challenges as the code evolves. Rather, I'd suggest that a Board has an Array of Square, which is initialized with Square objects.
Assuming the board is meant to have $size squared places, then you could do something like:
class Board {
has #.squares[$size ** 2];
method TWEAK() {
#!squares = map { Square.new(i => $_ % $size) }, ^($size ** 2);
}
}
That is, take the range from 0 up to but excluding $size squared, and then map each value into a Square instance. (We modulo the index to avoid an index out of bounds in one of the other classes.)
A 2D array may be preferable:
class Board {
has #.squares[$size;$size];
method TWEAK() {
#!squares = (map -> $i { Square.new(:$i) }, ^$size) xx $size;
}
}
Here, we map again, but this time since we're just doing one dimension we drop the modulo. The use of a named $i parameter means we can use the :$i convenience, which is short for :i($i) (there's an opportunity to do that in the code you posted also). We then take that expression producing one row, and use xx to run it $size times in order to get data for every column.
Ultimately, it will probably not be quite so simple as this; perhaps Square should take two constructor arguments, both a numeric and a letter, to form its name. That's probably best done as a map of map. Further, the initialization of Piece instances probably wants to happen in Board too; while it's been a quarter of a century since I last played chess, I'm quite sure not every square has a piece on it at the start of the game.

Related

Printing mathematical series concisely in Raku

Mathematical series, take for example the consecutive sequence represented here as an array:
my #seq = my $a=0, {++$a} ... *;
for #seq[^10].kv {state $f=0; ($^k < 4 or $^k > 7) ?? say "a$^k = " ~ $^v !! (say "..." if $f ne 1; $f=1) };
Prints:
a0 = 0
a1 = 1
a2 = 2
...
a8 = 8
a9 = 9
1- Is there a simple way to drop just the first element i.e. a0 = 0 from the printed output?
2- Could this code be made more idiomatic?
This might be a bit more idiomatic:
my #seq = 0, *+1 ... *;
say #seq[^4], #seq[7..10]
You don't need to use a lexical variable within the sequence; either Whatever or placeholder variables can safely be used within sequences. Then you can simply select the elements of the sequence you want printed.
Which returns «(0 1 2 3)(7 8 9 10)␤»
You can skip the first N values on any Iterable or Sequence with skip:
for (^5).skip(3) {
.say
}
# 3
# 4
If you don't specify a number, it will skip only one element.
A barebones solution
Let's start with a very simple solution for printing a gist of a sequence. It doesn't deal with the specifics you've added to your question but it's a good starting point:
sub seq-range-gist ( #seq ) {
my #pairs = #seq.pairs;
join "\n", #pairs.head(3)».gist, '...', #pairs.tail(2)».gist
}
Unlike .kv, which converts its invocant into the form key1, value1, key2, value2, key3, value3, ..., i.e. 6 elements if its invocant contains 3 elements, .pairs converts its invocant into the form key1 => value1, key2 => value2, key3 => value3, ....
I used .pairs instead of .kv partly because it meant I could just use ».gist later on in the code to effortlessly get a nice key1 => value1 display for each element. We'll modify that below but this is a good idiomatic start.
The .head and .tail calls are the idiomatic way to create small lists of the first and last N elements from an invocant list (provided it's not lazy; more about that in a mo).
Given this initial solution, say seq-range-gist (0,1 ... Inf)[^10] displays:
0 => 0
1 => 1
2 => 2
...
8 => 8
9 => 9
Next, we want to be able to "drop just the first element ... from the printed output". Unfortunately say seq-range-gist (0,1 ... Inf)[1..9] displays:
0 => 1
1 => 2
2 => 3
...
7 => 8
8 => 9
We want the number on the left of the => to retain the numbering of the original sequence. To enable this we split the underlying sequence from the range that we want extracted. We add a second parameter/argument #range, and append [#range] to the second line of the sub:
sub seq-range-gist ( #seq, #range ) {
my #pairs = #seq.pairs[#range];
Now we can write say seq-range-gist (0,1 ... Inf), 1..9 to display:
1 => 1
2 => 2
3 => 3
...
8 => 8
9 => 9
In your question you used the format aINDEX = VALUE rather than INDEX => VALUE. To allow customization of the gist, we add a third &gist routine parameter/argument and invoke that instead of the built in .gist method:
sub seq-range-gist ( #seq, #range, :&gist ) {
my #pairs = #seq.pairs[#range];
join "\n", #pairs.head(3)».&gist, '...', #pairs.tail(2)».&gist
}
Note how the "method" invocations in the body of seq-range-gist sub are now .&gist, not .gist. The syntax .&foo invokes a sub &foo (which is typically invoked by writing just foo), passing the invocant on the left of the . as a $_ argument to the sub.
Note also that I've made the &gist parameter a named one by preceding it with a :.
So now say seq-range-gist (0,1 ... Inf), 1..9, gist => { "a{.key} = {.value}" } displays:
a1 = 1
a2 = 2
a3 = 3
...
a8 = 8
a9 = 9
Adding polish
The rest of this answer is bonus material for readers who care about polish.
say seq-range-gist (0, 1, 2, 3), ^3 displays:
0 => 0
1 => 1
2 => 2
...
1 => 1
2 => 2
Oops. And even if there were more pairs than the head and tail combined, so at least we didn't get repeated lines, it'd still be pointless using the head, ..., tail approach to elide just one or two elements. Let's change the last statement in the sub body to eliminate these issues:
join "\n",
#pairs < $head + $tail + 3 # Of course, the 3 is a bit arbitrary
?? #pairs».&gist
!! (#pairs.head($head)».&gist, '...', #pairs.tail($tail)».&gist)
Next, it would be nice if the sub did something useful if called without a range or gist. We can mostly fix that by giving the #range and &gist parameters suitable defaults:
sub seq-range-gist (
#seq,
#range = #seq.is-lazy ?? ^100 !! ^#seq,
:&gist = { .gist }
) {
If #seq is not lazy, then #range defaults to the full range of #seq. If #seq is infinite (in which case it's also lazy), then the upto 100 default is fine. But what if #seq is lazy but yields less than 100 defined values? To cover this case we append .grep: *.value.defined to the #pairs declaration:
my #pairs = #seq.pairs[#range].grep: *.value.defined;
Another simple improvement would be optional head and tail parameters, leading to a final polished solution:
sub seq-range-gist (
#seq,
#range = #seq.is-lazy ?? ^100 !! ^#seq,
:$head = 3,
:$tail = 2,
:&gist = { .gist }
) {
my #pairs = #seq.pairs[#range].grep: *.value.defined;
join "\n",
#pairs <= $head + $tail + 2
?? #pairs».&gist
!! (#pairs.head($head)».&gist, '...', #pairs.tail($tail)».&gist)
}
my #seq = my $a=0, {++$a} ... *;
my \i = 0;
say( 'a' ~ (i+$_) Z=> (i+$_) ) for #seq[^5];
print "------\n";
my \j = 1;
say( 'a'.succ ~ (j+$_) Z=> (j+$_) ) for #seq[^5];
Output:
(a0 => 0)
(a1 => 1)
(a2 => 2)
(a3 => 3)
(a4 => 4)
------
(b1 => 1)
(b2 => 2)
(b3 => 3)
(b4 => 4)
(b5 => 5)
I recognize the above doesn't include your 'bespoke' gist ellipsis conditional ( ($^k < 4 or $^k > 7) ), but you seem to have come up with a more elegant way of writing that in the comments. Still (if you don't want to use skip), number keys yourself and include an offset such as i or j indicating how many elements of #seq you wish to skip.
Addendum: Below is an attempt at implementing your 'bespoke' gist ellipsis conditional (using grep):
my #seq = my $a=0, {++$a} ... *;
my \i = 0; my \m = 4; my \n = 7;
do for #seq[^10].grep({4 > $_ or $_ > 7 }) {
say 'a' ~ (i+$_) Z=> (i+$_);
if $_ == 3 {print "...\n"};
}
Output when \i = 0:
(a0 => 0)
(a1 => 1)
(a2 => 2)
(a3 => 3)
...
(a8 => 8)
(a9 => 9)
Output when \i = 1:
(a1 => 1)
(a2 => 2)
(a3 => 3)
(a4 => 4)
...
(a9 => 9)
(a10 => 10)

How to avoid importing default symbols when using a module?

I am going to use Hash::Merge as an example. Consider:
use v6;
use Hash::Merge; # <-- imports all symbols marked with "is export" from Hash::Merge
my %hash1 = a1 => [1, 2, 3], b => "xxx", c => { ca => 1 }, e => 5;
my %hash2 = a1 => [1, 5, 3], b => "yyyy", c => { ca => 5, f => "a" }, d => 4;
my %res = merge-hash(%hash1, %hash2, :no-append-array);
Suppose I do not want to pollute my name space when using a module (here Hash::Merge is used as an example). I could achive this in Perl 5 by specifying an empty argument list to use:
use Hash::Merge (); # <-- No symbols will be imported into the current namespace
Then I would call the sub routine merge-hash using its fully qualified name:
Hash::Merge::merge-hash.
According to this bug report it seems like this is not possible in Perl 6. Is this correct?
To load a module without importing, use need instead:
need Hash::Merge;
In the case of the module in question, it does not declare the things it exports with our, which unfortunately means that calling it as:
Hash::Merge::merge-hash(...)
Will not work, since it's not installed in the package. However, it is still possible to dig the symbol out of the exports manually:
need Hash::Merge;
say Hash::Merge::EXPORT::DEFAULT::merge-hash({ a => 1 }, { b => 2 })
And, for more convenience, it can be aliased:
need Hash::Merge;
my constant &merge-hash = &Hash::Merge::EXPORT::DEFAULT::merge-hash;
say merge-hash({ a => 1 }, { b => 2 });
There is a speculated syntax along the lines of use Hash::Merge :MY<&merge-hash>, which is not implemented in current Perl 6 versions, but would probably have the same semantics as the constant trick shown here.
A simple way to deal with this is to just put the use of the module in a block.
{ use Hash::Merge }
Since the {} defines a scope, nothing escapes it.
You can get it so that something can escape by placing it in a do block.
do { use Hash::Merge }
What you can do then is have it so that the values you care about get stored in the correct places.
my &merge-hash = do { use Hash::Merge; &merge-hash }
my (&merge-hash,&merge-hashes) = do { use Hash::Merge; (&merge-hash, &merge-hashes) }
Another option is to just place it in as small a scope as possible.
my %a = a => 1;
my %b = b => 2;
my %c;
{
use Hash::Merge;
%c := merge-hash %a, %b
}
or
my %c := do {
use Hash::Merge;
merge-hash %a, %b
}
(The binding operator := was just used because the result of merge-hash is already a hash.)

Performing double induction in Welder

I'm trying to proof a property by double induction using Welder. The definitions are taken from here. A related question that gives more details of the theory can be found here. Anyways I just need some portion to show my problem:
Basically, I'm working with expressions that take the form of an integer, POP(i,p) and POW(i,p,q). There is a property of normality on them call it n. I want to proof that if n(x) && n(y) then n(x+y).
Let's look at the specific case x = POP(i,p), y = POP(j,q) then x+y is defined as follows:
if i = j then pop(i,p+q)
if i > j then pop(j,POP(i-j,p)+q)
if i < j then pop(i,POP(j-i,q)+p)
where pop is a function that mimics POP constructs with some slight differences.
I perform the proof by double induction in Welder as follows:
def property(x: Expr) = {
forall("y" :: shf){ case (y) =>
(n(x) && n(y)) ==> n(x+y)
}
}
structuralInduction(property _, "x" :: shf) { case (ihs1, goal1) =>
val xi = ihs1.expression
xi match{
...
The relevant case I want to focus is the following:
case C(`POP_ID`,i,pshf) =>
def popproperty(y: Expr) = {
n(y) ==> n(xi+y)
}
structuralInduction(popproperty _, "y" :: shf) { case (ihs2, goal2) =>
val yi = ihs2.expression
implI(n(yi)){ axioms2 =>
yi match{
case C(`constshfID`, fc) => andI(ihs1.hypothesis(pshf),axioms1)
case C(`POP_ID`,j,qshf) =>
andI(
implE(forallE(normpop1Lemma)(i,normadd(pshf,qshf)))( g =>
andI(implE(forallE(ihs1.hypothesis(pshf))(qshf))( g =>
andI(axioms1,axioms2)), axioms1, axioms2)),
implI(i > j){ gt =>
implE(forallE(normpop1Lemma)(i,normadd(POP(i-j,pshf),qshf)))( g =>
andI(implE(ihs2.hypothesis(qshf))(g => axioms2),axioms1,axioms2,gt))
},
implI(i < j){ lt =>
implE(forallE(normpop1Lemma)(i,normadd(POP(j-i,pshf),qshf)))( g =>
andI(implE(ihs2.hypothesis(qshf))(g => axioms2),axioms1,axioms2,lt))
}
)
Here normpop1Lemma states that for having n(pop(i,p)) you need i to be natural and p normal. However, I find that the second case is not proved. In fact I would need to generalize the second property to
def popproperty(y: Expr) = {
forall("x" :: shf){
n(y) ==> n(x+y)
}
}
but then am I not breaking induction? Can I actually solve the cases i > j and i < j by doing so? (more to come while I experiment)
Edit
Currently, I can induct first on y and then on x and for the POP-POP case I can show the cases where i = j and i > j but the i < j is not. I thought it could work by using that POP(j-i,q) + p = p + POP(j-i,q) but it doesn't.
Instead, now I'm trying to proof two different properties assuming in each that the one of the cases cannot hold (either the i < j or the i > j).
Hmm, I would expect your proof to look something more like this:
structuralInduction((x: Expr) =>
forall("y" :: shf)(y => (n(x) && n(y)) ==> n(x+y)), "x" :: shf
) { case (ihs1, g1) =>
structuralInduction((y: Expr) =>
(n(ihs1.expression) && n(y)) ==> n(ihs1.expression+y), "y" :: shf
) { case (ihs2, g2) =>
implI(n(ihs1.expression) && n(ihs2.expression)) { normalXY =>
(ihs1.expression, ihs2.expression) match {
case (C(`POP_ID`,i,pshf), C(`POP_ID`,j,qshf)) => andI(
... // case (i == j)
... // case (i > j)
implI(i < j) { iLtJ =>
andI(
... // stuff using normprop1Lemma
implE(forallE(ihs1.hypothesis(pshf))(normadd(POP(j-i,qshf)) {
g => // the reason why n(normadd(POP(j-i,qshf)) and n(pshf)
},
... // invoke some lemma showing x+y == y+x
)
}
)
}
}
}
}
Here we use the induction hypothesis from the outer induction since we're performing induction on p \in x. I assume normprop1Lemma is telling you that normadd(POP(j-i,qshf)) is in normal form. You will probably need some lemma stating that p \in x is in normal form if x is in normal form.
Hope this helps!

How do I construct an object in Perl6 from a hash?

In Perl5 you can do something like this:
#!/usr/bin/env perl
use 5.010;
package Local::Class {
use Moo;
has [qw( x y )] => ( is => 'ro');
sub BUILDARGS { shift; return (#_) ? (#_ > 1) ? { #_ } : shift : {} }
}
use Local::Class;
# Create object directly
my $x = Local::Class->new( x => 1, y => 10 );
say $x->x, ' ', $x->y; # 1 10
# Arguments from a hash
my %hash = ( x => 5, y => 20 );
$x = Local::Class->new(%hash);
say $x->x, ' ', $x->y; # 5 20
# Arguments from a hash reference
$x = Local::Class->new(\%hash);
say $x->x, ' ', $x->y; # 5 20
The two calls in the bottom work the same because of the custom BUILDARGS method, which basically turns them both into the sort of hash references expected by Moo(se)?.
But how can I do the same in Perl6?
#!/usr/bin/env perl6
class Local::Class {
has $.x;
has $.y;
}
my $x;
# This works
$x = Local::Class.new( x => 1, y => 10 );
say $x.x, ' ', $x.y; # 1 10
# This doesn't
my %hash = %( x => 5, y => 20 );
$x = Local::Class.new(%hash);
# This doesn't either
$x = Local::Class.new(item(%hash));
# Both die with:
# Default constructor for 'Local::Class' only takes named arguments
So how can I take a hash that has been created elsewhere, and convert it into the sort of named arguments needed by the default constructor of a class?
Using the default constructor
The default .new constructor maps named arguments to public attributes.
In your example, you pass a hash as a positional argument. You can use the | syntax to interpolate the hash entries into the argument list as named arguments:
$x = Local::Class.new(|%hash);
However, note that this will cause problems if your class has an array attribute like has #.z:
class Local::Class {
has $.x;
has $.y;
has #.z;
}
my %hash = x => 5, y => 20, z => [1, 2];
my $x = Local::Class.new(|%hash);
say $x; # Local::Class.new(x => 5, y => 20, z => [[1, 2],])
This is because like all hashes, %hash places each of its values in an item container. So the attribute will be initialized as #.z = $([1, 2]), which results in an array of a single element which is the original array.
One way to avoid this, is to use a Capture instead of a Hash:
my $capture = \( x => 5, y => 20, z => [1, 2] );
my $x = Local::Class.new(|$capture);
say $x; # Local::Class.new(x => 5, y => 20, z => [1, 2])
Or use a Hash but then de-containerize its values with <> and turn the whole thing into a Map (which, unlike a Hash, won't add back the item containers) before interpolating it into the argument list:
my %hash = x => 5, y => 20, z => [1, 2];
my $x = Local::Class.new(|Map.new: (.key => .value<> for %hash));
say $x; # Local::Class.new(x => 5, y => 20, z => [1, 2])
Using a custom constructor
If you'd rather want to deal with this in the class itself rather than in code that uses the class, you can amend the constructor to your liking.
Note that the default constructor .new calls .bless to actually allocate the object, which in turn calls .BUILD to handle initialization of attributes.
So the easiest way is to keep the default implementation of .new, but provide a custom .BUILD. You can map from named arguments to attributes directly in its signature, so the body of the BUILD routine can actually stay empty:
class Local::Class {
has $.x;
has $.y;
has #.z;
submethod BUILD (:$!x, :$!y, :#!z) { }
}
my %hash = x => 5, y => 20, z => [1, 2];
my $x = Local::Class.new(|%hash);
say $x; # Local::Class.new(x => 5, y => 20, z => [1, 2])
Binding an array-in-an-item-container to a # parameter automatically removes the item container, so it doesn't suffer from the "array in an array" problem described above.
The downside is that you have to list all public attributes of your class in that BUILD parameter list. Also, you still have to interpolate the hash using | in the code that uses the class.
To get around both of those limitations, you can implement a custom .new like this:
class Local::Class {
has $.x;
has $.y;
has #.z;
method new (%attr) {
self.bless: |Map.new: (.key => .value<> for %attr)
}
}
my %hash = x => 5, y => 20, z => [1, 2];
my $x = Local::Class.new(%hash);
say $x; # Local::Class.new(x => 5, y => 20, z => [1, 2])

Should there be an indicesWhere method on Scala's List class?

Scala's List classes have indexWhere methods, which return a single index for a List element which matches the supplied predicate (or -1 if none exists).
I recently found myself wanting to gather all indices in a List which matched a given predicate, and found myself writing an expression like:
list.zipWithIndex.filter({case (elem, _) => p(elem)}).map({case (_, index) => index})
where p here is some predicate function for selecting matching elements. This seems a bit of an unwieldy expression for such a simple requirement (but I may be missing a trick or two).
I was half expecting to find an indicesWhere function on List which would allow me to write instead:
list.indicesWhere(p)
Should something like this be part of the Scala's List API, or is there a much simpler expression than what I've shown above for doing the same thing?
Well, here's a shorter expression that removes some of the syntactic noise you have in yours (modified to use Travis's suggestion):
list.zipWithIndex.collect { case (x, i) if p(x) => i }
Or alternatively:
for ((x,i) <- list.zipWithIndex if p(x)) yield i
But if you use this frequently, you should just add it as an implicit method:
class EnrichedWithIndicesWhere[T, CC[X] <: Seq[X]](xs: CC[T]) {
def indicesWhere(p: T => Boolean)(implicit bf: CanBuildFrom[CC[T], Int, CC[Int]]): CC[Int] = {
val b = bf()
for ((x, i) <- xs.zipWithIndex if p(x)) b += i
b.result
}
}
implicit def enrichWithIndicesWhere[T, CC[X] <: Seq[X]](xs: CC[T]) = new EnrichedWithIndicesWhere(xs)
val list = List(1, 2, 3, 4, 5)
def p(i: Int) = i % 2 == 1
list.indicesWhere(p) // List(0, 2, 4)
You could use unzip to replace the map:
list.zipWithIndex.filter({case (elem, _) => p(elem)}).unzip._2