Wednesday, July 11, 2012

Factorial and Fibonacci in Ceylon



Update 1: Porting code examples to Ceylon M5 - syntax changes on assignent operator, module definition and support for String Interpolation.
Update 2: Interoperability with java code via .jar containing the Stopwatch class imported as a ceylon module using ceylon import-jar.

Here below a little program in Ceylon that implements 2 modules (+ an extra utility Stopwatch java class from my previous post http://carlosqt.blogspot.com/2011/05/stopwatch-class-for-java.html). There is the module called Fiborial (Fibo(nnacci)+(Facto)rial) that implements the Fibonacci and the Factorial algorithms in two ways, one Recursive (using recursion) and the other Imperative (using loops and states). The second class is just an instance class that does the same thing, but its there just to show the difference between static and instance classes, and finally the execution of the program done by the running function "main".

You can also find 3 more little examples at the bottom. One prints out the Factorial's Series and Fibonacci's Series, the second one just shows a class that mixes both: static and instance members, and finally the third one that uses different return types (including java.math.BigInteger) for the Factorial method to compare the timing and result.

As with the previous posts, you can copy and paste the code below in your Eclipse IDE and start playing and learning with it. This little "working" program will teach you some more basics of the Programming Language.

There are some "comments" on the code added just to tell you what are or how are some features called. In case you want to review the theory, you can read my previous post, where I give a definition of each of the concepts mentioned on the code. You can find it here: http://carlosqt.blogspot.com/2011/01/new-series-factorial-and-fibonacci.html 


The Fiborial Program

module com.series.staticfiborial '1.0.0' {
    shared import java.base '7';
    import blog.series.lib '1.0.0';
}

// Factorial and Fibonacci in Ceylon  
  
// Module "Static Class" using top level members  
import java.math { BigInteger { bigone=\iONE, bigzero=\iZERO,  
                                big=\IvalueOf } }  
import blog.series.lib { Stopwatch }  
  
// '(private) Static Field' + Initialization  
variable String _className = "'Static' Constructor";  
  
// Static Initializer Method instead, but need to be explicitly called.  
shared void constructor() {  
    print(_className);  
}  
  
// 'Static' Method - Factorial Recursive    
 shared BigInteger factorialR(Integer n) {    
    if (n == 1) {  
        return bigone;  
    }  
    else {  
        return big(n).multiply(factorialR(n - 1));  
    }    
}    
  
// 'Static' Method - Factorial Imperative    
shared BigInteger factorialI(Integer n) {  
    variable Integer c = n;  
    variable BigInteger res = bigone;  
    while (c > 1) {    
        res = res.multiply(big(c));  
        c--;  
    }  
    return res;  
}    
  
// 'Static' Method - Fibonacci Recursive    
shared Integer fibonacciR(Integer n) {    
    if (n < 2) {   
        return 1;  
    }    
    else {  
        return fibonacciR(n - 1) + fibonacciR(n - 2);  
    }    
}    
  
// 'Static' Method - Fibonacci Imperative    
shared Integer fibonacciI(Integer n) {    
    variable Integer pre = 1;  
    variable Integer cur = 1;  
    variable Integer tmp = 0;  
    for (i in 2..n) {    
        tmp = cur + pre;    
        pre = cur;    
        cur = tmp;    
    }    
    return cur;    
}  
  
// 'Static' Method - Benchmarking Algorithms    
shared void benchmarkAlgorithm(Integer algorithm, Integer[] values) {    
    Stopwatch timer = Stopwatch();  
    variable BigInteger facTimeResult = bigzero;  
    variable Integer fibTimeResult = 0;  
      
    if (algorithm == 1) {  
        print("\nFactorial Imperative:");  
        // "For" Loop Statement    
        for (j in 0..values.size - 1) {  
            if (exists testValue = values[j]) {  
                // Taking Time    
                timer.start();   
                facTimeResult = factorialI(testValue);  
                timer.stop();    
                // Getting Time    
                print(" (``testValue.string``) = ``timer.elapsed``");    
            }              
        }    
    }  
    else if(algorithm == 2) {  
        print("\nFactorial Recursive:");    
        // "While" Loop Statement    
        variable Integer i = 0;  
        while (i < values.size) {  
            if (exists testValue = values[i]) {       
                // Taking Time    
                timer.start();    
                facTimeResult = factorialR(testValue);    
                timer.stop();    
                // Getting Time    
                print(" (``testValue.string``) = ``timer.elapsed``");               
            }  
            i++;  
        }          
    }  
    else if (algorithm == 3) {  
        print("\nFibonacci Imperative:");    
        // "For Each" Loop Statement    
        for (testValue in values) {              
            // Taking Time    
            timer.start();   
            fibTimeResult = fibonacciI(testValue);    
            timer.stop();    
            // Getting Time    
            print(" (``testValue.string``) = ``timer.elapsed``");                        
        }    
    }  
    else if (algorithm == 4) {  
        print("\nFibonacci Recursive:");    
        // "For Each" Loop Statement    
        for (testValue in values) {              
            // Taking Time    
            timer.start();    
            fibTimeResult = fibonacciR(testValue);    
            timer.stop();    
            // Getting Time    
            print(" (``testValue.string``) = ``timer.elapsed``");  
        }          
    }  
    else {  
        print("DONG!");  
    }  
}  

doc "Run the module `com.series.staticfiborial`."  
void run() {  
    // pass    
}  

module com.series.fiborial '1.0.0' {
    import java.base '7';
    shared import com.series.staticfiborial '1.0.0';
}

import java.math { BigInteger }  
import java.util { Scanner }  
import java.lang { System { sin=\Iin } }   
// Import 'Static' Module's 'methods'  
import com.series.staticfiborial { staticFiborial=constructor, benchmarkAlgorithm,  
                                    facI=factorialI, facR=factorialR,  
                                    fibI=fibonacciI, fibR=fibonacciR }  
  
// Instance Class    
class InstanceFiborial(className="Instance Constructor") {    
    // Instance Field and Constructor/Initializer  
    variable String className;  
    print(className);  
    // Instance Method - Factorial Recursive    
    shared BigInteger factorialR(Integer n) {    
        // Calling Static Method    
        return facR(n);    
    }    
    // Instance Method - Factorial Imperative    
    shared BigInteger factorialI(Integer n) {    
        // Calling Static Method    
        return facI(n);    
    }    
    // Instance Method - Fibonacci Recursive    
    shared Integer fibonacciR(Integer n) {    
        // Calling Static Method    
        return fibR(n);    
    }    
    // Instance Method - Fibonacci Imperative    
    shared Integer fibonacciI(Integer n) {    
        // Calling Static Method    
        return fibI(n);    
    }    
}    

doc "Run the module `com.series.fiborial`."  
void main() {  
    print("\n'Static' Class");    
    // Calling 'Static' Class and Methods    
    // No instantiation needed. Calling method directly from the class    
    staticFiborial();    
    print("FacImp(5) = ``facI(5).string``");    
    print("FacRec(5) = ``facR(5).string``");    
    print("FibImp(11)= ``fibI(11).string``");    
    print("FibRec(11)= ``fibR(11).string``");   
    
    print("\nInstance Class");  
    // Calling Instance Class and Methods    
    // Need to instantiate before using. Call method from instantiated object    
    value ff = InstanceFiborial();    
    print("FacImp(5) = ``ff.factorialI(5).string``");  
    print("FacRec(5) = ``ff.factorialR(5).string``");    
    print("FibImp(11)= ``ff.fibonacciI(11).string``");  
    print("FibRec(11)= ``ff.fibonacciR(11).string``");  
    
    // Create a (generic) list of integer values to test    
    // From 5 to 50 by 5        
    Integer[] values = [
        for (i in 5..50)    
            if (i%5==0) i
    ];    

    // Benchmarking Fibonacci    
    // 1 = Factorial Imperative    
    benchmarkAlgorithm(1, values);    
    // 2 = Factorial Recursive    
    benchmarkAlgorithm(2, values);    
    
    // Benchmarking Factorial    
    // 3 = Fibonacci Imperative    
    benchmarkAlgorithm(3, values);    
    // 4 = Fibonacci Recursive    
    benchmarkAlgorithm(4, values);  
    
    // Stop and exit    
    print("Press any key to exit...");   
    value s = Scanner(sin);  
    //String line = s.nextLine();  
    s.nextLine();
    s.close();  
}  

And the Output is:





Printing the Factorial and Fibonacci Series
module com.series.fiborialseries '1.0.0' {
    shared import java.base '7';
}
import java.math { BigInteger { bigone=\iONE,     
                                big=\IvalueOf } }    
      
class Fiborial() {      
    // Using a StringBuilder as a list of string elements        
    shared String getFactorialSeries(Integer n) {        
        // Create the String that will hold the list        
        value series = StringBuilder();        
        // We begin by concatenating the number you want to calculate        
        // in the following format: "!# ="        
        series.append("!");        
        series.append(n.string);    
        series.append(" = ");        
        // We iterate backwards through the elements of the series        
        variable value i = n;        
        while (i > 0) {        
            // and append it to the list        
            series.append(i.string);    
            if (i > 1) {        
                series.append(" * ");    
            }        
            else {        
                series.append(" = ");    
            }    
            i--;        
        }        
        // Get the result from the Factorial Method        
        // and append it to the end of the list        
        series.append(factorial(n).string);        
        // return the list as a string        
        return series.string;     
    }    
        
    // Using a StringBuilder as a list of string elements        
    shared String getFibonnaciSeries(Integer n) {        
        // Create the String that will hold the list        
        value series = StringBuilder();        
        // We begin by concatenating the first 3 values which        
        // are always constant        
        series.append("0, 1, 1");  
        // Then we calculate the Fibonacci of each element        
        // and add append it to the list        
        for (i in 2..n) {        
            if (i < n) {       
                series.append(", ");    
            }    
            else {        
                series.append(" = ");    
            }    
            series.append(fibonacci(i).string);        
        }        
        // return the list as a string        
        return series.string;      
    }        
        
    shared BigInteger factorial(Integer n) {      
        if (n == 1) {        
              return bigone;    
        }    
        else {    
            return big(n).multiply(factorial(n - 1));    
        }             
    }             
        
    shared Integer fibonacci(Integer n) {      
        if (n < 2) {      
            return 1;    
        }    
        else {      
            return fibonacci(n - 1) + fibonacci(n - 2);    
        }    
    }          
}      
    
void run() {    
     // Printing Factorial Series      
    print("");      
    value fiborial = Fiborial();     
    print(fiborial.getFactorialSeries(5));      
    print(fiborial.getFactorialSeries(7));     
    print(fiborial.getFactorialSeries(9));      
    print(fiborial.getFactorialSeries(11));      
    print(fiborial.getFactorialSeries(40));      
    // Printing Fibonacci Series      
    print("");    
    print(fiborial.getFibonnaciSeries(5));      
    print(fiborial.getFibonnaciSeries(7));      
    print(fiborial.getFibonnaciSeries(9));      
    print(fiborial.getFibonnaciSeries(11));     
    print(fiborial.getFibonnaciSeries(40));      
}  

And the Output is:

















Mixing Instance and Static Members in the same Class

In Ceylon it is not possible to define a class with static members. To emulate that you need to create a module with toplevel methods, attributes, and even other instance types/classes.
"There are no static members. Instead, toplevel methods and attributes are declared as direct members of a package. This, along with certain other features, gives the language a more regular block structure." taken from Ceylon documentation.

    
module com.series.staticfiborial '1.0.0' {}
    
// Module "Static Class" using top level members  
  
// 'Static' Field'   
variable Integer _staticCount = 0;    
// 'Static' Read-Only Property/Getter  
shared Integer staticCount {  
    return _staticCount;  
}  
// 'Static' Initializer Method instead, but need to be explicitly called.  
shared void constructor() {  
    print("\nStatic Constructor ``_staticCount.string``");  
}  
// 'Static' Method  
shared void fibonacci(Integer n) {  
    _staticCount++;  
    print("\nFibonacci(``n.string``)");  
}  
  
void run() {  
    // pass  
}  
    
module com.series.fiborial '1.0.0' {
    import com.series.staticfiborial '1.0.0';
}
    
import com.series.staticfiborial { staticCount, fibonacci,  
                                    staticFiborial=constructor }  
  
// Instance Class  
shared class InstanceFiborial(_instanceCount=0) {    
    // Instance Field and Constructor/Initializer  
    variable Integer _instanceCount;      
    print("Instance Constructor ``this._instanceCount.string``");      
    // Instance Read-Only Property/Getter  
    shared Integer instanceCount {  
        return this._instanceCount;  
    }  
    // Instance Method    
    shared void factorial(Integer n) {    
        this._instanceCount++;    
        print("\nFactorial(``n.string``)");    
    }    
}  
  
void main() {  
    // Calling Static Constructor and Methods    
    // No need to instantiate    
    staticFiborial();    
    fibonacci(5);    
    
    // Calling Instance Constructor and Methods    
    // Instance required    
    value fib = InstanceFiborial();    
    fib.factorial(5);    
    
    fibonacci(15);    
    fib.factorial(5);    
    
    // Calling Instance Constructor and Methods    
    // for a second object    
    value fib2 = InstanceFiborial();    
    fib2.factorial(5);    
    
    print("");    
    // Calling Static Property    
    print("Static Count = " + staticCount.string);    
    // Calling Instance Property of object 1 and 2    
    print("Instance 1 Count = " + fib.instanceCount.string);    
    print("Instance 2 Count = " + fib2.instanceCount.string);    
}  

And the Output is:






















Factorial using java.lang.Long, java.lang.Double, java.math.BigInteger


module com.series.fiborial '1.0.0' {
    shared import java.base '7';
    import blog.series.lib '1.0.0'; 
}
import java.math { BigInteger { bigone=\iONE, bigzero=\iZERO,  
                                big=\IvalueOf } }  
import blog.series.lib { Stopwatch }  
  
// Long/Integer Factorial      
Integer factorialInt64(Integer n) {    
    if (n == 1) {  
        return 1;  
    }  
    else {  
        return n * factorialInt64(n - 1);  
    }    
}    
  
// Double Factorial    
Float factorialDouble(Integer n) {    
    if (n == 1) {  
        return 1.0;  
    }  
    else {  
        return n * factorialDouble(n - 1);  
    }    
}    
  
// BigInteger Factorial      
BigInteger factorialBigInteger(Integer n) {    
    if (n == 1) {  
        return bigone;  
    }  
    else {  
        return big(n).multiply(factorialBigInteger(n - 1));  
    }    
}     

void main() {  
    value timer = Stopwatch();    
    variable Integer facIntResult = 0;    
    variable Float facDblResult = 0.0;    
    variable BigInteger facBigResult = bigzero;        
      
    print("\nFactorial using Int64");  
    // Benchmark Factorial using Int64    
    for (i in (5..50).by(5)) {  
        timer.start();  
        facIntResult = factorialInt64(i);   
        timer.stop();  
        print(" (``i.string``) = ``timer.elapsed.string`` : ``facIntResult.string``");    
    }  
  
    print("\nFactorial using Double");    
    // Benchmark Factorial using Double  
    for (i in (5..50).by(5)) {  
        timer.start();  
        facDblResult = factorialDouble(i);   
        timer.stop();  
        print(" (``i.string``) = ``timer.elapsed.string`` : ``facDblResult.string``");  
    }  
  
    print("\nFactorial using BigInteger");    
    // Benchmark Factorial using BigInteger  
    for (i in (5..50).by(5)) {  
        timer.start();  
        facBigResult = factorialBigInteger(i);   
        timer.stop();  
        print(" (``i.string``) = ``timer.elapsed.string`` : ``facBigResult.string``");  
    }    
}



And the Output is:

2 comments:

  1. Just a few comments on certain parts of your code:

    You can have stepped ranges, so you can do something like "for (i in (5..50).by(5))" instead of the more verbose (and inefficient) "for (i in {for (i in 5..50) if (i%5==0) i})".

    You have a loop using a Range and then you get an item from a sequence:

    for (j in 0..values.size - 1) {
    if (exists testValue = values[j]) {

    The shorter way to do that is simply "for (testValue in values)".

    Ceylon has its own StringBuilder class, you don't need to import java's StringBuffer (which by the way you should only use when you know it will be used concurrently, otherwise it's much more efficient to use java's StringBuilder).

    Also, you can think of top level methods as plain functions, which don't need to be inside any object, rather than "static" methods, which Ceylon doesn't have.

    ReplyDelete
    Replies
    1. Hi again,

      Thanks for your remarks.
      I have updated some parts of the code to use loop using ranges
      "for (i in (5..50).by(5))" and also the for each in sequence "for (testValue in values)"

      I will certainly change the code to use the StringBuilder class.
      I will have a look again at the explanation of java's StringBuffer and StringBuilder.

      "There are no static members. Instead, toplevel methods and attributes are declared as direct members of a package." It sounds to me like static members of a module, the only difference is that you don't type module.member, but just member, but you right, they are not static.

      Delete