Llenguatge De Programació Scala: Llenguatge de programació

Scala és un llenguatge de programació modern multi-paradigma dissenyat per a expressar patrons de programació generals d'una manera concisa, elegant i segura respecte als tipus.

Integra característiques d'orientació a objectes i de llenguatges funcionals.

Infotaula de llenguatge de programacióScala
Llenguatge De Programació Scala: Scala és orientat a objectes, Scala és funcional, Scala té tipus estàtics
Llenguatge De Programació Scala: Scala és orientat a objectes, Scala és funcional, Scala té tipus estàtics
Tipusllenguatge de programació orientat a objectes, llenguatge de programació funcional, llenguatge de programació multiparadigma, JVM language (en) Tradueix i llenguatge de programació Modifica el valor a Wikidata
Data de creació20 gener 2004 Modifica el valor a Wikidata
DissenyMartin Odersky Modifica el valor a Wikidata
DesenvolupadorMartin Odersky i Escola Politècnica Federal de Lausana Modifica el valor a Wikidata
Paradigma de programaciócomputació concurrent, programació funcional, llenguatge imperatiu i programació orientada a objectes Modifica el valor a Wikidata
Darrera versió estable3.4.0 () Modifica el valor a Wikidata
Llenguatge de programacióScala Modifica el valor a Wikidata
Influenciat perML, Java, Erlang, Haskell, Smalltalk, ML Estàndard, OCaml, Common Lisp (en) Tradueix, Llenguatge de programació Eiffel, Oz, Pizza i Scheme Modifica el valor a Wikidata
Extensió dels fitxersscala i sc Modifica el valor a Wikidata
Codi fontCodi font Modifica el valor a Wikidata
Llicènciallicència BSD i Llicència Apache, versió 2.0 Modifica el valor a Wikidata
Etiqueta d'Stack ExchangeEtiqueta Modifica el valor a Wikidata
Pàgina webscala-lang.org Modifica el valor a Wikidata

Scala és orientat a objectes

Scala és un llenguatge pur O.O. (orientat a objectes) en el sentit que cada valor és un objecte. Els tipus i comportaments dels objectes són descrits mitjançant classes i 'trets'. L'abstracció classe s'estén mitjançant subclasses i també amb un mecanisme flexible basat en classes 'mixins' (barrejables: similars als interfaces de Java però amb definició de tipus, variables i implementació de mètodes) anomenades trets com a substitució neta de l'herència múltiple.

Scala és funcional

Scala també és un llenguatge funcional en el sentit que cada funció és un valor. Scala proveeix una sintaxi fàcil per a la definició de funcions anònimes, suporta funcions d'ordre superior, permet la imbricació de funcions i suporta el currying. Les classes per cas (ang.: case classes) i el suport que inclou de reconeixement de patrons, serveix per a modelar tipus algebraics, emprats en molts llenguatges de programació funcional.

A més a més, la noció de reconeixement de patrons estén de manera natural el procés de dades XML amb l'ajuda de seqüències de patrons ignorant el que hi ha a la dreta (ang.:right-ignoring). En aquest context, les comprehensions de seqüències (iteració sobre Generadors-Filtres-Mapeig) són útils per a formular consultes. Aquestes característiques fan de Scala l'ideal per a desenvolupar aplicacions com a serveis web.

Scala té tipus estàtics

Scala està equipat amb un sistema de tipus expressiu que assegura estàticament que les abstraccions s'utilitzin de manera coherent i segura. En particular el sistema de tipus suporta:

  • classes genèriques,
  • anotacions de variació,
  • acotacions de tipus per dalt i per baix,
  • classes internes i tipus abstractes com a membres de l'objecte,
  • tipus compostos,
  • auto referències tipificades explícitament,
  • vistes, i
  • mètodes polimòrfics.

Un mecanisme d'inferència de tipus permet que l'usuari no hagi d'anotar els tipus de manera redundant. En combinació, aquestes característiques proporcionen una base potent per a la reutilització segura de les abstraccions de programació i també per a l'extensió de programari amb tipus de dades segurs.

Scala és extensible

El disseny de Scala reconeix el fet que, a la pràctica, el desenvolupament d'aplicacions d'un domini específic requereix extensions de llenguatge igualment específiques. Scala proporciona una combinació única de mecanismes de llenguatge que fan fàcil afegir noves construccions al llenguatge, amb suavitat, en forma de biblioteques:

  • qualsevol mètode pot ser emprat com a operador infix o postfix, i
  • els tancaments es construeixen automàticament depenent del tipus esperat.

Un ús conjunt d'ambdues característiques facilita la definició de noves instruccions sense estendre la sintaxi i sense emprar tècniques de meta-programació com ara macros.

Scala interopera amb Java i .NET

Scala està dissenyat per a interoperar bé amb entorns populars de programació com Java 2 (JRE) i el marc .NET (CLR). En particular, la interacció amb llenguatges orientats a objectes de gran acceptació com Java i C# és força suau. Scala té el mateix model de compilació (compilació separada, càrrega dinàmica de classes) que Java i C#, permetent accedir a milers de biblioteques d'alta qualitat.

Exemple: Hola món

Per començar:

El programa Hola món en Scala:

 // fitxer src/test/hola-mon.scala  package test   object Principal {     def main(args: Array[String]) = Console.println("Hola món!")  } 

alternativa derivant l'objecte Application (obsolet des de 2.9, ang:deprecated, eliminat a 2.11) es recomana fer servir el mòdul App

 // fitxer src/test/hola-mon.scala  package test   object Principal extends Application { // Application defineix main     // la inicialització del mòdul executa les expressions del cos.     Console.println("Hola món!")   } 

El mòdul App afegeix a Application la propietat args que permet accedir als arguments.

 // fitxer src/test/hola-mon.scala  package test   object Principal extends App {       Console.println ("Hola món!, els arguments són:" ++ args.mkString (", "))   } 
  • Compilació i execució a consola de comandes:
mkdir classes  # subdirectori per al codi objecte  # compilació (-cp és classpath, -d és directori per a la sortida)  scalac -cp classes -d classes src/test/hola-mon.scala  # execució scala -cp classes test.Principal          # execució pel nom de l'objecte que conté el mètode ''main''  Hola món! 

Gestor de projectes SBT

Aquesta manera de treballar directament amb el compilador ha quedat una mica antiquada. Si ho fem a través del gestor de projectes SBT, aquest ens descarregarà automàticament la versió del compilador i de les dependències del projecte, esmentades al fitxer de projecte.

Caldrà però seguir l'esquema de directoris especificat per l'SBT.

Al Linux el trobarem al gestor de paquets o bé el podem descarregar d'origen si volem la darrera versió.

A l'avaluador d'expressions (bucle Read-Eval-Print)

scala  scala> println("Hola món") Hola món  scala> 

Amb el gestor de projectes SBT podem engegar l'avaluador REPL amb la comanda següent:

// fitxer de projecte build.sbt lazy val root = (project in file(".")).   settings(     name := "hola_món",     version := "1.0",     scalaVersion := "2.11.7"  ) 
sbt consoleQuick    # engega REPL (Read-Eval-Print Loop) sense carregar les classes del directori de treball i subdirectoris sbt console         # engega REPL carregant les classes del directori de treball i subdirectoris  scala> 
  • Avaluació d'un mòdul Application (conté mètode main) (obsolet, ang:deprecated)
scala> :reset     // reinicialitza l\'avaluador scala> object Main extends Application {   // per accedir als arguments cal redefinir el mètode ''main''       |   println("Hola món")       | }   scala> Main       // avaluació del mòdul Application, executa les instruccions de l'objecte Main 
  • Avaluació d'un mòdul App (versió moderna de Application, incorpora membre args).
scala> :reset     // reinicialitza l\'avaluador scala> object Main extends App {  // arguments visibles com a ''args'', membre de la classe       |   for (arg <- args) {       |      println(arg)       |}} scala> Main.main(Array("a","b","c"))       // avaluació del mòdul App, executa les instruccions de l'objecte Main 

Característiques

Degut a ésser un llenguatge dissenyat inicialment per a la màquina virtual JVM, hereta del Java les característiques del lèxic i els tipus bàsics i les seves operacions així com una sintaxi molt similar.

Els punt-i-coma a final de línia es poden ometre (des de la versió 2).

Vegeu l'especificació del llenguatge.

tipus especials

Vegeu Tipus unificats (amb gràfic) a la ref.

    Unit
    Tipus d'un sol valor que és: ()       // no-op : no fer res (element neutre per l'esquerra de les operacions)

Resultat buit en efectes col·laterals (actualitzacions, impressions). Equival al tipus de retorn buit (void) de Java i C

    Any
    generalització d'un tipus qualsevol
    AnyVal
    generalització dels tipus primaris (els suportats per la CPU) que es passen per valor.
    AnyRef
    generalització dels tipus que es passen per referència. Correspon a Java.lang.Object
    ScalaObject
    base per als objectes de Scala (obsolet, retirat a partir de v2.10)
    Null
    es considera subtipus de qualsevol tipus que es passi per referència (classe o bé object), així el seu valor null pot pertànyer a tots ells.
    Nothing
    Tipus deshabitat (de valor impossible). És subtipus de tots els tipus. Té diversos usos:
  • És el tipus de retorn de les rutines que mai no retornen res, és a dir, que sempre acaben llançant excepcions.
  • El tipus List[Nothing] és subtipus de totes les llistes (covariants en el tipus de l'elem.) però no pot tenir elements, així el seu únic valor és Nil que és una extensió del tipus List[Nothing]. Això fa de Nil subtipus de List[T] per qualsevol T.
    Symbol
    literals: identificadors precedits per l'apòstrof.
scala> val saluda = 'hola saluda: Symbol = 'hola  scala> saluda match {        // salt de carro després de '{' o bé '(' activa el                               // mode multi-línia amb apuntador '|'      | case 'hola => println("Benvingut!")               | case 'adéu => println("A reveure!") }       // '}' al mateix nivell, tanca el mode multi-línia Benvingut! 
    Opcionals
    El tipus Option[T] amb les variants Some[T] i None facilita l'ús de paràmetres i resultats opcionals

Es pot convertir un valor possiblement nul de Java amb el generador Option(valorNullificable)

scala> Option(5 :java.lang.Integer) res1: Option[Integer] = Some(5)  scala> Option(null :java.lang.Integer) res2: Option[Integer] = None 
    Llista
    El tipus List[T]
// llista homogènia scala> List(1,7,5) res10: List[Int] = List(1, 7, 5)  // Conversió implícita de tipus (si hi ha una conversió implícita visible) scala> List(1, 'a', 3.5) res11: List[Double] = List(1.0, 97.0, 3.5)  // llista especificant un tipus base (més general) scala> List[Any](1, 'a', 3.5) res13: List[Any] = List(1, a, 3.5)  // llista buida de tipus sense especificar scala> List() res14: List[Nothing] = List()  // Nil especialitza la llista buida scala> Nil res16: scala.collection.immutable.Nil.type = List()  scala> Nil : List[Int] res17: List[Int] = List() 
    Tuples (Tuple2 a Tuple22)
scala> val a = Tuple3(1, 'a', 3.5) a: (Int, Char, Double) = (1,a,3.5)  scala> a._1 res0: Int = 1  scala> a._2 res1: Char = a 

Anàlisi per casos dels valors d'una expressió

Per encaix primer valors, després variables amb restricció de tipus (de més a menys específic)

package test ;  object Main extends App {    val llista: List[Any] = List(true, 'c', "abc"  , 8:Byte, 270:Short, 300 /* Int */, 200000000000L /* Long */  , 2.5F /* Float */, 3.12345678 /* Double */   ) ;   for (elem <- llista) {     elem match {            case 2 | 3 => print("dos o tres") ;            case w: AnyVal => w match { // tipus primitius que es passen per valor                      case v :Char => printf("%c u\\%04x", v, v.toInt);  // caràcter i hexadecimal                      case v :Byte => printf("és un byte: %d", v) ;                      case v :Short => printf("és un short: %d", v) ;                      case v :Int  => printf("és un int: %d 0%o x%08x", v, v, v);     // decimal, octal i hexad.                      case v :Long => printf("%d", v) ;                      case v :Float => printf("%.2f", v) ;                      case v :Double => printf("%.8f", v) ;                      case v => print(v)                      }            case w: AnyRef => w match { // tipus que es passen per referència                      case v :String => printf("%s", v) ;                      case v => print(v.toString) ;                      }                }     }     println("")    } } 

dona

true c u\0063 abc és un byte: 8 és un short: 270 és un int: 300 0454 x0000012c 200000000000 2,50 3,12345678 

Igualtat estructural vs. referencial

En la comparació d'objectes, l'operador de Scala (==) no correspon en Java al (==) sinó al mètode equals.

Scala Java Comentaris
igualtat de les referències eq, ne
(classe AnyRef)
(==), (!=)
igualtat estructural (==), (!=)
(classe Any)
.equals() excepte per al tipus Array[T]
igualtat dels elements .sameElements(that: GenIterable[A])
(classe GenIterable)
permet comparar elems. de diferents estructures

A Scala la igualtat d'objectes que es passen per referència AnyRef equival a

x == y <=> if (x eq null) (y eq null) else x.equals(y) 

polimorfismes

polimorfisme per subtipatge

trait Animal {   val nom: String   val crit: String } class Gat extends Animal {   val nom = "Gat"   val crit = "miola" } class Gos extends Animal {   val nom = "Gos"   val crit = "borda" }  function crida(animal: Animal) {      println (s"El ${animal.nom} ${animal.crit}") } 

polimorfisme paramètric

Les funcions es poden parametritzar per Tipus

 def nom_funció[VariableDeTipus](param1: VariableDeTipus, ..) = ... 

exemple:

object Main extends App {   def imprimeix[T](dada: T) =      dada match {       case v: String => println(v) ;       case v => println (v.toString) ;     }  imprimeix[Int](5) ;  imprimeix[String]("abc") ;  imprimeix(2);              // si no s'explicita el tipus, es dedueix dels paràmetres actuals.  imprimeix("def") ; } 

polimorfisme ad-hoc

Afegeix requeriments de context al paràmetre de tipus. A l'exemple amb [A: Numeric] indica que cal que existeixi una instància del genèric Numeric[T] per al tipus A (Numeric[A]), visible en l'àmbit d'ús.

import scala.math.Numeric._  function producte[A: Numeric](x: A, y: A):A = {   val instància = implicitly[Numeric[A]]   // instància de Numeric per al tipus del paràmetre emprat a la crida  instància.times(x, y) } 
  • scala.math.Numeric és la signatura d'un anell unitari ordenat (Numeric especialitza (extends) Ordering).
  • scala.math.Integral és la signatura d'un anell unitari euclidià ordenat íntegre (Integral especialitza (extends) Numeric).
  • scala.math.Fractional és la signatura d'un cos ordenat (Fractional especialitza (extends) Numeric).
    quan hi ha una sola instància, equival a la crida amb paràmetre implícit (vegeu #Implícits)
    quan hi ha més d'una instància d'un genèric (ex. diversos monoides), cal fer servir la crida amb implícit explicitant la instància desitjada
abstract class SemiGrup[A] {       def assoc(x: A, y: A): A     }  abstract class Monoid[A] extends SemiGrup[A] {       def neutre: A     }  object Monoides {        // Monoides per a la suma i el producte       implicit object IntMonoidSuma extends Monoid[Int] {         def assoc(x: Int, y: Int): Int = x + y         def neutre: Int = 0       }       implicit object IntMonoidProducte extends Monoid[Int] {         def assoc(x: Int, y: Int): Int = x * y         def neutre: Int = 1       } }  object Test extends App {    import Monoides._   // per fer servir les instàncies implícites, han d'estar visibles    import scala.collection.Iterable      def plegaLlista[A](xs: Iterable[A])(implicit m: Monoid[A]): A = xs.fold (m.neutre) (m.assoc)      println(plegaLlista(List(1, 2, 3)) (IntMonoidSuma))     println(plegaLlista(List(1, 2, 3)) (IntMonoidProducte)) } 

àlies de tipus

type LaMevaFun[A,B,C] = B => A => Pair[B, C] 

Relacions de Subtipus

  • A <: B indica A subtipus de B
  • A >: B indica B subtipus de A

En el següent exemple Tipus ha d'ésser subtipus de TCotaSuperior.

 type Tipus <: TCotaSuperior  // 'Tipus' ha d'ésser subtipus (més especialitzat) de 'TCotaSuperior' 

en el següent exemple Tipus ha d'ésser superclasse de TCotaInferior.

 type Tipus >: TCotaInferior  // 'Tipus' ha d'ésser supertipus (més general) de 'TCotaInferior' 

Subtipatge de funcions i de classes, Covariança, Contravariança

Vegeu ref.

  • Una funció f: (A' => B') és subtipus d'una altra funció g: (A => B) (f pot ser emprada en el lloc de g) si el resultat és de tipus més especialitzat (B' <: B) i admet arguments de tipus més general (A' >: A).

Es diu que la funció f subtipus de g és covariant en el resultat (la relació varia en el mateix sentit) i contravariant en el paràmetre de valor. El tipus funció definit a Predef és

// el prefix + en un paràmetre de tipus indica covariança // el prefix - en un paràmetre de tipus indica contravariança  // del mòdul Predef que conté els elements predefinits del llenguatge type Function[-A, +B] = (A)  B 

Perquè una classe C sigui subtipus d'una altra D, és a dir poder fer servir C com a tipus del paràmetre actual en una crida amb paràmetre formal de tipus D, C ha de contenir subtipus de tots els mètodes de D, a banda d'altres requeriments.

Això ens facilita la deducció de la relació de subtipatge de les col·leccions respecte de la que hi hagi entre els tipus dels elements.

  • La covariança d'una classe d'una col·lecció respecte del tipus de l'element es dona si:

Sempre que A <: B llavors C[A] <: C[B]

Així sabem que les llistes, són covariants respecte al tipus de l'element, i mantenen la relació de subtipatge entre llistes d'elements de tipus diferents, si hi és entre els tipus dels elements.

  • La contravariança d'una classe es dona si:

Sempre que A <: B llavors C[A] >: C[B]

Perquè una classe C[T] sigui covariant en un tipus T, C[+T], aquest tipus només pot aparèixer en posicions covariants (resultats) dels mètodes i no en les contravariants (arguments). Al revés per a les classes contravariants en T: C[-T]. Però sembla que és una condició necessària però no suficient. Vegeu el Principi de substitució de Liskov

Per utilitzar un tipus covariant en posicions d'argument (contravariant) en un mètode i continuar mantenint la covariança del mètode i, per tant, de la classe, cal fer servir el truc del límit inferior. Cal assegurar que l'argument ha d'admetre tipus més generals que T, fent servir per a l'argument un tipus existencial: aquells tipus U tals que U >: T.

class ListNode[+T](h: T, t: ListNode[T]) {   def head: T = h                                  def tail: ListNode[T] = t    // per poder utilitzar el tipus T en l'argument i mantenir la covariança de la classe respecte a T   // l'argument (posició contravariant) ha d'admetre qualsevol supertipus U >: T   def prepend[U >: T](elem: U): ListNode[U] =        ListNode(elem, this)                         } 

Tipus producte

Tuples (tipus producte anònim)

Definides les classes Tuple2, Tuple3, .., Tuple22. 
object Main extends App {    val parell = new Tuple2(10, "abc") ; // new amb el nom de la classe    val Tuple2(primer, segon) = parell;  // encaix de la tupla    println(parell._1) ; // primer   println(parell._2) ; // segon, ... } 

Classes (tipus producte amb nom), i Objectes singulars

A Scala se separen les parts dinàmica i estàtica de les classes del Java en class i object, objecte singular (únic) que conté els membres estàtics.

Extensibilitat. Els object tenen un tractament unificat amb les classes respecte a l'extensibilitat com si fossin classes d'una sola instància, que els permet implementar interfícies i estendre-les amb altres object i traits com a l'exemple amb l'objecte App.

Grafia. Els identificadors dels mòduls object comencen per majúscula segons el llibre d'estil.

La inicialització dels object i també de les classes, es produeix seqüencialment processant definicions i instruccions executables, com a l'ML Estàndard i l'OCaml.

// ''App'' és l'object d'engegada que conté el mètode ''main''  object ProvaFiltratge extends App {    def filtrar(xs: List[Int], llindar: Int) = {      def processa(ys: List[Int]): List[Int] =    // funció a l'interior d'una altra funció      if (ys.isEmpty) ys       else if (ys.head >= llindar) ys.head :: processa(ys.tail)       else processa(ys.tail)      processa(xs)   }   println(filtrar(List(1, 9, 2, 8, 3, 7, 4), 5)) } 

En importar una classe de Java, l'identificador designa la classe equivalent en Scala amb els mètodes dinàmics (de la instància), així com l'Object que conté els mètodes, variables i constants estàtics.

import java.lang.{Color} val vermell = new Color(0xFF, 0, 0);                    // constructor de la ''classe'' val componentRoigDelBlanc = Color.getRed(Color.WHITE);  // membres estàtics de l{{'}}''Object'' 
    els object (mòduls amb els membres estàtics) de les col·leccions incorporen generadors
object List {   def apply[T] (elements: T*)  // genera una llista amb els elements proporcionats com a paràmetres                                // ''apply'' és el nom de funció per omissió (es pot ometre)                                // així List (1, 2, 3)  equival a List.apply(1, 2, 3)    def empty[T] ()              // generador de col·lecció buida } 
    override
    els mètodes que es redefineixen han de dur el qualificatiu override per distingir-los dels que es volen nous, per evitar que si algú afegeix un mètode amb el mateix nom i signatura a alguna de les superclasses o trets que s'hereten, el comportament esdevingui diferent del que se n'espera.
    val/var (al paràmetre de la classe)
    l'ús de declaradors val/var converteix el paràmetre en un camp membre de la classe, evita redefinir-lo com a camp amb el mateix nom, i haver d'assignar-li el valor del paràmetre dins el constructor.
  class Classe(val p1: Tipus, p2: Tipus2 = vPerDefecte, ...)  // amb val, p1 passa a ser membre de la classe 

Extractors

Un extractor és un objecte singular que implementa la funció unapply que permet extreure'n el valor en encaixar patrons en les opcions de les clàusules match.

    // el següent codi genera un objecte extractor     object Doble {       // generador       def apply (x: Int): Int = x * 2                                 // inversa per als encaixos de patrons, retorna en un Option l'èxit o fracàs de l'encaix       def unapply (z: Int): Option[Int] = if (z % 2 == 0) Some (z/2) else None       }      object ProvaDoble extends App {       val x = Doble (21)             // aplica 'apply' (nom de funció per omissió a les expressions)       x match { case Doble (n) => Console.println(n) } // aplica 'unapply' (nom de funció per omissió als patrons)     } 

tipus "suma" amb classes encaixables

Les classes encaixables (ang:case class) permeten definir com a subtipus les variants d'un tipus suma (unió discriminada) permetent l'anàlisi per casos.

Les classes encaixables són classes per a les quals es defineix, automàticament, un objecte extractor (secció precedent), facilitant l'ús del nom de la classe i els seus paràmetres com a patró en un selector per encaix de patrons match/case.

abstract class Arbre[T] // ''val'' al paràmetre el declara membre de la classe, estalvia redeclaracions case class Fulla[T](val dada: T) extends Arbre[T] case class Branca[T](val dada: T, val esquerra: Arbre[T], val dreta: Arbre[T]) extends Arbre[T]  object Main extends App {     def imprimeixArbre[T](arbre: Arbre[T]):Unit =      arbre match {             case Fulla(d) => println ("fulla: " ++ d.toString)             case Branca(d, esq, dreta) => { imprimeixArbre(esq) ;                                              println("branca: " ++ d.toString) ;                                             imprimeixArbre(dreta) ;                                            }     }    // els constructors de case classes no necessiten ''new'', de fet no són constructors sinó generadors membres de l'objecte extractor   // l'expressió {Fulla(1)} equival a {Fulla.apply(1)}  // ''apply'' és el nom de funció per omissió.    val arbre1 = Fulla[Int](1);      // si s'omet el paràmetre de tipus ''[T]'', es pren el tipus del paràmetre actual.   val arbre2 = Branca(2, arbre1, arbre1) ;  imprimeixArbre(arbre2) ; } 

Equivalència en ML Estàndard:

datatype 't Arbre = Fulla of 't | Branca of ('t * 't Arbre * 't Arbre) 

Equivalència en Haskell:

data Arbre t = Fulla t | Branca t (Arbre t) (Arbre t) 
exhaustivitat als encaixos (atribut sealed)

L'atribut sealed (segellat) a la classe abstracta de la qual es deriven les variants encaixables (case classes), permet circumscriure'n la definició de variants al fitxer on està definida, formant un conjunt tancat, facilitant que el compilador pugui avisar per manca d'exhaustivitat als encaixos.

Exemple de compilació amagant el cas Fulla:

sealed abstract class Arbre[T]              // 'sealed': classe segellada, per al control d'exhaustivitat als encaixos case class Fulla[T](val dada: T) extends Arbre[T] case class Branca[T](val dada: T, val esquerra: Arbre[T], val dreta: Arbre[T]) extends Arbre[T]  object Main extends App {     def imprimeixArbre[T](arbre: Arbre[T]):Unit =      arbre match {             // case Fulla(d) => println ("fulla: " ++ d.toString)    // comentat expressament per veure l'avís del compilador             case Branca(d, esq, dreta) => { imprimeixArbre(esq) ;                                              println("branca: " ++ d.toString) ;                                             imprimeixArbre(dreta) ;                                            } ... 
sbt compile [info] Compiling 1 Scala source to ... [warn] /home/gabi64/lleng/scala/sealed/Main.scala:9: match may not be exhaustive. [warn] It would fail on the following input: Fulla(_) [warn]     arbre match { [warn]     ^ [warn] one warning found [success] Total time: 10 s, completed 07/06/2016 15:35:50 

Seqüències numèriques

  • Range // classe de seqüències de Int
  • Per generalitzar-ho per a enters de qualsevol precisió {Long, Int, Short, Byte}

NumericRange[T: Integral] // classe de seqüències de tipus que implementen Integral[T] (anell unitari íntegre ordenat euclidià: operacions dels enters)

Les següents expressions retornen un iterador, que es pot fer servir com a generador en una clàusula for, o bé aplicar toList per obtenir-ne una llista.

(1 until 5)  // interval obert (exclou límit superior) (1 to 5)     // interval tancat (inclusiu) (1 to 5 by 2)  // interval amb increment 

Llistes

object Main extends App {   val llista = (1 to 3).toList ;     // List(1,2,3)   def descriuLlista[T](llista: List[T]) =     llista match {      case Nil => println ("llista buida") ;       case ll @ (_ :: _) =>      // as-pattern (variable @ patró): unifica variable i terme encaixat                           println ("llista no buida: " + ll.toString) ;     }   descriuLlista(llista) } 

Llistes per comprensió

La clàusula for admet una primera part amb generadors i filtres (clàusula if), i una segona part que pot ésser o bé un bloc d'efectes col·laterals sobre els elements del generador, o bé una clàusula yield (trad.:produeix) que mapeja l'expressió o bloc als elements del primer generador retornant la col·lecció resultant.

 // retorna els dobles dels parells de l'interval  // en una col·lecció del tipus del primer generador   val iterador = for (i <- Iterator.range(des_de, topall_exclòs)                      if i % 2 == 0                     ) yield 2 * i   // el tipus resultant és Iterator[Int]  println (iterador.toList)   // equivalent amb rangs: (des_de to topall_inclòs)  val iterador = (1 to 12).iterator.filter(_ % 2 == 0).map(_ * 2)   println (iterador.toList) 
Encadenament monàdic

Les llistes per comprensió introdueixen l'encadenament monàdic: encadenament d'una acció amb una funció d'efectes col·laterals sobre el resultat precedent, el tipus final és el de l'acció primera amb resultat determinat per la funció (avaluada en segon lloc). L'acció també és assimilable a l'avaluació d'una col·lecció amb el tipus de l'element com a tipus del resultat.

La clàusula for incorpora una seqüència de generadors, guardes (clàusula if), expressions basades en resultats precedents (clàusula let) i cos, que es tradueix en l'ús de les primitives flatMap, filter i finalment map per al cos, retornant sempre una col·lecció del tipus del primer generador, amb elements del tipus del resultat del cos.

scala> val opt3 = Some(3) scala> val opt4 = Some(4)  scala> for { x <- opt3;               y <- opt4 } yield (x+y) res4: Option[Int] = Some(7)  // desplegament monàdic equivalent  scala> opt3 flatMap { x => opt4 map { y => x + y}} res5: Option[Int] = Some(7) 

Per als tipus amb "efecte fallada" (element absorbent per a flatMap), com ara Option[T], Try[T], List[T], ..., l'encadenament de generadors, filtres i expressions evita la necessitat de consultar la correcció del resultat a cada pas, perquè l'encadenament progressa només per als elements no absorbents per l'esquerra respecte a l'encadenament (aquí l'op. flatMap).

-- ús de l'encadenament monàdic per al tipus Try[T] (variants Success[T] i Failure[Throwable]) -- Try encapsula, a l'exemple, l'avaluació de l'operació de lectura, retornant Failure(err) en cas d'excepció -- Failure(..) és l'element absorbent per l'esquerra de ''flatMap'' per al tipus Try, fent que l'encadenament no progressi.  val result = for (dividend <- Try(Console.readLine("entreu dividend: ") ;                   divisor  <- Try(Console.readLine("entreu divisor: ")             ) yield dividend / divisor result match {       case Success(v) => println ("correcte: " + v.toString)       case Failure(err) => println ("pifiada: " + err.getMessage)       } 

Vegeu presentació "Introduction to Monads in Scala".

Variables i mètodes

El tipus de les declaracions es pot ometre si es dedueix de la seva inicialització de manera inequívoca, altrament el compilador avisarà.

val identificador :Tipus = expressió     // amb val: assigna el valor de l'expressió com a constant var identificador :Tipus = expressió     // amb var: variable mudable def identificador :Tipus = expressió     // amb def: es recalcula a cada invocació  // Les inicialitzacions de variables amb el comodí '_'  // assignen l'element zero del tipus si és subtipus de 'AnyVal' (pas per valor)  // o bé 'null' si és subtipus de 'AnyRef' (pas per referència)  scala> var v : Boolean = _ v: Boolean = false  scala> var v : Int = _ v: Int = 0  scala> var v : String = _ v: String = null 

La diferència entre una definició amb val i un mètode (definit amb def) és que l'assignació amb val es calcula una única vegada, mentre que amb def es recalcula a cada invocació.

Operadors

Els operadors són mètodes definits amb símbols.

class Q {           def ##(that:Q) = this != that         } 

La precedència la determina el primer caràcter del símbol. (Vegeu apartat 'Infix operators' al manual de ref. del llenguatge).

(all letters) | ^ & < > = ! : + - * / % (all other special characters) 

associativitat per la dreta explícita

L'associativitat la determina el darrer caràcter del símbol, que si és ':' llavors és associatiu per la dreta, altrament associatiu per l'esquerra. Vegeu ScalaReference.pdf (paràgraf 6.12.3 Infix Operations).

L'associativitat per la dreta, en agrupar per la dreta, requereix que l'operador en notació infix s'avaluï com a mètode del tipus de l'operand de la dreta:

// si 'x1' i 'x2' són elements i 'xs' és una col·lecció x1 +: x2 +: xs  x1 +: (x2 +: xs) // (+:) ha de ser mètode del tipus de l'operand de la dreta: la col·lecció 

L'associativitat per la dreta pels ':' finals, afecta a més dels operadors, als constructors definits amb símbols, com ara (::) el "Cons" de les llistes i també (#::) el "Cons" dels Stream.

// (++:) concatenació amb associativitat per la dreta (pels ':' final) // (++:) és membre del tret "Traversable", que haurà d'implementar l'operand de la dreta  // StringBuilder és una ''String'' mudable que implementa ''Growable'' (que pot créixer). // seq.mkString converteix una seqüència en una String  val strBldr = "abc" ++: "def" ++: new StringBuilder("ghi")  val novaStr = strBldr.mkString 

Trets (ang:traits): el sistema d'extensió de classes i d'objectes singulars

La paraula anglesa trait es pot traduir per tret o característica (vegeu diccionaris d'anglès).

Un tret encapsula una característica incorporable a una classe d'objectes o bé a un object singular.

Són assimilables als interface del Java, però amb el potencial d'una classe abstracta.

Els trets poden contenir estructura (camps), comportament (mètodes) i tipus associats, tant abstractes com concrets (amb implementació), però no són instanciables per si mateixos, ni podien tenir paràmetres de valor. Això canviarà pròximament permetent els paràmetres als trets.

import java.awt.{Color} ;  trait Acoloriment { var color:Color;  def pinta(nouColor: Color): Unit = { color = nouColor} def imprimeixColor: Unit = { print("color: "); println(color) }  }  trait Cloneable extends java.lang.Cloneable {   override def clone(): Cloneable = { super.clone(); this } }  trait Resetable {   def reset: Unit }  def cloneAndReset(obj: Cloneable with Resetable): Cloneable = {   val cloned = obj.clone()   obj.reset   cloned } 

herència múltiple

L'herència múltiple es basa en la incorporació de trets.

import java.awt.{Color} ;  class Punt(xInicial:Int) { var x: Double = xInicial.toDouble ; def desplaça(dx: Double): Unit = { x = x + dx} def imprimeixPosició: Unit = { print("posició: "); println(x) }  }  // els trets (ang: trait) són com una classe abstracta trait Acoloriment { var color: Color;  def pinta(nouColor: Color): Unit = { color = nouColor} def imprimeixColor: Unit = { print("color: "); println(color) }  }  // incorpora el tret Acoloriment donant valor als membres abstractes class PuntAcolorit(xInicial:Int, colorInicial: Color) extends Punt(xInicial) with Acoloriment {  var color = colorInicial ;  def desplaçaIPinta(dx: Double, nouColor: Color) = {     super[Punt].desplaça(dx) ;   super[Acoloriment].pinta(nouColor) ; } }  object Main extends App {    val punt = new PuntAcolorit(0, Color.WHITE) {   // extensió en instanciar                                       // només aquesta instància incorpora l'extensió      def desplaçaRàpid(dx: Double) = {     x = x + 10.0 * dx   }   }   punt.desplaçaIPinta(2, Color.BLUE) ;   punt.desplaçaRàpid(3.5)   punt.imprimeixPosició ;   punt.imprimeixColor ; } 

dona el resultat:

posició: 37.0 color: java.awt.Color[r=0,g=0,b=255] 

Restricció del tipus propi (ang:self-types)

La restricció al tipus propi en un tret (especificant un identificador, com ara self, abans de =>) permet relacionar un tret amb una instància concreta d'un tipus abstracte. És possible utilitzar this per aquest afer, però en cas de referir-s'hi en una classe interna (niuada) l'identificador this quedaria tapat. this es referiria a l'objecte corresponent a la classe interna i no a l'externa.

Del document "Scala Component Abstractions" de M.Odersky.

abstract class Graf {   type Node <: NodeBase;  // volem que els nodes implementin NodeBase    class Arc(val origen: Node, val destí: Node) {}    class NodeBase {     def connectaAmb(n: Node): Arc =       new Arc(this, n);   // il·legal !! 'this' no és de tipus Node   } } 

Solució amb self-types

abstract class Graf {   type Node <: NodeBase;  // volem que els nodes implementin NodeBase    class Arc(val origen: Node, val destí: Node) {}    abstract class NodeBase {     self: Node => // requeriment que el tipus propi sigui subtipus de Node en obtenir-ne una classe concreta     def connectaAmb(n: Node): Arc =       new Arc(self, n);   // ara si que passa la compilació   } }  // classe concreta de Graf que incorpora la classe concreta Node class GrafEtiquetat extends Graf {   class Node(etiq: String) extends NodeBase {     def getEtiqueta: String = etiq;   } } 

Anàlisi per casos del subtipatge per especialització

Refinament de tipus (ang: Downcasting) al d'una classe derivada, amb caracterització mitjançant asInstanceOf prèvia comprovació amb isInstanceOf.

def fun(param: Tipus) = if (param.isInstanceOf[Subtipus]) {                                 val subtipParam = param.asInstanceOf[Subtipus];                                 ... }                          else ... 

Enumeracions

object Main extends App {      object DiaSetm extends Enumeration {       type DiaSetm = Value       val Dl, Dt, Dc, Dj, Dv, Ds, Dg = Value     }    import DiaSetm._      def ésDiaFeiner(d: DiaSetm) = ! (d == Ds || d == Dg)      println (DiaSetm.values filter (ésDiaFeiner) mkString (", "))   } 
$ sbt run Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256M; support was removed in 8.0 [info] Set current project to enums (in build file:/home/gabi64/lleng/scala/enums/) [info] Compiling 1 Scala source to /home/gabi64/lleng/scala/enums/target/scala-2.10/classes... [info] Running Main  Dl, Dt, Dc, Dj, Dv [success] 

Parametrització de tipus amb cota superior

Admetre tipus més especialitzats que un tipus determinat.

trait Similar {   def isSimilar(x: Any): Boolean }  case class MyInt(x: Int) extends Similar {   def isSimilar(m: Any): Boolean =     m.isInstanceOf[MyInt] &&     m.asInstanceOf[MyInt].x == x }  object UpperBoundTest extends App {    def findSimilar[T <: Similar](e: T, xs: List[T]): Boolean =    if (xs.isEmpty) false     else if (e.isSimilar(xs.head)) true     else findSimilar[T](e, xs.tail)    val list: List[MyInt] = List(MyInt(1), MyInt(2), MyInt(3))    println(findSimilar[MyInt](MyInt(4), list))   println(findSimilar[MyInt](MyInt(2), list)) } 

funcions amb llista d'arguments variable

El símbol '*' després del tipus del darrer argument, indica que el paràmetre actual corresponent pot aparèixer zero o més vegades, i que cal prendre aquest argument com una seqüència.

def imprimeix(args: String*) = {   for(arg <- args){          println(arg);   } } imprimeix("abc", "def", "ghi") 

funcions anònimes com a expressió

exemple:

(x: Int, y:Int) => x + y         // el tipus del resultat és deduït per inferència en aquest cas 

En l'exemple següent el compilador dedueix els tipus dels paràmetres de la funció sobre la qual s'aplica.

object Main extends App {   val llista = List(1,2,3,4);  // pels literals infereix tipus List[Int]    // aplica la funció a un acumulador i cada valor de la llista   // els tipus dels paràmetres els dedueix de l'aplicació parcial (funció ometent paràmetres) precedent.   //     tipus de llista.foldleft(0) : (Int * Int) => Int    val result = llista.foldLeft(0) { (x,y) => x+y };     Console.print("resultat: ") ;   Console.println(result); } 

dona l'esperat

resultat: 10 

Aplicació parcial d'operadors i mètodes

L'aplicació parcial d'operadors s'expressa substituint un o més operands pel comodí '_' en una expressió. Ex. (3 + _) : (Int => Int)

  • També es pot referir a mètodes: (_.toString) : (Any => String)
scala> :type List[Any](1, 2.5, "abc") .map (_.toString) List[String]  scala> def floatMes3: (Float => Float) = (_ + 3)    // cal la restricció de tipus floatMes3: Float => Float 

Aplicació parcial d'una funció

Per obtenir el valor de funció dels mètodes def d'un object (que són mètodes estàtics) i poder passar-lo com a paràmetre a una funció d'ordre superior, cal substituir els paràmetres no aplicats amb un únic comodí '_'.

Scala no té polimorfisme d'ordre superior (ang:impredicative polymorphism), per tant les funcions que es passen com a paràmetre no poden ser polimòrfiques (cal donar valor als seus paràmetres de tipus).

// Definint ''flip'' (intercanviar l'ordre d'arguments) per fer-lo servir més tard scala> def flip[T1, T2, R](f : T1 => T2 => R) (x: T2) (y: T1) : R = f (y) (x) flip: [T1, T2, R](f: T1 => (T2 => R))(x: T2)(y: T1)R  scala> def cons[A] (x: A) (xs: List[A])  = x :: xs cons: [A](x: A)(xs: List[A])List[A]  scala> :type cons[Int] (5) _            // tipus aplicant només un paràmetre List[Int] => List[Int]  scala> :type cons[Int] _                // tipus aplicant zero paràmetres Int => (List[Int] => List[Int])   scala> flip (cons[Int] _) (List(2,3)) (1)    // passant 'cons' a 'flip' (funció d'ordre superior) res0: List[Int] = List(1, 2, 3) 

Composició i currificació de funcions

La interfície Function1 de funcions d'un argument aporta mètodes per la composició de funcions.

La interfície Function2 de funcions de dos arguments aporta mètodes per obtenir versions currificades (.curried) i la inversa (.tupled).

Function3 a Function19 també definides.

package test  object Main extends App {      // composició de les funcions d'un arg.      def doble(x: Int) = x * 2     def suma3(x: Int) = x + 3      // f compose g == f. g     // f andThen g == g. f      // cal especificar '_' per obtenir l'aplicació parcial     def doblaiSuma3 = (doble _).andThen(suma3 _)   // composició d'esquerra a dreta     def doblaiSuma3 = (suma3 _).compose(doble _)   // composició amb estil matemàtic, resultat idèntic a l'anterior      val prova = doblaiSuma3(5)      // currificació i aplicabilitat als arg. en tupla      def cons[A] (x: A, xs: List[A])  = x :: xs      def f[T] = (cons[T] _)  // cal especificar '_' per obtenir l'aplicació parcial      val x1 = f (1, List(2,3))       // currificació (amb 'curried'' la funció s'aplica als param. separats     val x2 = f[Int].curried (1) (List(2,3))   // cal explicitar el paràmetre actual de tipus  } 

funcions d'ordre superior

Amb funcions als paràmetres o al resultat.

// en aquest cas, funció que pren una funció d'argument i en retorna una altra  def sum(f: Int => Int): (Int, Int) => Int = {    def sumF(a: Int, b: Int): Int =             // suma f(x), x <- [a..b]    if (a > b) 0 else f(a) + sumF(a + 1, b)    &sumF }  def sumaDobles = &sum(x => 2 * x) def sumaQuadrats = &sum(x => x * x) 

Definició per patrons d'una funció - El tret PartialFunction

Vegeu "Pattern-matching anonymous functions".

def or : (Boolean, Boolean) => Boolean =            { case (true, _) => true;              case (false, x) => x           } 

Equival a la definició per encaixos dels llenguatges funcionals (ex.: Haskell) amb un sol paràmetre i un resultat.

Una definició per seqüència de patrons, si no són exhaustius, és una funció definida parcialment.

Les funcions amb definició per patrons implementen el tret PartialFunction[A /*entrada*/, B /*resultat*/], amb mètodes molt interessants.

val pf: PartialFunction[A, B] = { case patró1 if condició => ...;                                    case patró2 => ... ;                                   ... } 

La funció pf.isDefinedAt(x:A) retorna si el paràmetre encaixa en alguna de les clàusules case (filtres compresos) !!

El mètode lift com a pf.lift embolcalla el resultat en un Option oferint una versió total de la funció parcial.

El mètode applyOrElse permet especificar una funció per defecte per als casos no definits.

pf.applyOrElse(x, funcPerDefecte)  if(pf isDefinedAt x) pf(x) else funcPerDefecte(x) 

El mètode orElse admet la composició amb una altra funció parcial, que s'avalua per als casos no definits de la primera.

scala> val dobleDeParell: PartialFunction[Int, Int] = {      |   case x if x % 2 == 0 => 2 * x      | } dobleDeParell: PartialFunction[Int,Int] = <function1>  scala> dobleDeParell.isDefinedAt(3) res0: Boolean = false  scala> dobleDeParell.isDefinedAt(2) res1: Boolean = true  scala> dobleDeParell.lift(3) res2: Option[Int] = None 
  • A les col·leccions:

collect mapeja amb una funció parcial obtenint la col·lecció de les imatges d'aquells elements per als quals la funció està definida:

// interfície GenTraversable def collect[B](pf: PartialFunction[A, B]): Repr[B]  // interfície GenTraversableOnce def collectFirst[B](pf: PartialFunction[A, B]): Option[B]  // obté, si existeix, la primera de les imatges de mapejar amb una funció parcial 

Excepcions

Excepcions en la Gestió de recursos

La clàusula try {..} catch (err:Throwable) {..} finally {..} possiblita la gestió d'excepcions i alliberament de recursos.

L'objecte Exception aporta diverses possibilitats addicionals.

gestió automàtica

La recent construcció a Java 7 try-with-resources que encapsula el recurs i el seu alliberament ha fet repensar una clàusula semblant a Scala. Per a l'anomenat "Loan pattern" o patró del préstec (perquè el recurs s'ha de tornar), es proposa la següent construcció (Closeable és una interfície de Java)

def using[A, R <: Closeable](r : R)(f : R => A) : A = {     var result : A = _     try {         result = f(r)     } finally {         try {r.close()}          catch {case e: IOException => ()}     }     result   } 

La biblioteca scala-arm proporciona una implementació a través de la implementació monàdica de les clàusules for, que evita el niuament de construccions en cas d'adquisició de múltiples recursos.

import resource._  // dos recursos, còpia de input a output, amb alliberament automàtic  for(input <- managed(new java.io.FileInputStream("test.txt");       output <- managed(new java.io.FileOutputStream("test2.txt")) {    val buffer = new Array[Byte](512)   def read(): Unit = input.read(buffer) match {     case -1 => ()     case  n => output.write(buffer,0,n); read()   }   read() } 

Anàlisi per casos de l'èxit o fracàs de l'avaluació. La classe Try

El generador Try(expr) avalua una expressió que pot llançar excepcions, permetent analitzar per casos l'èxit o fracàs de l'avaluació, oferint com a resultat dues variants d'encaix (case classes) de la classe Try[T] que són Success(resultat:T) i Failure(excepció:Throwable).

A més implementa la classe de tipus Mònada amb (Failure(e:Throwable)) com a element absorbent de l'encadenament, de manera que en una clàusula for (avaluació monàdica) la fallada fa que no s'avaluïn les clàusules subsegüents del for evitant la necessitat de comprovació de l'èxit a cada pas.

A l'exemple el tipus del for és com sempre el del primer generador, en aquest cas un tipus Try.

val res = for (dividend <- Try(Console.readLine("entreu dividend: ") ;                divisor <- Try(Console.readLine("entreu divisor: ")          ) yield dividend / divisor res match {       case Success(v) => println ("correcte: " + v.toString)       case Failure(err) => println ("error de lectura (del readLine) " + err.getMessage)       } 

Disseny per Contracte

Precondicions, postcondicions i invariants

Assercions

Vegeu Assercions i Opció de compilació -enableassertions

    assert(predicat [,missatge])
    especifica un predicat que cal provar i dispara l'excepció java.lang.AssertionError: assertion failed: missatge
    assume(axioma [,missatge])
    especifica un axioma (asserció que les eines de comprovació estàtica de tipus no han de deduir de proposicions precedents). En temps d'execució actua com un assert disparant l'excepció java.lang.AssertionError: assumption failed: missatge
    error(missatge)
    per al cas d'estats inconsistents o bé de funció no definida per un cas dels paràmetres
    ???
    indica implementació pendent, dispara UnsupportedOperationException("not implemented").
def mètode : Tipus = ???   // mètode pendent d'implementar (per poder passar la compilació) 
Precondició

La clàusula require ha estat declarada obsoleta i retirada a la v2.8.

Es recomana fer servir la definició per patrons per especificar les precondicions.

Postcondició
    {acció} ensuring (_ => condició [,missatge])
    asserció com a funció sobre el resultat d'un bloc de codi.
// malament a posta, per fer petar la postcondició scala> def incrementa(x: Int) = { x - 1 } ensuring (_ > x, "el resultat no és superior") incr: (x: Int)Int  scala> incrementa (5) java.lang.AssertionError: assertion failed: el resultat no és superior   at scala.Predef$Ensuring$.ensuring$extension3(Predef.scala:256)   at .incr(<console>:10)   ... 43 elided 

Anàlisi per casos de l'avaluació de funcions parcials

És interessant evitar les funcions que disparen error() degut a paràmetres il·legals o estats inconsistents, i obtenir-ne versions amb resultat opcional (Option[T]) que obliga a analitzar el resultat a la rutina que fa la crida.

Vegeu també #Definició per patrons d'una funció - El tret PartialFunction

Igualment les funcions que disparen excepcions poden ser avaluades amb Try(expressió) oferint resultat de tipus Try[T] que retorna o bé el resultat, amb la variant Success[T](v:T), o bé l'excepció amb Failure(e:Throwable), evitant que aquesta es propagui més amunt.

XML al codi

Admet notació de llenguatge de marques XML com a literal, amb interpolació d'expressions entre claus "{}". Vegeu

object Main extends App {   val idioma = "ca" ;   val títol = "El meu títol" ;   val xmlTítol = <title> {títol} title>;    // entre claus '{'  '}' interpolació d'expressions.   val xmlCapçalera = <head> {xmlTítol} head>;   val xmlCos = <body> Aquesta pàgina duu per títol: {títol} body> ;    // substitució a l'atribut, hi afegeix les cometes.   val xmlPàgina = <html lang={idioma}> {xmlCapçalera} {xmlCos} html> ;    // prefix   val capsalXml ="\n" ;   val doctype = " +            " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" ;   println(capsalXml + doctype + xmlPàgina) ; } 

El paquet de tractament de XML scala.xml, que anteriorment formava part de la biblioteca base de Scala, ha estat separat en un paquet Jar independent que es pot obtenir amb el codi font al GitHub d'aquí.

Implícits

Scala té diversos mecanismes relacionats amb el tipus que busquen automàticament entre les instàncies visibles en l'àmbit,

    • o bé la conversió implícita de tipus,
    • o bé, en cas d'un genèric de tipus (classe de tipus), la implementació corresponent al tipus emprat (tipus del paràmetre actual).

Vistes de tipus (conversions implícites)

Si el tipus del paràmetre actual no correspon amb el del paràmetre formal, però existeix una funció de conversió implícita entre ambdós tipus:

// del Predef // char2int permet emprar valors de tipus Char en paràmetres Int  implicit def char2int (x: Char): Int 

Restricció de Convertibilitat (View bound)

És una alternativa menys estricta que un requeriment de subtipatge.

type A <% T // indica que cal que existeixi una conversió implícita de A a T. Vegeu Scala Views

trait Set[A <% Ordered[A]]... // consulteu la ref. 

Instàncies implícites de classes de tipus

El mecanisme dels implicit objects permet obtenir la instància (implementació) d'una classe de tipus (classe abstracta genèrica parametritzada per un tipus) per al tipus del paràmetre actual.

A l'exemple el paràmetre (implicit m: Monoid[A]), m obtindrà el valor de la instància específica del genèric Monoid[A] per al tipus del paràmetre actual emprat en la crida a la funció def sumaLlista[A](xs: List[A])(implicit m: Monoid[A]).

    abstract class SemiGrup[A] {       def suma(x: A, y: A): A     }      abstract class Monoid[A] extends SemiGrup[A] {       def zero: A     }     object ImplicitTest extends App {        // mòdul instància de Monoid per al tipus String       implicit object StringMonoid extends Monoid[String] {         def suma(x: String, y: String): String = x concat y         def zero: String = ""       }        // mòdul instància de Monoid per al tipus Int       implicit object IntMonoid extends Monoid[Int] {         def suma(x: Int, y: Int): Int = x + y         def zero: Int = 0       }        // sumaLlista       // el paràm. implícit selecciona instància segons el tipus del paràm. actual (a la crida)             // entre els ''implícit object''s visibles en l'àmbit.        def sumaLlista[A](xs: List[A])(implicit m: Monoid[A]): A =           xs match {               case Nil => m.zero               case y :: ys => m.suma(y, sumaLlista(ys))          }        println(sumaLlista(List(1, 2, 3)))  // obté l'objecte implícit segons el tipus del paràmetre.       println(sumaLlista(List(1, 2, 3)) (IntMonoid))   // explicita l'implícit quan n'hi ha diversos (Monoide per a la suma, per al producte, ...)        println(sumaLlista(List("a", "b", "c")))     } 
Restricció de context (Context bound) i obtenció d'instàncies amb implicitly

Introdueix el requeriment d'instanciació de classes de tipus (genèrics) en el paràmetre de tipus.

La restricció de context [A: Monoid] indica que cal que hi hagi una instància de Monoid per al tipus A, visible en l'àmbit d'ús.

Codificació alternativa de sumaLlista. La instància de Monoid s'obté amb implicitly.

      def sumaLlista[A: Monoid](xs: List[A]): A = {                val m = implicitly[Monoid[A]]     // obté una instància de Monoid per al tipus A          xs match {              case Nil => m.zero              case y :: ys => m.suma(y, sumaLlista(ys))              }         } 
Restricció de context respecte a Paràmetre implicit - Ordered i Ordering

Ordered[A] i Ordering[A] ofereixen una funcionalitat similar

  • El tret Ordered[A] equival a la interfície "Ordenat" i implementa la ordenació bàsica del tipus i només se'n defineix una de sola. Es pot especificar com a requeriment de context
  def nomFunció [A:Ordered](params...) = ... 
  • El tret Ordering[A] (Ordenació) equival a una classe de tipus instanciable en implicit objects per tipus, i en podem tenir diverses instàncies per al mateix tipus, amb relacions d'ordre diferents (ascendent, descendent, basada en un component, ...). Per explicitar la que vulguem, s'ha de fer servir com a paràmetre implícit.
  def nomFunció [A](params...)(implicit m:Ordering[A]) = ... 
Injecció de mètodes

La conversió implícita d'un tipus a una classe o tret, té l'efecte d'augmentar els mètodes per al tipus que es converteix.

object Test extends App {    object Augmentador {      class ToMyInt(s:String) {       def toMyInt: Int = java.lang.Integer.parseInt(s)     }     implicit def string2ToMyInt(s: String) = new ToMyInt(s)   }   import Augmentador._  // incorpora l'implícit a l'àmbit    println ("12".toMyInt)  } 
  • Augment ràpid (en escriure'l però més lent d'execució).
object Test extends App {   implicit def string2ToMyInt(s: String) = new {      def toMyInt: Int = java.lang.Integer.parseInt(s)   }    println ("12".toMyInt)  } 

Convenció per indicar efectes col·laterals en els mètodes sense paràmetres

  • Per convenció, els mètodes sense paràmetres podran ometre els parèntesis, a la definició i en l'ús, per indicar que no inclouen efectes col·laterals.
object Demo {   val valor = ...   def llegir() = ...  // efectes col·laterals, cal posar els parèntesis    def obtenirValor = ... // no hi ha efectes col·laterals => no s'hi han de posar els parèntesis } 

Avaluació tardana (ang: lazy)

    valors lazy val (retarda l'avaluació fins que fa falta en una expressió)
    amb el qualificador lazy, l'expressió només s'avalua quan fa falta alguna variable del patró, i, a més a més, per l'ús de val memoritza el valor (aprofita el resultat en crides posteriors)
 lazy val patró = expressió 
    paràmetres per-nom (param: => tipus) (Call by name)
    afegint => abans del tipus, aquests paràmetres s'avaluen substituint el nom del paràmetre en el codi per l'expressió del paràmetre actual.
object Main extends App {   var cnt = 0   def perNom(n: => Int) = {           println(cnt);            println(n); // aquí se substitueix el nom del param. per l'expressió del param. actual           println(n); // aquí també       }   perNom({ cnt += 1; cnt}) } 

execució a l'intèrpret:

scala> Main 0 1 2 
    Vistes
    Avaluació no-estricta de les col·leccions.
    Traversable.view
    genera una "vista d'avaluació diferida" d'una col·lecció, resultant un tipus TraversableView. Les transformacions que s'hi apliquin (map, filter, etc.), en comptes de generar una nova estructura, quedaran pendents d'aplicació generant una nova vista, fins que s'hi apliqui el mètode force.
    Traversable.toIterator
    permet recórrer una estructura una sola vegada (l'iterador muda de valor a cada 'next()'). Els elements s'obtenen per necessitat, evitant la generació d'estructures intermèdies en les transformacions successives de l'estil de map, filter, fold.
    Traversable.toStream
    permet obtenir-ne els elements de manera tardana (per necessitat). Els streams es poden recórrer tants cops com calgui a partir d'una referència, per què hi ha memoïtzació (memorització en una taula dels resultats evitant el recàlcul).

evitant la necessitat de desforestació

En l'aplicació funcional successiva, ex.: estructura.filtre(predicat).map(funcióDeTransformació).reducció(op_binària), les classes Iterator i Stream, en recuperar els elements de manera tardana, eviten la problemàtica de la desforestació (malversació d'espai per la creació d'estructures intermèdies temporals quan l'avaluació és estricta) i la necessitat d'escriure rutines multifunció filtreMapReducció(predicat, funcióDeTransformació, op_binària) d'un sol bucle per solucionar-ho.

Intervals numèrics

La classe Range permet definir iteradors (avaluació tardana) sobre seqüències numèriques de tipus Int.

La classe NumericRange[T] generalitza la classe Range a sencers de diferents precisions (Han d'implementar Integral[T] (anell unitari íntegre ordenat euclidià: ops. dels enters).

(0 to 2).toList         // interval tancat [0,2]:  List(0,1,2) (0 until 2).toList      // interval obert per la dreta [0,2): List(0,1)  (2 to 0 by -1).toList   // interval amb increment: List(2,1,0) 

La API de Scala

Scala té una llarga biblioteca per facilitar la feina amb varietat de col·leccions immutables i mudables així com tractament de XML i altres que trobareu aquí.

L'especificació oficial del llenguatge és aquí.

Els elements predefinits són a l'object Predef

Cercadors de l'API

La API de Scala www.scala-lang.org/api/current/ permet cercar per nom de paquet, o signatures d'àmbit global.

  • Hi ha un cercador d'índex de biblioteques a la mateixa web anomenat "Scaladex": index.scala-lang.org
  • La web Scalex.org Arxivat 2020-11-11 a Wayback Machine. permetia, fins fa poc, cercar els identificadors exportats per un paquet, de manera similar a Hayoo o bé Hoogle per al llenguatge Haskell. Tanmateix se'n pot descarregar el codi del Github.

Biblioteques

Alguns paquets que abans estaven inclosos a la biblioteca bàsica, com ara scala.xml, ara s'han de referenciar separadament des del GitHub de Scala.

La llista de biblioteques de tercers és a Scala wiki - Tools and Libraries.

Scala Actors -- Concurrència amb el model d'Actors

Vegeu ref.

STM (Transaccions de memòria per programari)

Vegeu ref.

Col·leccions

Vegeu ref.

Gràfic de les col·leccions immutables.

Gràfic de les col·leccions mudables.

    paral·lelisme
    Scala també disposa de col·leccions per al paral·lelisme, per aprofitar la potència dels processadors multicor amb algorismes que utilitzen l'estratègia "dividir i vèncer". El mètode par ofereix implementacions amb paral·lelisme de la majoria de col·leccions que implementen el tret ParIterable amb l'ajut d'un particionador (Splitter) que ofereix un iterador sobre les subcol·leccions disjuntes resultants de la partició.
(col·lecció.par.map (_ * 2)).toList  // multiplica, paral·lelitzant, els elements d'una col·lecció de numèrics [http://www.scala-lang.org/api/current/index.html#scala.math.Numeric] // el factor 2 és convertit al tipus de l'element si existeix una conversió implícita 
    concurrència
    Existeix una versió de diccionaris TrieMap (basats en arbres de prefix i taula de dispersió (ang:hash-array)) per a l'accés concurrent (simultani des de diversos fils d'execució) i lliure de baldes (ang:lock-free).

operacions comunes dels mòduls estàtics (object) que acompanyen les col·leccions

Vegeu "Creating collections".

// Generador de col·lecció buida def empty[A]: Col·lecció[A]  // Generador partint d'elements oferts en una crida de nombre variable de paràmetres def apply[A](xs: A*): Col·lecció[A]  // el nom apply es pot ometre, és el nom de funció per defecte // Col·lecció(elem1, elem2, ...) equival a Col·lecció.apply(elem1, elem2, ...) 

operacions comunes de les classes de les col·leccions

def isEmpty: Boolean def nonEmpty: Boolean  // companion: 'Object' fàbrica d'instàncies de la col·lecció (generadors i altres mètodes estàtics) def companion: GenericCompanion[Col·lecció]  def sameElements(that: GenIterable[A]): Boolean  // compara els elements amb la col·lecció 'that'  def iterator: Iterator[A]      // obté un iterador (mètodes: hasNext(), next()) sobre els elements  def mkString: String                 // mostra la col·lecció en una String def mkString (sep: String): String    // ... intercalant separador  def toString(): String            // a String def toArray: Array[A]             // a Array, correspon als vectors del Java del tipus (A []) def toBuffer[B >: A]: Buffer[B]   // a Buffer (magatzematge temporal incrementable)  def toSeq: Seq[A]                 // a seqüència, implementada amb List def toSet[B >: A]: Set[B]         // a conjunt def toList: List[A]               // a llista def toIndexedSeq: IndexedSeq[A]   // a seqüència d'accés aleatori, implementada amb Vector def toVector: scala.Vector[A]     // a vector, implementació per defecte de les IndexedSeq  def toMap[T, U]: collection.Map[T, U]   // a diccionari, si i només si els elements són parells (A ~ (T, U))  def toIterator: Iterator[A]       // obté un iterador (travessable un sol cop), equival al mètode 'iterator' def toStream: Stream[A]           // obté un Stream (travessable més d'un cop)   def toTraversable: collection.Traversable[A] // a Travessable (mètode forEach) // retorna la mateixa instància si la col·lecció ja ho és.  def to[Col[_]]: Col[A]            // a col·lecció explicitant el paràmetre de tipus  scala> (1 to 3).to[List] res1: List[Int] = List(1, 2, 3)  scala> (1 to 3).to : List[Int]    // o bé mitjançant una restricció de tipus  res2: List[Int] = List(1, 2, 3) 
  • Seqüències (entre d'altres):
// apply: obtenir element enèsim // el nom apply es pot ometre, és el nom de funció per defecte: // l'element de la col·lecció xs escrit xs(n) equival a xs.apply(n)  def apply(idx: Int): A               // obtenir l'element a l'índex 'idx'  def isDefinedAt(idx: Int): Boolean    // està definit per a l'índex 'idx' ?  def reverseIterator: Iterator[A]    // iterador en sentit invers 
  • IndexedSeq: seqüències d'accés aleatori: ArrayBuffer, Vector, ...
  • LinearSeq: seqüències d'accés lineal (d'un amb un)
    • avaluació estricta: List, Queue
    • avaluació tardana: Iterator, Stream
    • avaluació diferida: TraversableView (difereix l'avaluació dels mètodes aplicats, fins que s'aplica .force o se'n demana la conversió a una col·lecció concreta)
  • Altres signatures: Traversable, Iterable, Set, Map

Interfície TraversableOnce

Vegeu ref.

  • interfície per les operacions de Reducció d'una col·lecció a un valor (gramaticalment: A* ⇒ A)
    // A és el tipus de l'element de la col·lecció      def foldLeft[B](z: B)(op: (B, A)  B): B    // plegat per l'esquerra sobre valor inicial     def foldRight[B](z: B)(op: (A, B)  B): B   // plegat per la dreta sobre valor inicial      // [A1 >: A] vol dir que els paràmetres es tipifiquen per algun tipus A1 supertipus de A      // si l'operació del plegat és associativa en el domini dels elements,      // tindrem el mateix resultat per l'esquerra com per la dreta, per tant no cal explicitar-ho.     def fold[A1 >: A](z: A1)(op: (A1, A1)  A1): A1    // plegat endomòrfic sobre valor inicial amb op. associativa      // (z /: xs) equival a (xs foldLeft z) operador associatiu per la dreta (xq. acaba en ':')     def /:[B](z: B)(op: (B, A)  B): B         // (xs :\ z) equival a (xs foldRight z) op. associatiu per l'esquerra.     def :\[B](z: B)(op: (A, B)  B): B         def reduceLeft[B >: A](op: (B, A)  B): B       // plegat per l'esquerra de la cua sobre el cap (col·l. no buida); func. parcial !!             def reduceLeftOption[B >: A](op: (B, A)  B): Option[B]      // versió total (col·l. buida => None)      def reduce[A1 >: A](op: (A1, A1)  A1): A1    // plegat endomòrfic de la cua sobre el cap, amb op. associativa (col·l. no buida); func. parcial !!     def reduceOption[A1 >: A](op: (A1, A1)  A1): Option[A1]     // versió total (col·l. buida => None) 
    • plegats sobre domini que implementi Numèric, math.Numeric és la signatura d'un Anell unitari ordenat
    • plegats de col·leccions d'elements ordenats, o via projecció a imatge ordenable.
    def sum[A: Numeric]: A     def product[A: Numeric]: A      // plegats; el tipus de l'elem ha d'ésser subtipus d'algun que implementi Ordering (Ordenació)     //    quina instància d'ordenació (ord. ascendent, descendent, s/. l'ordre d'un component) es pot adjuntar com a param.     def min[B >: A](implicit cmp: Ordering[B]): A     def max[B >: A](implicit cmp: Ordering[B]): A      // plegats via funció de projecció amb imatge ordenable quina instància d'ordenació es pot adjuntar com a param.     def minBy[B](f: (A)  B)(implicit cmp: Ordering[B]): A     def maxBy[B](f: (A)  B)(implicit cmp: Ordering[B]): A 

Interfície Traversable

Interfície TraversableLike per al recorregut (visita dels elements) d'una col·lecció; defineix foreach i conté la major part dels mètodes de Traversable, afegeix a TraversableOnce, a banda d'altres trets, els mètodes següents

  • Consulta
    // A és el tipus de l'element de la col·lecció de tipus Repr      def isEmpty: Boolean             // és buida ?     def nonEmpty: Boolean            // és no buida ?     def size: Int                    // mida      def hasDefiniteSize: Boolean     // és finita la col·lecció ?      def isTraversableAgain: Boolean  // és travessable altra vegada? (els iteradors només una)       // com a seqüència     def head: A                      // cap, funció parcial!!! excepció NoSuchElementException si col. buida     def headOption: Option[A]        // cap, versió total     def tail: Repr                   // cua, funció parcial!!! excepció NoSuchElementException si col. buida      // quantificadors amb predicat     def exists (predicat: (A)  Boolean): Boolean  // algun element compleix el predicat?     def forall (predicat: (A)  Boolean): Boolean  // el compleixen tots?     def count (predicat: (A)  Boolean): Int       // compta els que compleixen      // cerca per predicat     def find (predicat: (A)  Boolean): Option[A]  // cerca per predicat       // altres components     def last: A    // darrer. funció parcial!!! excepció NoSuchElementException si col. buida     def lastOption: Option[A]  // darrer, versió total     def init: Repr // descarta el darrer. funció parcial!!! excepció UnsupportedOperationException si col. buida      // tails: successió de subcol·leccions escapçant pel davant     def tails: Iterator[Repr]  // equival a ((0 to col.size).map (col.drop))      // inits: successió de subcol·leccions escapçant pel darrere     def inits: Iterator[Repr] //  equival a ((col.size to 0 by -1).map (col.take)) 
  • Combina
    def ++ [B](that: GenTraversableOnce[B]): Traversable[B]  // (xs ++ that) concatena associatiu per l'esquerra      // els operadors acabats en ':' (associatius per la dreta) en notació infix, són mètodes de l'operand de la dreta       def ++: [B](that: TraversableOnce[B]): Traversable[B]    // (that ++: xs) concatena associatiu per la dreta 
  • Visita i/o transforma
    def foreach [U](f: Elem => U): Unit   // iteració amb funció d'efectes laterals     def map [B](f: (A)  B): Traversable[B]  // mapeja (obté la col·lecció de les imatges de l'aplicació f)      // encadena amb funció de resultat múltiple (col·lecció) i concatena resultats      def flatMap [B](f: (A)  GenTraversableOnce[B]): Traversable[B]  // equival a l'encadenament monàdic del llenguatge Haskell (>>=)      // collect i collectFirst ofereixen un mapeig amb funcions que implementen el tret PartialFunction,      // retornant la col·lecció de les imatges corresponents als elements per als quals la funció `pf` està definida.      def collect [B](pf: PartialFunction[A, B]): Traversable[B]     def collectFirst [B](pf: PartialFunction[A, B]): Option[B]   // obté, la primera de les imatges dels elements vàlids en el subdomini de 'pf' 
  • Partició
    def slice (from: Int, until: Int): Repr  // subcol·lecció corresp. a l'interval obert per la dreta [from .. until) (exclou límit superior)      // a l'índex     def take (n: Int): Repr  // prefix de n elements     def drop (n: Int): Repr  // escapça el prefix de n elements     def splitAt (n: Int): (Repr, Repr)  // partició a l'índex, com {col => (col.take(n), col.drop(n))}      // segons predicat     def filter (predicat: (A)  Boolean): Repr // subcol·lecció dels que compleixen      def partition (predicat: (A)  Boolean): (Repr, Repr) // parteix com {col => (col.filter (predicat), col.filter (not predicat))}      def takeWhile (predicat: (A)  Boolean): Repr // prefix mentre elements compleixen     def dropWhile (predicat: (A)  Boolean): Repr // sufix de takeWhile(predicat)      def span (predicat: (A)  Boolean): (Repr, Repr)   // parteix com {col => (col.takeWhile(predicat), col.dropWhile(predicat))}      // agrupa els elements en funció de les imatges de 'f', en un diccionari de subcol·leccions     def groupBy [K](f: (A)  K): immutable.Map[K, Repr]      // monàdic, per a ser emprat a les clàusules 'for' i llistes per comprensió (encadenament monàdic)     def withFilter (predicat: (A)  Boolean): FilterMonadic[A, Repr]   // filtre monàdic      // TraversableView difereix les transformacions posteriors fins que s'aplica '.force' o es converteix a col·lecció concreta.     def view: TraversableView[A, Repr] // vista d'avaluació diferida     def view (from: Int, until: Int): TraversableView[A, Repr]  // llesca d'avaluació diferida 
  • Reducció
    // successió de plegats parcials per l'esquerra     def scanLeft [B, That](z: B)(op: (B, A)  B)(implicit bf: CanBuildFrom[Repr, B, That]): That       // successió de plegats parcials per la dreta     def scanRight [B, That](z: B)(op: (A, B)  B)(implicit bf: CanBuildFrom[Repr, B, That]): That        // scan: successió de plegats parcials amb op. associativa (tant és si el plegat és per la dreta o per l'esquerra)     def scan [B >: A, That](z: B)(op: (B, B)  B)(implicit cbf: CanBuildFrom[Repr, B, That]): That       // concatena elements de col·lecció de col·leccions travessables     def flatten [B](implicit asTraversable: (A)  GenTraversableOnce[B]): Traversable[B] 

El tret CanBuildFrom

El tret CanBuildFrom[-ColOrigen, -Elem, +ColDestí] descriu una interfície genèrica per la generació de col·leccions des d'altres col·leccions amb implementacions optimitzades per a tipus específics quines instàncies (objectes implícits) han d'estar visibles.

És d'interés quan, com a sortida d'un mètode d'una col·lecció, es vol crear una col·lecció diferent. Per això només caldrà especificar, amb una restricció del tipus resultant, la col·lecció de destinació.

Interficie Iterable

IterableLike, caracteritzada per definir un iterador que faciliti visitar els elements, i que conté la major part dels mètodes d'Iterable afegeix a TraversableLike entre d'altres els següents:

  • partició:
def takeRight(n: Int): Repr              // obtenir sufix de llargada n def dropRight(n: Int): Repr              // escapçar el sufix  def grouped(n: Int): Iterator[Repr]   // retorna iterador de trams de llargada fixa n 
  • combinació:
def zip[B](that: GenIterable[B]): GenIterable[(A, B)]   // aparellament per posició amb altra col·lecció  // zipAll: zip igualant llargades amb farciment def zipAll[B](that: collection.Iterable[B], thisElem: A, thatElem: B): GenIterable[(A, B)]  def zipWithIndex: GenIterable[(A, Int)]    // aparella una seqüència amb l'índex correlatiu (partint de zero) 

mètodes de consulta específics

// a les seqüències def apply (n: Int): A  // obtenir elem. enèsim partint de zero: xs(3). funció parcial!!! amb IndexOutOfBoundsException  // comprova amb una segona seqüència si es compleix un predicat entre els corresponents posicionals  def corresponds [B](that: GenSeq[B])(predicat: (A, B)  Boolean): Boolean  // als conjunts def contains (key: A): Boolean def subsetOf (that: GenSet[A]): Boolean  // és subcjt de ?  def subsets (len: Int): Iterator[Set[A]]  // subcjts de llarg. específica def subsets: Iterator[Set[A]]             // tots els subconjunts  // als diccionaris def get (key: A): Option[B]  // als opcionals ''Option[+A]'' def getOrElse[B >: A](default:  B): B      // cas de la variant (Some x) n'extreu el valor, altrament avalua el param.  def toRight[T](left:  T):Either[T, A]      // cas de la variant (Some x) torna Right(x), altrament Left amb el param. def toLeft[T](right:  T):Either[A, T]      // cas de la variant (Some x) torna Left(x), altrament Right amb el param.  // al dual ''Either[+A, +B](Left(l:A) | Right(r:B))'' left.getOrElse [AA >: A](or:  AA): AA      // cas de la variant (Left a) n'extreu el valor, altrament avalua param. right.getOrElse [BB >: B](or:  BB): BB     // cas de la variant (Right b) n'extreu el valor, altrament avalua param.  left.toOption: Option[A]    // cas de Left(a) torna Some(a), altrament None right.toOption: Option[B]   // cas de Right(b) torna Some(b), altrament None  // al dual ''Try[+T] d'avaluació d'expressions que poden llançar excepcions //         avaluant a  Success(resultat:T) | Failure(excepció: Throwable)''  def get: T     // cas de la variant (Success resultat) retorna resultat, cas de (Failure excep) llança l'excepció def getOrElse [U >: T](default:  U): U  // cas de la variant (Success r) n'extreu el valor, altrament avalua el param.  def toOption: Option[T]   // cas de Success(resultat) retorna Some(resultat) altrament None 
def contains(elem: Any): Boolean  // està contingut?  def distinct: Seq[A]              // diferents (elimina elements repetits)  def diff (that: Seq[A]): Seq[A]  // multi-diferència, per cada elem. del de la dreta, elimina una ocurrència. def union (that: Seq[A]): Seq[A]      // multi-unió (concatena) def intersect (that: Seq[A]): Seq[A]  // multi-intersecció  def combinations (n: Int): Iterator[Seq[A]] def permutations: Iterator[Seq[A]] 
  • seqüències, accés indexat [0..
def indices: immutable.Range // retorna rang d'índexs  // obtenir element per posició (basada en 0), ''apply'' és el nom de funció per defecte i es pot ometre: xs(n) def apply (n: Int): A   // Funció parcial!!! IndexOutOfBoundsException  // cerca posició per valor def indexOf (elem: A [, from: Int]): Int    // retorna índex o bé (-1) def lastIndexOf (elem: A [, end: Int]): Int  // cerca posició per predicat def indexWhere (predicat: (A)  Boolean [, from: Int]): Int def lastIndexWhere (predicat: (A)  Boolean [, end: Int]): Int  // actualització funcional  amb associació (índex, elem) def updated [A1 >: A](index: Int, elem: A1): Repr   // retorna nova col·lecció actualitzada 
  • seqüències, consulta de subseqüències
def slice (from: Int, until: Int):Seq[A]  // subseq. a l'interval obert per la dreta  def containsSlice [B](that: GenSeq[B]): Boolean  // conté subseq. ?  def startsWith [B](that: GenSeq[B], offset: Int): Boolean  // comprova prefix def endsWith [B](that: GenSeq[B]): Boolean                 // comprova sufix  // cerca posició de subseq. def indexOfSlice [B >: A](that: GenSeq[B] [, from: Int]): Int     // retorna pos. o bé (-1) def lastIndexOfSlice [B >: A](that: GenSeq[B] [, end: Int]): Int  // retorna pos. o bé (-1)  // llarg. de subseq. dels elements que compleixen el predicat def prefixLength (predicat: (A)  Boolean): Int   // llargada de `takeWhile(predicat)` def segmentLength (predicat: (A)  Boolean, from: Int): Int   // llargada `drop(from).takeWhile(predicat)`  // obtenció de subseqüències desplaçant una finestra de la mida esmentada def sliding (size: Int): Iterator[Seq[A]] def sliding (size: Int, step: Int): Iterator[Seq[A]]   // step: especifica la distància entre inicis 
  • seqüències, ordenació
def sortWith (lt: (A, A)  Boolean): Seq[A]   // especificant la relació d'ordre  // sorted: si l'element és subtipus d'un ordenable, quina instància d'ordenació podem especificar def sorted [B >: A, B](implicit cmp: Ordering[B]): Seq[A]  // ordena segons la imatge ordenable d'una projecció quina instància d'ordenació podem especificar def sortBy [B](f: (A)  B)(implicit cmp: Ordering[B]): Seq[A] 

operadors d'actualització

Els operadors binaris en notació infix i associatius per la dreta (aquells quin símbol acaba en ':') s'avaluen sintàcticament com a mètodes de l'operand de la dreta. (exemple: x1 +: (x2 +: xs); la col·lecció va a la dreta quan l'operador acaba en ':')

Cas d'operacions amb dues versions funcional i imperativa, s'utilitza el mode verbal infinitiu per la versió imperativa i el mode verbal participi per la versió funcional. Per exemple a les seqüències d'accés aleatori (IndexedSeq[A]) update és la versió imperativa i updated la versió funcional.

a les seqüències

per a les seqüències dels tipus immutable.Seq[+A] i mutable.Seq[A].

funcionals purs a les seqüències
  • Operadors i mètodes funcionals purs (sense efectes col·laterals) de les seqüències (immutables o mudables)
      retornen nova seqüència
x +: xs // nova seq. afegint al capdavant (associatiu per la dreta xq. acaba en ':') xs :+ x  // nova seq. afegint pel final  xs ++ (ys: GenTraversableOnce[A]) // nova seq. concatenant amb elements de col·lecció (ys: TraversableOnce[A]) ++: xs // nova seq. prefixant elems. de col·lecció (op. associatiu per la dreta)  // updated: actualització funcional que retorna nova seq. actualitzada // cas de col·lecció covariant en el tipus de l'elem Col[+A], l'argument (posició contravariant)  // ha d'admetre tipus més generals: A1 tals que (A1 >: A). // altrament A1 = A def updated [A1 >: A](index: Int, elem: A1): Repr   // actualitza amb elem. a l'índex  // patch (apedaçar): retorna còpia inserint seqüència ''that'' a l'índex ''from'', substituint ''replaced'' elements def patch (from: Int, that: GenSeq[A], replaced: Int): Repr   // combina com a multi-conjunt xs intersect ys // multi-intersecció xs diff ys     // multi-diferència xs union ys    // multi-unió (concatenació)  def distinct: Seq[A]              // distints (nova seq. eliminant repetits)  // transposició d'una seqüència de seqüències def transpose [B](implicit asTraversable: (A)  GenTraversableOnce[B]): Seq[Seq[B]] 
mutadors a les seqüències
  • Operadors mutadors de les seqüències mudables (ListBuffer[A] i MutableList[A])
x +=: xs // afegir pel davant (associatiu per la dreta)  xs += x  // afegir al final xs -= x  // eliminar  xs.+= (x, y, ...)  // afegir diversos (llista d'arguments variable) xs.-= (x, y, ...)  // eliminar diversos  xs ++= (ys: TraversableOnce[A])  // afegir elems. de col·lecció travessable xs --= (ys: TraversableOnce[A])  // eliminar elems de col·lecció travessable  def transform(f: (A)  A): Seq.this.type // aplica una funció als elements de la seqüència  // només a ListBuffer (ys: TraversableOnce[A]) ++=: xs // afegeix al capdavant elements de col·lecció 
als Buffer

Buffer: estructura mudable de magatzematge temporal incrementable que permet eliminacions.

Per al tipus mutable.Buffer[A].

  • Mètodes mutadors als Buffers, addicionals als esmentats prèviament per a les seqüències
def append (elems: A*): Unit                         // afegeix diversos (llista d'arguments variable) def appendAll (ys: TraversableOnce[A]): Unit         // afegeix de col·lecció   def prepend (elems: A*): Unit                        // prefixa diversos def prependAll (ys: TraversableOnce[A]): Unit        // prefixa de col·lecció  def insert (n: Int, elems: A*): Unit                // insereix diversos a la posició n def insertAll (n: Int, ys: Traversable[A]): Unit   // insereix de col·lecció a la pos. n  def update (n: Int, newelem: A): Unit                // actualitza l'enèsim def remove (n: Int): A                               // elimina l'enèsim  def trimStart (n: Int): Unit                         // retalla pel davant n elems. def trimEnd (n: Int): Unit                           // retalla pel darrere n elems.  def clear(): Unit                                   // buida 
a les seqüències d'accés aleatori, anomenades indexades

per a seqüències d'accés aleatori dels tipus immutable.IndexedSeq[+A] i mutable.IndexedSeq[A]

  • Mètodes funcionals a les seq. indexades IndexedSeq (mudables o immutables)

updated (mode verbal participi) és la versió funcional del mutador update (verb en mode imperatiu), descrites tot seguit:

// updated: actualització funcional que retorna una nova seq. indexada amb l'actualització // cas de col·lecció covariant a l'elem. (+A), requereix que el tipus de l'elem. a l'argument (posició contravariant)  // ha d'admetre tipus més generals: A1 tals que (A1 >: A). // altrament A1 = A def updated[A1 >: A](índex: Int, elem: A1): IndexedSeq[A]  // retorna nova seq. d'accés aleatori 
  • Mètodes mutadors a les seq. indexades mudables mutable.IndexedSeq
def update(índex: Int, elem: A): Unit             // actualitza la posició indexada 
a les cues

per a les cues dels tipus immutable.Queue[+A] i mutable.Queue[A].

funcionals purs a les cues
  • Operadors i mètodes funcionals purs (sense efectes col·laterals) de les cues (immutables o mudables)
      retornen nova cua.
xs :+ x  // nova cua, encuant al final x +: xs // nova cua, encuant al capdavant (associatiu per la dreta xq. acaba en ':')  xs ++ (ys: GenTraversableOnce[A]) // nova cua encuant elements de col·lecció (ys: TraversableOnce[A]) ++: xs // nova cua prefixant elems. de col·lecció (op. associatiu per la dreta) 
  • Operadors i mètodes funcionals purs (sense efectes col·laterals) només de les cues immutables.
def enqueue[B >: A](elem: B): Queue[B]             // encua un element retornant nova cua def enqueue[B >: A](iter: Iterable[B]): Queue[B]   // encua els elements d'una col·lecció iterable.  def dequeue: (A, Queue[A])                         // descompon la cua NO BUIDA en parell (cap, resta) // funció parcial !! def dequeueOption: Option[ (A, Queue[A]) ]             // versió total 
mutadors a les cues
  • Operadors mutadors de les cues mudables (mutable.Queue[A])
xs += x  // encua al final x +=: xs // encua al capdavant (associatiu per la dreta)  xs.+= (x, y, ...)  // encua diversos (llista d'arguments variable)  xs ++= (ys: TraversableOnce[A])  // encua elems. de col·lecció travessable  def enqueue(elems: A*): Unit              // encua diversos (llista d'arguments variable) def dequeue: A                            // desencua el cap i el retorna.  // segons predicat. def dequeueFirst(predicat: (A)  Boolean): Option[A]     // desencua el primer que compleix el predicat 
cues amb prioritat

Cues amb prioritat definida per un ordre implícit o bé explícit. mutable.PriorityQueue[A]

Els elements s'encuen davant d'aquells que són menors en l'ordre.

Integral[T] especifica un anell íntegre que implementa la divisió euclidiana, habitualment enters de qualsevol precisió.

scala> import scala.collection.mutable.{PriorityQueue => PQ} import scala.collection.mutable.{PriorityQueue=>PQ}  // amb ordre implícit, prioritat al més gran scala> PQ(3,5,2).head res2: Int = 5  scala> val intOps = implicitly[Integral[Int]]   -- Integral[T]: ops. dels íntegres (anell euclidià) intOps: Integral[Int] = scala.math.Numeric$IntIsIntegral$@3a20f22d  // ordenació explícita per establir prioritat al més petit scala> PQ(3,5,2)(intOps.reverse).head res8: Int = 2 
  • Operadors mutadors de les mudables "cues amb prioritat" (mutable.PriorityQueue[A])
xs += x  // encua un elem.  xs.+= (x, y, ...)  // encua diversos (llista d'arguments variable)  xs ++= (ys: TraversableOnce[A])  // encua elems. de col·lecció travessable  def enqueue(elems: A*): Unit              // encua diversos (llista d'arguments variable) def dequeue: A                            // desencua el cap i el retorna. 

També es pot definir l'element com a parell (prioritat, valor) definint l'ordre per comparació del primer de la tupla. Vegeu exemple.

als conjunts

per a conjunts del tipus immutable.Set[A] i mutable.Set[A].

funcionals purs als conjunts
  • Operadors funcionals purs (sense efectes col·laterals) dels conjunts (Set) (immutables o mudables)
      retornen nou conjunt
xs + x  // actualitza (unió) amb {x} xs - x  // diferència amb {x}  xs.+ (x, y, ...)  // actualitza amb diversos elements (llista d'arguments variable) xs.- (x, y, ...)  // diferència amb diversos  xs ++ (ys: GenTraversableOnce[A]) // actualitza amb elements de col·lecció xs -- (ys: GenTraversableOnce[A]) // diferència conjunt col·lecció  (ys: TraversableOnce[A]) ++: xs // actualització, associatiu per la dreta  xs & ys; xs intersect ys // intersecció de conjunts xs &~ ys; xs diff ys    // diferència de conjunts xs | ys; xs union ys    // unió de conjunts 
mutadors als conjunts
  • Operadors mutadors dels conjunts mudables
xs += x  // inserir xs -= x  // eliminar  xs.+= (x, y, ...)  // inserir diversos (llista d'arguments variable) xs.-= (x, y, ...)  // eliminar diversos  xs ++= (ys: Traversable[A])  // inserir elems de col·lecció xs --= (ys: Traversable[A])  // elimina elems de col·lecció 
als diccionaris

per a diccionaris (taules associatives) amb tipus immutable.Map[A, +B] i mutable.Map[A, B].

      associació és una denominació de la correspondència (clau, valor)
funcionals purs als diccionaris
  • Operadors funcionals purs (sense efectes col·laterals) dels diccionaris (Map) (immutables o mudables)
      retornen nou diccionari
dicc + ((k, v))  // unió de dicc. amb parell (clau, valor) dicc - k  // diferència amb el parell de la clau k  dicc.+ ((k1, v1), (k2, v2), ...)  // unió amb diversos parells (llista d'arguments variable) dicc.- (k1, k2, ...)  // diferència amb els parells quines claus s'esmenten  dicc ++ (ys: GenTraversableOnce[(A, B)]) // unió amb parells de col·lecció dicc -- (ys: GenTraversableOnce[A]) // diferència amb claus de col·lecció  (ys: TraversableOnce[(A, B)]) ++: dicc  // unió amb parells de col·lecció, associatiu per la dreta  // updated: actualització funcional que retorna nou diccionari actualitzat // cas de col·lecció covariant a l'elem.(+B), requereix que el tipus de l'elem. a l'argument (posició contravariant)  // ha d'admetre tipus més generals: B1 tals que (B1 >: B). // altrament B1 = B dicc.updated[B1 >: B](key: A, value: B1): Map[A, B1]  // unió amb associació (k, v) 
mutadors als diccionaris
  • Operadors mutadors dels diccionaris mudables
dicc += ((k, v))  // afegeix associació de parell (clau, valor) dicc -= k  // elimina associació de clau k  dicc.+= ((k1, v1), (k2, v2), ...)  // incorpora diversos parells (llista d'arguments variable) dicc.-= (k1, k2, ...)  // elimina diverses claus  dicc ++= (ys: GenTraversableOnce[(A, B)]) // incorpora associacions de col·lecció de parells dicc --= (ys: GenTraversableOnce[A]) // elimina claus de la col·lecció  dicc.update(key: A, value: B): Unit 

Cadenes de text, Plantilles i Expressions regulars

String com a seqüència

El tipus String és augmentat implícitament a StringOps permetent aplicar-hi la interfície IndexedSeqLike (tractant String com una seq. d'accés aleatori). A més a més, incorpora els mètodes següents:

def lines: Iterator[String]    // parteix en línies def linesWithSeparators: Iterator[String]   // parteix ... incorporant separadors de línia, siguin {LF, CRLF, FF}  def r: Regex    // interpreta la cadena com a patró d'expressió regular, obtenint-ne un valor Regex def r (groupNames: String*): Regex  // ... donant noms als grups   scala> """(\d\d)-(\d\d)-(\d\d\d\d)""".r("mes", "dia", "any").findFirstMatchIn("12-31-1999").map(_.group("dia")) res0: Option[String] = Some(31)   def capitalize: String   // retorna String amb majúscula al primer caràcter  // to{tipus}: interpreta contingut com a literal del tipus, llançant excepció si no concorda en tota l'extensió. def {toBoolean, toByte, toShort, toInt, toLong, toFloat, toDouble}   // format: utilitza el contingut de la cadena com a format per mostrar args, igual que 'sprintf' de C def format(args: Any*): String   def padTo(len: Int, elem: A): String[A]   // farceix al final fins a llargada 'len' amb 'elem'   def stripPrefix (prefix: String): String   // escapça el prefix def stripSuffix (suffix: String): String   // escapça el sufix  // 'stripMargin' escapça marge esquerre (blancs i tabuladors) fins a un delimitador de marge, // en una String que pot ser multilínia (literals amb delimitador triple), // només d'aquelles línies de la cadena que continguin el delimitador!!  def stripMargin: String // escapça el marge esquerre delimitat per '|', incloent el delimitador def stripMargin (delimitadorDeMarge: Char): String   // igual per al delimitador especificat.  def stripLineEnd: String   // elimina {LF, CRLF, FF} del final  // diversos mètodes de java.lang.CharSequence def charAt(arg0: Int): Char  def length(): Int def subSequence(arg0: Int, arg1: Int): CharSequence   // diversos mètodes de java.lang.String def matches(arg0: String): Boolean  def regionMatches(arg0: Boolean, arg1: Int, arg2: String, arg3: Int, arg4: Int): Boolean   def replaceFirst(arg0: String, arg1: String): String  def replace(arg0: Char, arg1: Char): String  def replace(arg0: CharSequence, arg1: CharSequence): String  def replaceAll(arg0: String, arg1: String): String  def substring (arg0: Int): String  def substring (arg0: Int, arg1: Int): String  def trim(): String  def toCharArray(): Array[Char] def to{Lower|Upper}Case (): String             // Minúscules/Maj. def to{Lower|Upper}Case (arg: java.util.Locale): String   // Min./Maj. segons 'Locale' ... 

String especial per a multi-línia, i expressions regulars

També anomenat "document tot seguit" (ang:heredoc), amb cometes triples. La barra '\' invertida no s'hi interpreta com a caràcter d'escapament, permetent escriure-hi expressions regulars que no les han de doblar.

""" Primera línia continuació"""  """ Primera línia, "entrecomillat"     |continuació amb marge""".stripMargin       // stripMargin elimina el marge esquerre fins al delimitador '|'  """ Si no volem el delimitador de marge '|'     #continuació amb marge i delimitador especial""".stripMargin('#')   // ... per al delimitador de marge especificat  // per al cas d'expressions regulars """(\d\d\d\d)-(\d\d)-(\d\d)"""  // Aquí la barra invertida NO s'interpreta com a caràcter d'escapament  "tira normal\n" // Aquí la barra invertida sí que s'interpreta com a caràcter d'escapament 

Plantilles

Plantilles amb interpolació, identificades per un prefix de plantilla.

// La plantilla "s" com a: s"Hola, $nom !!" val nom = "Jaumet" println(s"Hola $nom !!")  // Hola Jaumet !!  // si l'expressió a interpolar és més complexa, cal delimitar-la entre claus scala> val benvinguda = "benvingut !!" scala> s"Hola $nom. ${benvinguda.capitalize}" res1: String = Hola Jaumet. Benvingut !!  // La plantilla "f" permet afegir-hi especificadors de format com els del "printf" del llenguatge C scala> val edat = 10 scala> f"En $nom%15s té $edat%3d anys !!" res2: String = En          Jaumet   10 anys !! 

Expressions regulars

  • El tipus Regex.

A l'avaluador d'expressions:

scala> val regexDataISO = """(\d\d\d\d)-(\d\d)-(\d\d)""".r     // expr. de tipus Regex regexData: scala.util.matching.Regex = (\d\d\d\d)-(\d\d)-(\d\d)  scala> "2004-01-20" match {case regexDataISO (any, mes, dia) => s"allò va passar el $dia/$mes/$any."; case _ => "no s'ha trobat cap data!!"} res4: String = allò va passar el 20/01/2004.  // per al cas de voler caçar múltiples ocurrències  scala> (for (m <- regexDataISO findAllMatchIn "2004-03-01, 2005-12-02") yield { m.subgroups match {case List(any, mes, dia) => s"$dia/$mes/$any"}}).toList res5: List[String] = List(01/03/2004, 02/12/2005) 

Biblioteques rellevants

    scala-arm
    control d'excepcions en la gestió de recursos amb alliberament automàtic en acabar. Evita, en cas d'adquisició de múltiples recursos, la implantació de construccions try-finally una dins l'altra.
import resource._  // dos recursos, còpia de input a output, amb alliberament automàtic  for(input <- managed(new java.io.FileInputStream("entrada.txt");       output <- managed(new java.io.FileOutputStream("sortida.txt")) {    val buffer = new Array[Byte](512)   def read(): Unit = input.read(buffer) match {     case -1 => ()     case  n => output.write(buffer,0,n); read()   }   read() } 
    slick
    aporta col·leccions específiques per a l'accés a BDD relacionals generant SQL a partir de mètodes funcionals.
val explicitInnerJoin = for {   (c, s) <- coffees join suppliers on (_.supID === _.id)    // FROM coffees AS c INNER JOIN suppliers AS s ON c.supid = s.id  if c.name like "%expresso%"  // WHERE c.name LIKE "%expresso%"   sortBy (s.price.desc)     // ORDER BY s.price DESC   drop (20)                    // OFFSET n   take (10)                    // LIMIT m } yield (c.name, s.name)       // SELECT c.name, s.name 

Entorns de desenvolupament

  • La manera més vistosa és descarregar l'entorn web de desenvolupament Activator de lightbend.com (abans TypeSafe.com).
  • Per treballar a consola de comandes, cal descarregar el gestor de projectes SBT que per cada projecte descarregarà si no la té, la versió de Scala especificada pel fitxer de projecte (build.sbt). És convenient seguir l'estructura de directoris especificada per l'SBT.
  • Una altra manera de treballar amb Scala és descarregant d'aquí l'endollable per a Eclipse (o altres entorns de desenvolupament Java) que ja incorpora el compilador i les biblioteques de les quals, clicant, se'n pot examinar el codi font.

Scala a Eclipse

A Eclipse un cop incorporat l'endollable al menú Help->Install new software podrem crear un "Projecte Scala" que incorporarà automàticament les biblioteques de Scala al projecte si ho fem amb el menú de context de la barra lateral esquerre a l'opció "New -> Project -> Scala Wizards -> Scala Project" del menú de context de la barra lateral de projectes.

Scala segueix la mateixa estructura de mòduls (package) que el Java. Tanmateix podem posar diversos objects, classes i traits al mateix fitxer de codi font.

És habitual crear primer sobre el directori src, abreviació de source (codi font), un Package amb nom on crear els fonts, si volem facilitar l'estructuració modular o la inter-operabilitat amb paquets d'altres projectes.

Després podrem triar, al menú de context del package, els assistents (ang:wizards) de generació d'esquelets Scala Application, Scala Object, Scala Class o Scala Trait

Execució: per cada projecte cal crear una configuració d'engegada que podrem desar, especificant projecte, nom de l'objecte que conté el mètode main i possibles paràmetres de l'aplicació. Clicant el menú de context del projecte, opcions "RunAs -> Run configurations -> especificar la configuració o seleccionar-la -> botó Run"

Exemples

Lector amb analitzador gramatical

Basat en la classe JavaTokenParsers per aprofitar la decodificació de literals String.

La classe interna Parser adequa els operadors ((~>) seqüència, (|) alternativa, postfix (*) zero o més repeticions, postfix (+) una o més, postfix (?) zero o una) als combinadors de gramàtica regular específics de la classe, i també els que permeten l'extracció (^^) o bé l'especificació de resultat (^^^).

package test              // fitxer: test/LaMevaApp.scala  /* Contingut del fitxer de dades amb el llenguatge de simulació de teclat que pretenem processar Str "abcd" Enter Str "linia2" Move Up Move Dn Move Left Back Move Right Del * /  object LaMevaApp extends App {  import scala.util.parsing.combinator.JavaTokenParsers  object MoveDirEnum extends Enumeration {   type TMoveDir = Value   val MoveDir_Up, MoveDir_Dn, MoveDir_Left, MoveDir_Right = Value }  import MoveDirEnum._  // Simulació de pulsacions de teclat // "sealed" permet comprovar exhaustivitat en l'encaix de patrons  abstract sealed class Cmd();  case class CmdStr(val str: String) extends Cmd() {   override def toString = "CmdStr " ++ str } case class CmdEnter() extends Cmd() {   override def toString = "CmdEnter" } case class CmdBack() extends Cmd() {   override def toString = "CmdBack" } case class CmdDel() extends Cmd() {   override def toString = "CmdDel" } case class CmdMove(val dir:TMoveDir) extends Cmd() {   override def toString = "CmdMove " ++ dir.toString }  def escapçaDelimitadors(s: String): String = {      assume (s.length >= 2, "cas no previst"); // assumpció       s.slice(1, s.length -1) // substring del segon al penúltim      }  import scala.language.postfixOps  class MyParser extends JavaTokenParsers {    // produccions   def unOMesEspais = accept(' ')+   def zeroOMesEspais = accept(' ')*   def nl = zeroOMesEspais ~> accept('\n')   def strParam = stringLiteral ^^ { s => CmdStr(escapçaDelimitadors(s)) } // : Parser[Cmd]   def str = "Str" ~> unOMesEspais ~> (strParam | err("Str: manca param"))   def up = "Up" ^^^ MoveDir_Up  // : Parser[TMoveDir]   def dn = "Dn" ^^^ MoveDir_Dn   def right = "Right" ^^^ MoveDir_Right   def left = "Left" ^^^ MoveDir_Left   def moveDir = (up | dn | left) ^^ { dir => CmdMove(dir) }   def move = "Move" ~> unOMesEspais ~> (moveDir | err("Move: manca param"))   def enter = "Enter" ^^^ CmdEnter()   def back = "Back" ^^^ CmdBack()   def del = "Del" ^^^ CmdDel()   def statement : Parser[Cmd] = str | move | enter | back | del   def statements : Parser[List[Cmd]] = rep1sep(statement, nl)   }  object MyParser {   import java.io.File  import scala.io.Source    def processaFitxer(parser:MyParser, nomFitxer: String): List[Cmd] =  {         val file = new File(nomFitxer)        if (file.exists()) {                 val contingut = Source.fromFile(file, "UTF-8").getLines().flatMap(_ ++: "\n").mkString                 parser.parseAll(parser.statements, contingut) match {                         case parser.Success(resultat,_) => resultat                         case x => { println(x); Nil }                 }         }         else { printf("el fitxer %s no existeix\n", nomFitxer); Nil              }         }   }  val parser = new MyParser()  args.toList match {      case nomFitxer :: Nil => // List(nomFitxer) també detecta llistes d'un sol element                             { val cmds = MyParser.processaFitxer(parser, nomFitxer)                               cmds match {                                 case Nil => {println("no s'han trobat comandes"); sys.exit(1)}                                 case _ => { println("les comandes són: ")                                             cmds.foreach(cmd => println(cmd))                                           }                                 }                             }       case _ => println("ús: nomprog nomFitxer")      }  } 
# compilació a l'Eclipse: clicar al menú de context RunAs -> Run configurations  #  -> doble-clic a Scala application -> New Configuration  #  -> Ajustar el nom de l'objecte d'engegada (el que deriva de App o conté el mètode main)  #  -> prémer botó Run  # compilació en consola de comandes mkdir classes scalac -d classes src/test/LaMevaApp.scala # execució scala -cp classes test.LaMevaApp 

Cues amb prioritat sobre parells (prioritat, valor)

Cal especificar explícitament l'ordenació sobre el primer del parell.

import scala.math.Numeric._ import scala.collection.mutable.{PriorityQueue => PQ}  object Main extends App {    val intOps = implicitly[ Integral[Int]]    val cuaDeParellsAmbPrioritatAlPrimerDelParell_DeMajorAMenor =                      PQ((3,"b"),(1,"c"),(5,"a")) (intOps.on(_._1))  // Ordering explícit sobre el primer del parell    val cuaDeParellsAmbPrioritatAlPrimerDelParell_DeMenorAMajor =                      PQ((3,"b"),(1,"c"),(5,"a")) (intOps.on({ case (k, v) => -k }))    for (pq <- List(cuaDeParellsAmbPrioritatAlPrimerDelParell_DeMajorAMenor,                    cuaDeParellsAmbPrioritatAlPrimerDelParell_DeMenorAMajor)) {println("El cap és: " + pq.head)} } 

Referències

Enllaços externs

Tags:

Llenguatge De Programació Scala Scala és orientat a objectesLlenguatge De Programació Scala Scala és funcionalLlenguatge De Programació Scala Scala té tipus estàticsLlenguatge De Programació Scala Scala és extensibleLlenguatge De Programació Scala Scala interopera amb Java i .NETLlenguatge De Programació Scala Exemple: Hola mónLlenguatge De Programació Scala CaracterístiquesLlenguatge De Programació Scala Biblioteques rellevantsLlenguatge De Programació Scala Entorns de desenvolupamentLlenguatge De Programació Scala ExemplesLlenguatge De Programació Scala ReferènciesLlenguatge De Programació Scala Enllaços externsLlenguatge De Programació ScalaLlenguatge de programacióOrientació a objectes

🔥 Trending searches on Wiki Català:

SakhàVidaThe DoorsPoor thingsPredadorSportsklubben BrannSalma Paralluelo AyingonoL'Imperi contraataca2004Arthur WellesleyDemocràticaJudit FarrésEUAMelina MatthewsLev LandauTiberiPilota d'orDune (pel·lícula de 2021)Sílvia Orriols SerraSacre Imperi Romanogermànic1929Mètode científicVarsòviaLleóElefantsMontserratCatalunyaCillian MurphyCopa del Món de Futbol de 2022MadridPelatgeJoventut BadalonaProteïna19 de desembreMary Alexandra EarpsLluçanèsMúscul deltoideAntimoni2016Bisbe (Església Catòlica)VDVJueusHomo sapiensPaís ValenciàFerrocarrils de la Generalitat de CatalunyaLlanterna màgicaHinduismeEleccions al Parlament de Catalunya de 2021SenglarsTractat de París (1814)JapóTasmàniaRachel PortmanCosta RicaFrancisco Román Alarcón SuárezProcariotaMiravetManuel Jabois SueiroNarbonaSK Brann KvinnerBostonCarcassonaAra RepúbliquesTolosa de LlenguadocEleccions presidencials dels Estats Units de 2016ErminiRavenissa comunaCopa del Món de Futbol de 2014Jesús de NatzaretMaternitat d'ElnaAmazicsTour de França 2004American Broadcasting CompanyVolta a Espanya de 2023Steffi GrafAdam Driver🡆 More