Day 2 covers method definition and it’s potential. Io manages to express quite a few interesting things in its minimal syntax.
The more interesting features of methods are the Message
meta information (the ability of a method invocation to introspect both the caller, callee, and the parameters), and the selective parameter evaluation, which supports the creation of new control operations (such as fancy loop or conditionals).
Exercises
In line with today’s topic, exercises are about defining and executing methods.
Fibonacci sequence
I change the exercise a bit: I defined the fibonacci sequence for Number, and it uses the number it is called on as the argument. So I compute 40 fib
rather than fib(40)
. For this reason I have to use self
when I want to refer to the original argument explicitly.
The recursive (and slow) method translated directly from the definition:
1
|
|
For the iterative method, I’m using a intermediate function with accumulators to build the result (I have easier time thinking in functional than imperative terms):
1 2 |
|
Finally, an iterative imperative method, with explicit looping (the iteration starts at 2 because the for
method iterates up to and including the upper bound):
1 2 3 4 5 6 7 8 9 10 |
|
Change /
to return 0 when divided by 0
First I save the original definition of /
, then I update the operator slot with the new definition:
1 2 |
|
Add up all the numbers in a two dimensional array
Assuming the array is implemented as a List
of list, the following invocation will sum the numbers:
1 2 |
|
The initial value is supplied explicitly; otherwise reduce
would use the first value, which is not a number but a list.
Alternatively (and much shorter):
1 2 |
|
Define myAverage
Using the reduce
method, it is easy to compute the sum of a list. The size
method can then be used to compute the average:
1 2 3 |
|
If the list is empty, the reduce
method returns nil
, so we get an exception (as nil
does not respond to the /
method). But this is consistent with the existing average
method.
Throwing Exception
Technically, this solution already raises an Exception
when one of the elements is not a Number
, but here is how I would implement explicit type checking:
1 2 3 4 5 6 7 8 9 10 11 |
|
I am using a new operator, +?
, with the same priority as +
, and explicitly check the prototype with hasProto
.
Two-dimensional list prototype
First I clone a specialization of List
as the implementation to the two dimensional array.
The initialization simply creates then grows the internal lists to the appropriate size, and stores the original parameters as slots:
1 2 3 4 5 6 7 |
|
The accessor methods can use the dimension slots to check for out of bound access:
1 2 3 |
|
The method checkBounds
guarantees an exception is raised if the position parameters are not within bounds. The method get
simply invokes at
twice to get at the data; set
first locate the right sub list with at
, then update the correct value with atPut
, and finally returns the updated array.
Two-dimensional list transpose method
With Dim2
defined as above, the transpose
method is trivial:
1 2 3 4 5 6 7 |
|
Just initialize a new array, swapping the dimensions, then iterate over both dimensions, swapping the parameters for the get
and the set
methods.
Matrix Input/Output
First the method asString
can be used to get the string representation of an object, including the two-dimensional list.
However, the default asString
returns the same representation as for regular nested lists. In order to read the object from the string, asString
has to be overridden to emit something specific.
1 2 3 4 5 6 7 8 |
|
With this defined, a two dimensional list has a unique representation:
1 2 |
|
With this in place, I can define a twodim
function that creates an instance of Dim2
and fills it with the passed data:
1 2 3 4 |
|
The twodim
method is not defined on Dim2
, but globally, so that the content of a string representing a Dim2
instance can be parsed in any context.
With this in place, the object can be serialized and unserialized, using the doString
method (which evaluates the string in the target context):
1 2 3 4 5 6 |
|
So the last step is to store the string representation in a file, and read from it:
1 2 3 4 5 6 |
|
Unsurprisingly, the content of the file is
1
|
|
Reading is just as simple:
1 2 3 4 5 6 |
|
The original Dim2
instance is equal to the unserialized one. I did not really expected that (I didn’t write any comparison code for the new object), but Io provided a sensible implementation anyway.
Note: theres is a serialized
method, but it’s output representation in the case of Dim2
is the same as the one for List
. There should be a way to override serialized
as well, but it’s exact semantic is not clear to me.
Guess a Number Game
Given the above, the last exercise a walk in the park. Getting the number from the standard input was a bit harder to figure out. For some reason, on Mac OS X, reading from the standard input also displays nil
; I guess it is a bug, although not a very serious one.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Wrapping up
Well, that was quite a day. The exercises did not cover the more advanced use of method (such as implicit argument evaluation), but otherwise gave the opportunity to define useful behaviours, and play with important classes from the standard library.