While there I translated the fractal tree program from Clojure to Scala.
As you can see, they're effectively the same program.
The command lines to run them are:
$ clj fractaltree.clj
$ scalac ScalaFractalTree.scala && scala ScalaFractalTree
It instantly becomes obvious that the Scala version is considerably faster.
I tried using (set! *warn-on-reflection* true), which told me that I needed to type hint the graphics objects in the clojure program, and it also says something about the proxy JPanel. I don't know how to fix that, but I don't think it's called often enough to be important.
The graphics type hints did seem to speed the Clojure version up a bit, but it's still noticeably slower than the Scala.
I'm presuming that the recursive calls to draw-tree are boxing and unboxing the parameters in the Clojure version.
Does anyone know if that's the case, and if so what can be done about it?
(import '(javax.swing JFrame JPanel ) '(java.awt Color Graphics Graphics2D)) (defn draw-tree [ #^Graphics g2d angle x y length branch-angle depth] (if (> depth 0) (let [new-x (- x (* length (Math/sin (Math/toRadians angle)))) new-y (- y (* length (Math/cos (Math/toRadians angle)))) new-length (fn [] (* length (+ 0.75 (rand 0.1)))) new-angle (fn [op] (op angle (* branch-angle (+ 0.75 (rand)))))] (. g2d drawLine x y new-x new-y) (draw-tree g2d (new-angle +) new-x new-y (new-length) branch-angle (- depth 1)) (draw-tree g2d (new-angle -) new-x new-y (new-length) branch-angle (- depth 1))))) (defn render [ #^Graphics g w h ] (doto g (.setColor (Color/BLACK)) (.fillRect 0 0 w h) (.setColor (Color/GREEN))) (let [init-length ( / (min w h) 5), branch-angle (* 10 (/ w h)), max-depth 12] (draw-tree g 0.0 (/ w 2) h init-length branch-angle max-depth))) (defn create-panel [] "Create a panel with a customised render" (proxy [JPanel] [] (paintComponent [g] (proxy-super paintComponent g) (render g (. this getWidth) (. this getHeight))))) (defn run [] (let [frame (JFrame. "Clojure Fractal Tree") panel (create-panel)] (doto frame (.add panel) (.setSize 640 400) (.setVisible true)))) (run)Here's the scala version
import scala.swing._ import java.awt._ import javax.swing._ object ScalaFractalTree { def main(args: Array[String]){ val frame=new JFrame("Scala Fractal Tree") val panel=new MyPanel() frame add panel frame setSize (640, 400) frame setVisible true } } class MyPanel extends JPanel{ override def paintComponent(g:Graphics):Unit = { super.paintComponent(g) render(g, getWidth(), this.getHeight()) } def render(g:Graphics, w:Int, h:Int){ g.setColor (Color.BLACK) g.fillRect( 0, 0, w, h) g.setColor (Color.GREEN) val initlength=if (w<h) w/5 else h/5 val branchangle=10*w/h val maxdepth=12 drawtree( g, 0.0, w/2.0, h ,initlength, branchangle, maxdepth) } def drawtree(g:Graphics, angle:Double, x:Double, y:Double, length:Double, branchangle:Double, depth:Double){ if (depth>0){ val newx= x-length*(Math.sin(Math.toRadians( angle))) val newy= y-length*(Math.cos(Math.toRadians( angle))) val newlength1 = length*(0.75+0.1*Math.random) val newlength2 = length*(0.75+0.1*Math.random) val newangle1 = angle+branchangle*(0.75+Math.random) val newangle2 = angle-branchangle*(0.75+Math.random) g.drawLine(x.toInt, y.toInt, newx.toInt, newy.toInt) drawtree(g, newangle1, newx, newy, newlength1, branchangle, depth-1) drawtree(g, newangle2, newx, newy, newlength2, branchangle, depth-1) } } }
