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:
Just a few comments on certain parts of your code:
ReplyDeleteYou 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.
Hi again,
DeleteThanks 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.