On this second day of Io language tutorials the first exercise is a classic
First question
- A Fibonacci sequence starts with two 1s. Each subsequent number is the sum of the two numbers that came before: 1, 1, 2, 3, 5, 8, 13, 21, and so on. Write a program to find the nth Fibonacci number. fib(1) is 1, and fib(4) is 3. As a bonus, solve the problem with recursion and with loops.
For this we decide to create a new object
Fibonacci := Object clone
In this we'll implement the solution in a recursive and non recursive way.
The non recursive way consists in a trivial use of loops by accumulating the previous two iterations of the Fibonacci sequence
Fibonacci nrec := method( n,
if(n == 1 or n == 2,return( 1 ))
n1 := 1
n2 := 1
n0 := n1 + n2
for( i, 3, n, 1,
n0 = n1 + n2
n2 = n1
n1 = n0
)
return(n0)
)
Looking into the for loop we can see that we start iterating in 3 with an increment of 1 each time. This way it will not run unless the n>=3
The recursive method has the same principle but the loop is replaced by a recursive call
Fibonacci rec := method(n,
if(n==1 or n==2, return(1))
return(rec(n-1)+rec(n-2))
)
In this case we have the loop doing backwards until it reaches the base cases when n1 or n2
Finally we can check the results by calling
for(i,1,10, Fibonacci nrec(i) println)
for(i,1,10, Fibonacci rec(i) println)
Second question
- How would you change / to return 0 if the denominator is zero?
For this second challenge all we got to do is
to find the original operator associated with the object Number and then replace it from a version that handles correctly the case in which the 0 is on the denominator
originalOperator := Number getSlot("/")
With the previous code we assign the original / operator into the slot originalOperator now the only thing we need to do is the redefinition of the original operator by doing
Number / := method (i,
if(i==0, 0,originalOperator(i))
)
Finally all we need to do is to check if everything is working as expected
10 /0 println
1 / 2 println
Third question
- Write a program to add up all of the numbers in a two-dimensional array.
To this we decide to tackle the problem again by creating a object to encapsulate the operations, this was called Matrix
Matrix := Object clone
To store the bidimentional array e decide to create a slot called data in the Matrix object
Matrix data := List clone
However for the currently question all we got to do is to define a method that we call sumArray on the Matrix object
Matrix sumArray := method(
marray,acc := 0;
marray foreach(v,acc=acc + v sum); return(acc)
)
To check if all this is working we can test it by doing
m1 := Matrix clone
m1 data =list(list(1,2,3),list(3,4,5))
m1 sumArray(m1 data) println
All we've done was to create a Matrix object called m1 and fetch the data slot with a bidimentional array and then fetching it to the method that we had created
Fourth question
- Add a slot called myAverage to a list that computes the average of all the numbers in a list. What happens if there are no numbers in a list? (Bonus: Raise an Io exception if any item in the list is not a number.)
In this question we create a new slot in the Matrix object and call it myAverage to compute the average of the bidimentional array.
Matrix myAverage := method(
l,acc := 0;
l foreach(v,
if(v type != Number type,
Exception raise("Element not a number"),
acc=acc+v))
return(acc/(l size))
)
The method is straightforward. All it do is to iterate over the list, check if it is a number, and then compute the average. The only new thing was the introduction of the
Exception raise("Element not a number")
when the element on the list don't match a Number object signature
Again, for testing purposes, all we need to do is try something like this
m1 myAverage(list(1,10,3)) println
Fifth question
- Write a prototype for a two-dimensional list. The dim(x, y) method should allocate a list of y lists that are x elements long. set(x, y,value) should set a value, and get(x, y) should return that value.
We've start this exerise by creating a new object signature
List2D := List clone
And then all we've got to do is to implement the method to initialize the two-dimentional array
List2D dim := method(x, y,
mlist := list
callback := call evalArgAt(2)
if(callback isNil, callback := block setScope(call sender))
for(i, 1, y,
slist := list
for(j, 1, x,
slist append(callback call(i, j))
)
mlist append(slist)
)
mlist
)
The method is relatively straightforward. The only catch here is the use of the
callback := call evalArgAt(2)
to retrieve the callback as a third parameter that will be called every time a new object is inserted in the two-dimentional list.
The rest of the method is a trivial iteration over the two indexes and by populating the list as we expected.
The set and get are trivial and listed bellow
List2D setXY := method(l,x,y,val,
l at(x) atPut(y,val)
)
List2D getXY := method(l,x,y,
l at(x) at(y)
)
Sixth question
- Bonus: Write a transpose method so that (new_matrix get(y, x)) == matrix get(x, y) on the original list.
By having the previous method to initialize a bidimentional array it is pretty easy to implement a transpose method in the Matrix object
Matrix transpose := method(
l1 := List2D clone
l1 data = data
return l1 dim(
data size,
data at(0) size,
method(x,y, method(x,y,
return data at(y-1) at(x-1)
)
)
)
)
For this to work the only change we need to do is to add a data slot in the List2D object
List2D data := List clone
To check if everything is working you can do
m2 data println
m2 transpose() println
m2 data = m2 transpose()
m2 transpose() println
This should output something like this
list(list(1, 2, 3), list(4, 5, 6))
list(list(1, 4), list(2, 5), list(3, 6))
list(list(1, 2, 3), list(4, 5, 6))
Seventh question
- Write the matrix to a file, and read a matrix from a file
This is a trivial exercise. All is needed is to search a little bit about the Io File API and then put all the knowledge to practice
Bellow we got the method that save the matrix into a file.
Matrix saveToFile := method(fileName,
f := File open(fileName)
data foreach(d, f write(d join(",")); f write("\n"))
f flush
f close
)
As you can see all it does is to iterate over the data List object and print it to the file and force the writing of the data before close the file resource
Matrix loadFromFile := method(filename,
f := File open(filename)
line := f readLine
data=list()
while(line != nil, data append(line split(",")) ;line = f readLine)
)
In the case of loading a Matrix from a file all we need to do is to iterate over all the lines and then build a List object with the contents per line
Last (8) problem
- Write a program that gives you ten tries to guess a random number from 1–100. If you would like, give a hint of "hotter" or "colder" after the first guess.
Like we did for the previous examples here we created a new object called GuessOrWhat to implement the game. The object only has one method called play. The game is pretty straightforward. All it does is to repeat until you get the value or reach a limit of 10 attempts and keep checking the answer with the previous to measure the distance between the current attempt with the last. Based in these two measures it will tell you if you are closer, warm, or not so closer cold.
GuessOrWhat := Object clone
GuessOrWhat play := method(
rnumber := (Random value( 99 ) + 1) floor();
rnumber println
stdio := File standardInput();
prev := nil;
10 repeat(
"Guess the number in the interval (1..100): " println();
if(prev == rnumber,break;)
guess := stdio readLine() asNumber();
if(
(guess != rnumber),
if(
prev,
if(
((rnumber - guess) abs()) >= ((rnumber - prev) abs()),
"Colder..." println(),
"Warmer..." println()
);
);
);
prev = guess;
);
if(
(guess == rnumber),
(
"Great! You've done it!" println();
),
(
"Keep trying!" println();
)
)
);
To play the game all you got to do is to invoke the following code
g := GuessOrWhat clone
g play