As I mentioned in my last Axum post, the way I like to get the hang of a new language is to do the first 15 Euler challenges in that language.
Whilst this rarely helps me become proficient in any new language, rather highlight how well I can write C# in any paradigm, it does help me get familiar with the keywords and feel of that language
I am, however, fairly keen this time around to not just play code golf, rather to explore the possibilities, traps and best practices of Axum
The first Euler problem is as follows:
"Add all the natural numbers below one thousand that are multiples of 3 or 5."
It is not a challenging tricky problem, I admit, through they do get progressively tougher.
I was fairly sure that my first attempt at the code would not be particularly successful, and indeed I purposely wrote the code not to be too defensive. I wanted to see what worked and what broke.
My initial design was to have a Totals agent receiving numbers and a ModSwitch agent determining whether or not a given number met the rule of modulous 3 or 5
The totals agent will request the ModSwitch agent to assess whether a number should be added to the total, then add it to its output if it is valid.
Thinking about this code before we run it, we can see that the call to the ModSwitch agent will be blocking (remember receives are blocking), and will not lend us any performance gain.
Secondly, it is not initally evident that Totals agent will be done adding totals by the time the output is requested. True, in theory the receive should be blocking until such time as the Agent has a value at its output port, but this code is assigning it after each iteration.
Finally, how will the instance of the Totals agent cope with being assigned multiple inputs in a tight loop? Thinking about it, as the Totals agent should block each time it has performed an operation, and new inputs queue-up until ready.
My first try is below, and I emphasise It Doesn't Work, at least not as planned. I will discuss its output (sadly: "0" although Console Writes do show that it is generating the correct answer), in my next post.
using System;
namespace Euler1
{
channel CModSwitch
{
input int value;
output bool isValid;
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Euler1
{
private agent AModSwitch : channel CModSwitch
{
public AModSwitch()
{
while(true)
{
int value = receive(PrimaryChannel::value);
bool valid = (value % 3 == 0 || value % 5 == 0);
Console.WriteLine("{0} % 3 || 5 == 0 : {1}", value, valid);
PrimaryChannel::isValid <-- valid;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Euler1
{
channel CTotals
{
input int value;
output int sum;
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Euler1
{
private agent ATotals : channel CTotals
{
public ATotals()
{
int _totals = 0;
var modSwitch = AModSwitch.CreateInNewDomain();
while(true)
{
int value = receive(PrimaryChannel::value);
modSwitch::value <--value;
if (receive(modSwitch::isValid))
{
Console.WriteLine("Adding {0} to total = {1}", value, _totals + value);
_totals += value;
}
PrimaryChannel::sum <-- _totals;
}
}
}
}
using System;
using Microsoft.Axum;
using System.Concurrency.Messaging;
namespace Euler1
{
public domain Program
{
agent MainAgent : channel Microsoft.Axum.Application
{
public MainAgent()
{
String [] args = receive(PrimaryChannel::CommandLine);
var adder = ATotals.CreateInNewDomain();
for (int i=0; i<1000; i++)
{
Console.WriteLine("Looping {0}", i);
adder::value <-- i;
}
Console.WriteLine("Total = {0}", receive(adder::sum));
}
}
}
}
