Summa sidvisningar

torsdag 17 november 2011

Programming in Scala - fördjupning

Nu har jag programmerar Scala på ledig tid i ganska exakt ett år. Det har varit roligt och lärorikt och något jag kan rekommendera insnöade Java-programmerare och andra som vill testa ett elegant språk med stöd för funktionell programmering!

Jag har hittills bara läst 200-300 sidor ut boken Programming in Scala second edition men planen är nu att läsa hela boken och att samtidigt förstå språket lite mer på djupet. Sättet jag tänkt göra det på är i huvudsak genom kodexempel. Jag kommer att göra nedslag på sådant jag tycker förtjänar extra uppmärksamhet eller som är svårsmält för en icke insatt, dit jag än så länge räknar mig själv! Jag kommer att läsa boken från pärm till pärm och rapportera om mina "upptäckter". Projektet Tetris Analyzer återkommer jag till vid ett senare tillfälle.

Exempel 1 - Referera arrayer och listor


I Scala kan man referera arrayer och listor med syntaxen (t ex) minLista(0) när man vill hämta ut ett element ur listan. Kompilatorn kommer då att göra om anropet till minLista.apply(0). Detta var jag förstås tvungen att testa (all kod finns på GitHub):
package nu.tengstrand.scalalab.programminginscala

/**
 * How to build your own List/Array classes.
 * Page 39 in the book "Programming in Scala, 2nd edition".
 *
 * Author: Joakim Tengstrand
 * Repository: git@github.com:tengstrand/Scala-Lab.git
 */
object Example001ListSyntax {
  def main(args: Array[String]) {
    val myArray = new MyArray()
    println(myArray(0)) // output: 1
    println(myArray.apply(1)) // output: 2
  }
}

class MyArray {
  val array = Array(1,2,3)

  def apply(i: Int) = array(i)
}

Allt som behövs är att implementera metoden apply(Int) på rad 21 varefter vi på rad 13 hämtar första elementet i listan. Metoden apply är en helt vanlig metod, vilket demonstreras på rad 14. Scala löser denna och andra liknande uppgifter genom konventioner. Konventionen i detta fall är att kompilatorn förväntar sig finna metoden apply när den stöter på en variabel följt av parenteser, vilket i mitt tycke är ett elegant sätt att lösa problemet.

Exempel 2 - Symboler


Nästa konvention är att om ett '-tecken "enkelfnutt" föregår en variabel så kommer kompilatorn uppfatta det som en Symbol vars attribut name är själva värdet på variabeln, detta beskrivs förvisso bra i boken men jag kände mig ändå sugen på att testa:
package nu.tengstrand.scalalab.programminginscala

/**
 * Example how to use symbols.
 * Page 80 in the book "Programming in Scala, 2nd edition".
 */
object Example002Symbol {
  def main(args: Array[String]) {
    new Example().print('KalleKanin) // output: Value: KalleKanin
  }
}
g
class Example {
  def print(value: Symbol) {
    println("Value: " + value.name)
  }
}
Verkar ju fungera! KalleKanin skickas in från rad 9 och skrivs ut på rad 15.

Exempel 3 - Teckensättning


I nästa exempel tänkte jag testa hur man gör sin egen klass som hanterar teckensättning:
package nu.tengstrand.scalalab.programminginscala

/**
 * Example how to use unary operators on own classes
 * Page 83 in the book "Programming in Scala, 2nd edition".
 */
object Example003UnaryOperatorSyntax {
  def main(args: Array[String]) {
    val value = new MyType(123)
    println(-value) // output: -123
    println(+value) // output: 123
    println(value.unary_-) // output: -123
  }
}

class MyType(value: Int) {
  def unary_- = new MyType(-value)
  def unary_+ = this
  override def toString = value.toString
}

Metoden unary_- implementeras på rad 17 och anropas från rad 10, metoden unary_+ implementeras på rad 18 och anropas från rad 11. Rad 12 visar att det är en helt vanlig metod som kan anropas som vilken annan metod som helst.

Exempel 4 - Lokala funktioner


I Scala kan man definiera funktioner i andra funktioner och metoder, så kallade lokala funktioner. Detta kan vara användbart när man vill öka läsbarheten och minska kodduplicering i koden:
package nu.tengstrand.scalalab.programminginscala

/**
 * Shows how to use local functions to simplify the code.
 * Page 144 in the book "Programming in Scala, 2nd edition".
 */
object Example004LocalFunctions {

  def main(args: Array[String]) {
    println(format(1,2,3,4, 10)) // output: (13:23)(33:43)
  }

  /**
   * The outer variable 'factor' is accessible from the local method multiply.
   * This technique can be used to simplify a function/method.
   */
  def format(a1: Int, a2: Int, b1: Int, b2: Int, factor: Int) = {
    val x = 3

    def multiply(value: Int) = value * factor + x
    def concat(value1: Int, value2: Int) = "(" + multiply(value1) + ":" + multiply(value2) + ")"

    concat(a1,a2) + concat(b1,b2)
  }
}

Det fina med lokala funktioner är att de har åtkomst till variabler och argument definierade i den omslutande funktionen vilket gör att dessa inte behöver läggas till argumentlistan i den lokala funktionen. I det här exemplet har metoden multiply tillgång till variabeln x och argumentet factor.

Det var allt för denna gång!

Inga kommentarer:

Skicka en kommentar