Monday, May 10, 2010

Arising from Slumber

Sorry for falling off the world of Iron Man; life has been busy.

Seeing the new proto web page got me thinking I should try to bring Vector and ABC forward into the world of the current Rakudo. Okay, make clean and make work fine for Vector. make test fails every test.

So I started in with 01-basics.t. It seems to be having trouble with the custom Unicode operators. Then I remembered seeing something on #perl6 about using our with custom operators. So I added it to both the dot product operators (one of them the Texas version, "dot"). And got the following error:

error:imcc:syntax error, unexpected '\n'
in file 'EVAL_5' line -1061556260

Perl 6 experts, help?

Sunday, March 21, 2010

Apologies and Vector Again

In my last post I griped a bit about the lack of progress on several of the issues that have been bothering me. Well, it turns out there is a sad explanation for why things have been slow. I came into the Perl 6 sphere after the previous cancer bout referred to here, so this was a complete surprise for me. Of course Patrick's wife's health is vastly more important than getting my toy program working. I wish them both all the best.

In the meanwhile, while that stuff hasn't been fixed, there's apparently been a huge wave of progress with the metaops in the last week -- enough so that I thought it might be worth trying Vector again. And, well... It blew up compiling Vector.pm with a pretty obscure error message. Pretty easy to guess the source of the problem, though:


> my @a = 1..3; my @b = 3..5; say @a >>*<< @b
3815
> my @a = 1..3; my @b = 3..5; say @a »*« @b
Confused at line 1, near "say @a \x{c2}\x{bb}*"

So Texas hyper-ops work, but the proper ones do not. Well, that's an easy enough change.

Hmmm... next issue: is also is history. It's augment now. That's easy enough. And then you get Can't augment class Vector without 'use MONKEY_TYPING'. Which is also easy to fix.

Next error, though is

error:imcc:syntax error, unexpected '\n'
in file 'EVAL_5' line 58
Contextual $*PKGDECL not found


Ummm... I've got nothing. Any one have an idea what this might mean?

Thursday, March 11, 2010

Progress and Congress

So, it's was interesting to see the relative receptions of my last two posts. Laziness, the Catch caused a massive flurry on discussion on #perl6, and eventual plans for how to overhaul Perl 6 and Rakudo to deal with it. As far as I know, though, there's been no development in this area in Rakudo, so the scary bug is still very much present.

On the other hand, the Lazy Sieve of Eratosthenes gather / take bug is generally acknowledged, but doesn't have any momentum towards a patch at all, as far as I can see from #perl6.

On the gripping hand, at least a few of the file operators (another issue with the E03 update) now work, thanks to lue++.

It does seem like a fantastic amount of progress is being made on Rakudo at the moment. I just hope these issues get addressed before Rakudo Star.

Wednesday, March 3, 2010

Lazy Sieve of Eratosthenes, sidetracked

A Hacker News article about the Sieve of Eratosthenes got me thinking about how to do a lazy, infinite Sieve in Perl 6. (I didn't read the whole article, because I wanted to produce my own implementation first!) Unfortunately, my attempt to do so turned up what seems to be a very major bug lurking in Rakudo's gather / take.

Basically, my notion was this. The Sieve essentially consists of crossing off multiples from a list each time you find a new prime. So what if instead of taking the list to be a fixed limited, fixed array of numbers, you made it an actual infinite lazy list? All you need is a function which, given two infinite lists of numbers, takes the least element at the front of the lists and returns that. Then you when find a new prime, you take your existing list and use that function to merge in the list of multiples of the new prime.

So I started to code this up, and instantly ran into weirdness.

sub sorted-infinite-sequence-merge(Iterator $a-iterator, Iterator $b-iterator)
{
my $a = $a-iterator.get;
my $b = $b-iterator.get;

gather loop {
my $next = $a min $b;
# say "$a, $b, $next";
take $next;
$a = $a-iterator.get if $a == $next;
$b = $b-iterator.get if $b == $next;
}
}

sorted-infinite-sequence-merge((1..100).iterator.map({$_*3}), (1..100).iterator.map({$_*4})).batch(20).perl.say;
sorted-infinite-sequence-merge((3, 6 ... *), (4, 8 ... *)).batch(20).perl.say


That code generates the following two lists:

(3, 4, 6, 8, 9, 12, 15, 16, 18, 20, 21, 24, 27, 28, 30, 32, 33, 36, 39, 40)
(4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80)


As you can see, the first list is clearly correct. The second list (which calls the same merge function, but using source lists generated via series instead of Range) is clearly incorrect. I believe this must represent some sort of gather/take bug triggered by nested gather/take operations.

Help?

Saturday, February 27, 2010

E03: Laziness: The Catch

So, jnthn++ fixed the Nominal type check failed for parameter '@a'; expected Positional but got GatherIterator instead issue. Errr, sort of. Here's my sum sub:


sub sum(@a) {
+@a ?? @a.reduce({ $^a + $^b }) !! 0;
}


Now it does get called properly if you say sum(@costs.grep({$_ >= 1000})). But even though it appears to be a very simple function, it doesn't work when called this way! The problem, as far as I can tell, is that @a may look like a Seq or an Array, it's still actually an iterator. That means the first time you use it for something like, say, +@a, it is completely consumed! Then the next time you access it, there's nothing left. This is definitely not what is expected...

Update: Just realized that without "Perl 6", Ironman didn't pick up the previous post or this one. Time to edit...

E03 in Master

So, I thought porting my E03 script to run on the current Rakudo master would make a good topic for a post. So, what did I have to change?

1. The code wouldn't parse with the metaoperators in it. So they had to go. Sob!

2. Symbol '$is_active_bit' not predeclared. Okay, replace constant with my.

3. No applicable candidates found to dispatch to for 'subst'. Well, that never actually worked properly in alpha, either. Easy enough to replace this with map and a new sub.

4. Method 'e' not found for invocant of class 'Str'. Ack. I just commented out these calls (I presumed :s wouldn't work either).

5. := binding of variables not yet implemented. Switching this to = --- errr --- breaks the code. Guess I'll have to fix that in a minute.

6. Method 'count' not found for invocant of class 'Perl6MultiSub'. Blast. This was triggered by my new code
reduce(&infix:<+>)
. Easy enough to change it to reduce({ $^a + $^b }).

7. Cannot reduce() empty list. Arrrgh! Turns out duplicating what [+] does is harder than it looks. I added a sub sum to cleanly check for the empty list.

8. Nominal type check failed for parameter '@a'; expected Positional but got GatherIterator instead. Oh, no. This is some sort of internal issue showing through, and I have no idea how to fix it.

Guess I will stop there for now, because I don't know how to go further.

sub add_trailing_slash_if_needed($path) {
$path ~~ / \/ $ / ?? $path !! $path ~ '/';
}

sub load_data($filename, $version = 1, *@dirpath is copy) {
@dirpath = './', 'peter' unless +@dirpath;
@dirpath .= map({ add_trailing_slash_if_needed($_) });
say @dirpath.join("\n");

my %data;
for @dirpath -> $prefix {
my $filepath = $prefix ~ $filename;

# if ($filepath ~~ :e and 100 < ($filepath ~~ :s) <= 1e6)
{
say "Trying to open $filepath";
my $fh = open($filepath, :r)
or die "Something screwy with $filepath: $!";
my ($name, $vers, $status, $costs) = $fh.lines(4);
next if $vers < $version;
$costs = [split /\s+/, $costs];
%data{$filepath} = {};
%data{$filepath}<name vers stat costs rest> =
($name, $vers, $status, $costs, $fh.slurp);
say "$filepath done";
$fh.close;
}
}
return %data;
}

sub save_data(%data) {
for %data.kv -> $filepath, $data {
say "saving $filepath";
my $fh = open($filepath, :w)
or die "Something screwy with $filepath: $!";
$fh.print: ($data.<name vers stat>, ~($data.<costs>), $data.<rest>).join("\n");
$fh.close;
}
}

# I've no idea what this sub was supposed to do, so let's stick with something really
# simple for the moment.
sub amortize($a) {
$a;
}

sub sum(@a) {
+@a ?? @a.reduce({ $^a + $^b }) !! 0;
}

my %data = load_data(filename=>'weblog', version=>1);
my $is_active_bit = 0x0080;
for %data.kv -> $file, $data {
say "$file contains data on { $data<name> }";
$data<stat> +^= $is_active_bit;

# my @costs := $data<costs>;
my @costs = $data<costs>;
my $inflation = 0;
$inflation = prompt 'Inflation rate: '
until $inflation > 0;

# @costs = (@costs >>*>> $inflation).sort({ amortize($_) });
@costs = @costs.map({ $_ * $inflation }).sort({ amortize($_) });

# say "Total expenditure: { [+] @costs }";
# say "Major expenditure: { [+] @costs.grep({$_ >= 1000}) }";
# say "Minor expenditure: { [+] @costs.grep({$_ < 1000}) }";

say "Total expenditure: { sum(@costs) }";
say "Major expenditure: { sum(@costs.grep({$_ >= 1000})) }";
say "Minor expenditure: { sum (@costs.grep({$_ < 1000})) }";
say "Odd expenditures: { @costs.map(-> $a, $b { $a }) }";
}

# save_data(%data, log => {name=>'metalog', vers=>1, costs=>[], stat=>0});
save_data(%data);


Update: Adding the words "Perl 6" in text so Ironman picks it up. Sigh.

Saturday, February 20, 2010

E03 Second Stab

As usual, masak++ has some brilliant stuff in his take on the E03 challenge, which I am shamelessly borrowing for my second version. On the other hand, I think he has done himself a disservice by not making sure his code actually works in Rakudo, as I see several dodgy spots and an entire missing function in his version.

So, some notes on masak's code:
1) His default value for @dirpath is wonky. First, he seems to be assigning @std_dirpath to @last_dirpath if the former is undefined. Second, neither of those variables is actually declared. Third, he's left off the '.' case, which is the one that actually works.

2) On the plus side, he has a working regular expression for the subst, and includes the assignment operator I forgot (in my comment version). On the minus side, the right hand side of the substitution is bad -- it works in theory, but does not work in practice in Rakudo alpha (nor master, so far as I know).

3) I'm amused that he used comb instead of the original's split. Both lines have the exact same effect, so far as I know. Arguably his has a little more style than mine.

4) I'm interested that he didn't notice that %data{$filepath}{"filepath"} = $filepath is kind of redundant.

5) Kudos for remembering how to declare a constant. But without the seek method, @StartOfFile is never actually used in the code.

6) His save_data is kind of drastically more complicated than mine, because he didn't just read the rest of the file in load_data and store it. He also uses lines instead of slurp, which makes writing out the data a bit more complicated as well. (Are those .list statements really necessary?)

7) He relies on the E03's explanation that a Str converted to Num will return NaN if it is not a valid number. That's certainly not true of Rakudo alpha. I'm not clear if it's true in the Perl 6 spec or not. (Of course, I skipped that bit altogether...)

So here's my second version. I cribbed a few things from masak's version, and added an arbitrary second default directory so I could make sure that it handled having more than one file.

sub load_data($filename, $version = 1, *@dirpath is copy) {
@dirpath = './', 'peter/' unless +@dirpath;
@dirpath>>.=subst(/(<-[/]>)$/, {"$1/"}); # doesn't actually work in Rakudo alpha
say @dirpath.join("\n");

my %data;
for @dirpath -> $prefix {
my $filepath = $prefix ~ $filename;

if ($filepath ~~ :e and 100 < ($filepath ~~ :s) <= 1e6) {
say "Trying to open $filepath";
my $fh = open($filepath, :r)
or die "Something screwy with $filepath: $!";
my ($name, $vers, $status, $costs) = $fh.lines(4);
next if $vers < $version;
$costs = [split /\s+/, $costs];
%data{$filepath} = {};
%data{$filepath}<name vers stat costs rest> =
($name, $vers, $status, $costs, $fh.slurp);
say "$filepath done";
$fh.close;
}
}
return %data;
}

sub save_data(%data) {
for %data.kv -> $filepath, $data {
say "saving $filepath";
my $fh = open($filepath, :w)
or die "Something screwy with $filepath: $!";
$fh.print: ($data.<name vers stat>, ~($data.<costs>), $data.<rest>).join("\n");
$fh.close;
}
}

# I've no idea what this sub was supposed to do, so let's stick with something really
# simple for the moment.
sub amortize($a) {
$a;
}

my %data = load_data(filename=>'weblog', version=>1);
constant $is_active_bit = 0x0080;
for %data.kv -> $file, $data {
say "$file contains data on { $data<name> }";
$data<stat> +^= $is_active_bit;

my @costs := $data<costs>;
my $inflation = 0;
$inflation = prompt 'Inflation rate: '
until $inflation > 0;

@costs = (@costs >>*>> $inflation).sort({ amortize($_) });

say "Total expenditure: { [+] @costs }";
say "Major expenditure: { [+] @costs.grep({$_ >= 1000}) }";
say "Minor expenditure: { [+] @costs.grep({$_ < 1000}) }";
say "Odd expenditures: { @costs.map(-> $a, $b { $a }) }";
}

# save_data(%data, log => {name=>'metalog', vers=>1, costs=>[], stat=>0});
save_data(%data);