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. Scala és orientat a objectesScala é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 funcionalScala 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àticsScala 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:
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 extensibleEl 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:
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 .NETScala 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ónPer 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[3] (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.[4] // fitxer src/test/hola-mon.scala
package test
object Principal extends App {
Console.println ("Hola món!, els arguments són:" ++ args.mkString (", "))
}
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 SBTAquesta manera de treballar directament amb el compilador ha quedat una mica antiquada. Si ho fem a través del gestor de projectes SBT,[6] aquest ens descarregarà automàticament la versió del compilador i de les dependències del projecte, esmentades al fitxer de projecte.[7] Caldrà però seguir l'esquema de directoris especificat per l'SBT.[8] Al Linux el trobarem al gestor de paquets o bé el podem descarregar d'origen[6] 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>
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
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ístiquesDegut 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.[9] tipus especialsVegeu Tipus unificats (amb gràfic) a la ref.[10]
Resultat buit en efectes col·laterals (actualitzacions, impressions). Equival al tipus de retorn buit (void) de Java i C
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!
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 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()
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. referencialEn la comparació d'objectes, l'operador de Scala (==) no correspon en Java al (==) sinó al mètode equals. [14]
A Scala la igualtat d'objectes que es passen per referència AnyRef[18] equival a x == y <=> if (x eq null) (y eq null) else x.equals(y)
polimorfismespolimorfisme per subtipatgetrait 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ètricLes 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-hocAfegeix requeriments de context al paràmetre de tipus. A l'exemple amb 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)
}
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 tipustype LaMevaFun[A,B,C] = B => A => Pair[B, C]
Relacions de Subtipus
En el següent exemple Tipus ha d'ésser subtipus de TCotaSuperior.[22] 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.[23] type Tipus >: TCotaInferior // 'Tipus' ha d'ésser supertipus (més general) de 'TCotaInferior'
Subtipatge de funcions i de classes, Covariança, Contravarianç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.
Sempre que 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.
Sempre que 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).[24][25] 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.[25] 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 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 producteTuples (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 singularsA 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''
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
}
class Classe(val p1: Tipus, p2: Tipus2 = vPerDefecte, ...) // amb val, p1 passa a ser membre de la classe
ExtractorsUn 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.[26] // 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 encaixablesLes 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
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
Llistesobject 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)
}
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àdicLes 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".[27] Variables i mètodesEl 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ó. OperadorsEls 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ícitaL'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 singularsLa 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.[28] 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últipleL'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.[29] É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.[30] 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 ...
Enumeracionsobject 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 superiorAdmetre 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 variableEl 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 L'aplicació parcial d'operadors s'expressa substituint un o més operands pel comodí '_' en una expressió. Ex.
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 funcionsLa interfície Function1 de funcions d'un argument aporta mètodes per la composició de funcions.[31] La interfície Function2 de funcions de dos arguments aporta mètodes per obtenir versions currificades (.curried) i la inversa (.tupled).[32] 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
}
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 PartialFunctionVegeu "Pattern-matching anonymous functions".[33] 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 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
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
ExcepcionsExcepcions en la Gestió de recursosLa clàusula try {..} catch (err:Throwable) {..} finally {..} possiblita la gestió d'excepcions i alliberament de recursos. L'objecte Exception[35] aporta diverses possibilitats addicionals. gestió automàticaLa recent construcció a Java 7 try-with-resources[36] 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),[37] es proposa la següent construcció (Closeable[38] é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[39] 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 TryEl 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 ContractePrecondicions, postcondicions i invariants AssercionsVegeu Assercions[40] i Opció de compilació -enableassertions[5]
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.[42] Es recomana fer servir la definició per patrons per especificar les precondicions. Postcondició
// 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 codiAdmet notació de llenguatge de marques XML com a literal, amb interpolació d'expressions entre claus "{}". Vegeu[43] object Main extends App {
val idioma = "ca" ;
val títol = "El meu títol" ;
val xmlTítol = <title> {títol} </title>; // entre claus '{' <expr> '}' 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 ="<?xml version='1.0' encoding='UTF-8' ?>\n" ;
val doctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " +
" \"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ícitsScala té diversos mecanismes relacionats amb el tipus que busquen automàticament entre les instàncies visibles en l'àmbit,
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.
trait Set[A <% Ordered[A]]... // consulteu la ref.
Instàncies implícites de classes de tipusEl 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 implicitlyIntrodueix el requeriment d'instanciació de classes de tipus (genèrics) en el paràmetre de tipus. La restricció de context 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 OrderingOrdered[A] i Ordering[A] ofereixen una funcionalitat similar
def nomFunció [A:Ordered](params...) = ...
def nomFunció [A](params...)(implicit m:Ordering[A]) = ...
Injecció de mètodesLa 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.[45] 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)
}
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
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)
lazy val patró = expressió
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
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èricsLa 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 ScalaScala 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í.[52] L'especificació oficial del llenguatge és aquí.[53] Els elements predefinits són a l'object Predef[54] Cercadors de l'APILa API de Scala www.scala-lang.org/api/current/ permet cercar per nom de paquet, o signatures d'àmbit global.
BibliotequesAlguns 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'ActorsVegeu ref.[59] STM (Transaccions de memòria per programari)Col·leccionsVegeu ref.[62] Gràfic de les col·leccions immutables. Gràfic de les col·leccions mudables.
(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
operacions comunes dels mòduls estàtics (object) que acompanyen les col·leccionsVegeu "Creating collections".[67] // 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·leccionsdef 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)
// 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
Interfície TraversableOnceVegeu ref.[70]
// 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)
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 TraversableInterfície TraversableLike[73] 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
// 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))
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
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'
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
// 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 CanBuildFromEl tret É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 IterableIterableLike, caracteritzada per definir un iterador que faciliti visitar els elements, i que conté la major part dels mètodes d'Iterable[75] afegeix a TraversableLike entre d'altres els següents:
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
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]]
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
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
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: 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üènciesper a les seqüències dels tipus funcionals purs a les seqüències
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
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 BufferBuffer: estructura mudable de magatzematge temporal incrementable que permet eliminacions. Per al tipus
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 indexadesper a seqüències d'accés aleatori dels tipus
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
def update(índex: Int, elem: A): Unit // actualitza la posició indexada
a les cuesper a les cues dels tipus funcionals purs a les cues
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)
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
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 prioritatCues amb prioritat definida per un ordre implícit o bé explícit. Els elements s'encuen davant d'aquells que són menors en l'ordre. Integral[T][77] 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
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 conjuntsper a conjunts del tipus funcionals purs als conjunts
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
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 diccionarisper a diccionaris (taules associatives) amb tipus
funcionals purs als diccionaris
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
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 regularsString com a seqüènciaEl tipus String és augmentat implícitament a StringOps[80] permetent aplicar-hi la interfície IndexedSeqLike[81] (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 regularsTambé 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
PlantillesPlantilles amb interpolació, identificades per un prefix de plantilla.[82] // 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 té 10 anys !!
Expressions regulars
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
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()
}
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
Scala a EclipseA Eclipse[88] 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" ExemplesLector amb analitzador gramaticalBasat en la classe La classe interna 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
|