Monday, November 26, 2012

Mimicking cout and Java strings in Julia

If you're coming from C++, perhaps you're going to miss cout or some of those fancing ostream stuff in fstream. In Julia, you have print and println, but you have to enclose everything in brackets and the ',' seperation is not really what you're used to when you're coming from C++. I also always enjoyed how easy it is in Java to compose strings. Luckily, it is very easy to incorporate the ostream and String behaviour into Julia, and it also shows some of the nice features of Julia.

cout << "Hello, Julia!\n";

First, we're going to create a singleton named cout. There is a special syntax for singletons in Julia (see here): a type without members.
Next, we'll need a function which handles the operator <<. This works much like in C++, since operators are functions. The function will just take the other argument, and print it:

# our coutType singleton
type coutType
cout = coutType()

# the operator <<
function << (c::coutType, t)
    return cout

Here we have to apply the same mechanism as in C++: The operator << returns the output object so that chained <<'s are possible. Now we can try our code:

julia> cout << "Hello, Julia! It is " << 
       time()/3600/24/365 << " years since epoch.\n";
Hello, Julia! It is 42.93542962147783 years since epoch.

If you omit the ; at the end, you still have valid julia code, but in the julia shell you will get an output like coutType(XXXX) after your output (in loaded files you won't). That's because the julia shell automatically displays the return values of the last statement. ; is the chained expressions operator , so by inserting a ; at the end you are inserting an empty instruction, which has no return type.
Note that you have to enclose temporary calculations in brackets like for cout << (1+2) << " is " << 3 << "\n"; .

"composing " + "Strings"

Next we want to have something to use strings as in Java: a = "it's the year " + 2012 + "!". For this, we need to specify a function for +:
# + for strings with variable number of arguments
function +(a::ASCIIString, tArgs...)
    for t in tArgs
        a = strcat(a,t)
    return a

Here we used Julias nice vargs ability combined with the fact that something like "a"+2+"c" is translated in + ("a", 2, "b").


Of course you can apply the cout idea to other streams, here's for files:
# << operator for files
function << (out::IOStream, t)
   write(out, t)
   return out
# .<< operator for files for writing data stringified (e.g. 2 as "2")
function .<< (out::IOStream, t)
    write(out, string(t))
    return out

And again an example:

myFile=open("year.txt", "w")
year = 2012
myFile << "It is the year " .<<  year << ".\n"

Notice the use of the .<< operator here so 2012 is translated to the string "2012" before being written to the file (otherwise it will write 2012 as binary).

split ostreams

Another idea is to create objects that stream into the console and into a file, which is useful for logging and similar tasks:

type loggerType
logger = loggerType(open("log.txt", "w"))

function << (out::loggerType, t)
    out.file .<< t
    cout << t
    return out    

year = 2012
logger << "Still the year " << 2012 << "!\n"


No comments:

Post a Comment