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 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.
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.scalapackagetestobjectPrincipalextendsApplication{// 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.scalapackagetestobjectPrincipalextendsApp{Console.println("Hola món!, els arguments són:"++args.mkString(", "))}
mkdirclasses# subdirectori per al codi objecte# compilació (-cp és classpath, -d és directori per a la sortida)
scalac-cpclasses-dclassessrc/test/hola-mon.scala
# execució
scala-cpclassestest.Principal# execució pel nom de l'objecte que conté el mètode ''main''
Holamó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,[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")
Holamón
scala>
Amb el gestor de projectes SBT podem engegar l'avaluador REPL amb la comanda següent:
// fitxer de projecte build.sbtlazyvalroot=(projectinfile(".")).settings(name:="hola_món",version:="1.0",scalaVersion:="2.11.7")
sbtconsoleQuick# engega REPL (Read-Eval-Print Loop) sense carregar les classes del directori de treball i subdirectoris
sbtconsole# 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)[3]
scala>:reset// reinicialitza l\'avaluadorscala>objectMainextendsApplication{// 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\'avaluadorscala>objectMainextendsApp{// 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).
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)[11]
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:[12]
É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.[12]
Symbol
literals: identificadors precedits per l'apòstrof.[13]
scala>valsaluda='holasaluda:Symbol='holascala>saludamatch{// 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íniaBenvingut!
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)
// llista homogèniascala>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 especificarscala>List()res14:List[Nothing]=List()// Nil especialitza la llista buidascala>Nilres16:scala.collection.immutable.Nil.type=List()scala>Nil:List[Int]res17:List[Int]=List()
Per encaix primer valors, després variables amb restricció de tipus (de més a menys específic)
packagetest;objectMainextendsApp{valllista:List[Any]=List(true,'c',"abc",8:Byte,270:Short,300/* Int */,200000000000L/* Long */,2.5F/* Float */,3.12345678/* Double */);for(elem<-llista){elemmatch{case2|3=>print("dos o tres");casew:AnyVal=>wmatch{// tipus primitius que es passen per valorcasev:Char=>printf("%c u\\%04x",v,v.toInt);// caràcter i hexadecimalcasev:Byte=>printf("és un byte: %d",v);casev:Short=>printf("és un short: %d",v);casev:Int=>printf("és un int: %d 0%o x%08x",v,v,v);// decimal, octal i hexad.casev:Long=>printf("%d",v);casev:Float=>printf("%.2f",v);casev:Double=>printf("%.8f",v);casev=>print(v)}casew:AnyRef=>wmatch{// tipus que es passen per referènciacasev:String=>printf("%s",v);casev=>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.
[14]
objectMainextendsApp{defimprimeix[T](dada:T)=dadamatch{casev:String=>println(v);casev=>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.
importscala.math.Numeric._functionproducte[A:Numeric](x:A,y:A):A={valinstància=implicitly[Numeric[A]]// instància de Numeric per al tipus del paràmetre emprat a la cridainstància.times(x,y)}
scala.math.Numeric és la signatura d'un anell unitari ordenat (Numeric especialitza (extends) Ordering).[19]
scala.math.Integral és la signatura d'un anell unitari euclidiàordenatíntegre (Integral especialitza (extends) Numeric).[20]
scala.math.Fractional és la signatura d'un cosordenat (Fractional especialitza (extends) Numeric).[21]
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
abstractclassSemiGrup[A]{defassoc(x:A,y:A):A}abstractclassMonoid[A]extendsSemiGrup[A]{defneutre:A}objectMonoides{// Monoides per a la suma i el producteimplicitobjectIntMonoidSumaextendsMonoid[Int]{defassoc(x:Int,y:Int):Int=x+ydefneutre:Int=0}implicitobjectIntMonoidProducteextendsMonoid[Int]{defassoc(x:Int,y:Int):Int=x*ydefneutre:Int=1}}objectTestextendsApp{importMonoides._// per fer servir les instàncies implícites, han d'estar visiblesimportscala.collection.IterabledefplegaLlista[A](xs:Iterable[A])(implicitm: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
typeLaMevaFun[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.[22]
typeTipus<:TCotaSuperior// 'Tipus' ha d'ésser subtipus (més especialitzat) de 'TCotaSuperior'
en el següent exemple Tipus ha d'ésser superclasse de TCotaInferior.[23]
typeTipus>:TCotaInferior// 'Tipus' ha d'ésser supertipus (més general) de 'TCotaInferior'
Subtipatge de funcions i de classes, Covariança, Contravariança
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 llenguatgetypeFunction[-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).[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 U >: T.
classListNode[+T](h:T,t:ListNode[T]){defhead:T=hdeftail: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 >: Tdefprepend[U>:T](elem:U):ListNode[U]=ListNode(elem,this)}
Tipus producte
Tuples (tipus producte anònim)
Definides les classes Tuple2, Tuple3, .., Tuple22.
objectMainextendsApp{valparell=newTuple2(10,"abc");// new amb el nom de la classevalTuple2(primer,segon)=parell;// encaix de la tuplaprintln(parell._1);// primerprintln(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''objectProvaFiltratgeextendsApp{deffiltrar(xs:List[Int],llindar:Int)={defprocessa(ys:List[Int]):List[Int]=// funció a l'interior d'una altra funcióif(ys.isEmpty)yselseif(ys.head>=llindar)ys.head::processa(ys.tail)elseprocessa(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.
importjava.lang.{Color}valvermell=newColor(0xFF,0,0);// constructor de la ''classe''valcomponentRoigDelBlanc=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
objectList{defapply[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)defempty[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.
classClasse(valp1: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.[26]
// el següent codi genera un objecte extractorobjectDoble{// generadordefapply(x:Int):Int=x*2// inversa per als encaixos de patrons, retorna en un Option l'èxit o fracàs de l'encaixdefunapply(z:Int):Option[Int]=if(z%2==0)Some(z/2)elseNone}objectProvaDobleextendsApp{valx=Doble(21)// aplica 'apply' (nom de funció per omissió a les expressions)xmatch{caseDoble(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.
abstractclassArbre[T]// ''val'' al paràmetre el declara membre de la classe, estalvia redeclaracionscaseclassFulla[T](valdada:T)extendsArbre[T]caseclassBranca[T](valdada:T,valesquerra:Arbre[T],valdreta:Arbre[T])extendsArbre[T]objectMainextendsApp{defimprimeixArbre[T](arbre:Arbre[T]):Unit=arbrematch{caseFulla(d)=>println("fulla: "++d.toString)caseBranca(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ó.valarbre1=Fulla[Int](1);// si s'omet el paràmetre de tipus ''[T]'', es pren el tipus del paràmetre actual.valarbre2=Branca(2,arbre1,arbre1);imprimeixArbre(arbre2);}
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:
sealedabstractclassArbre[T]// 'sealed': classe segellada, per al control d'exhaustivitat als encaixoscaseclassFulla[T](valdada:T)extendsArbre[T]caseclassBranca[T](valdada:T,valesquerra:Arbre[T],valdreta:Arbre[T])extendsArbre[T]objectMainextendsApp{defimprimeixArbre[T](arbre:Arbre[T]):Unit=arbrematch{// case Fulla(d) => println ("fulla: " ++ d.toString) // comentat expressament per veure l'avís del compiladorcaseBranca(d,esq,dreta)=>{imprimeixArbre(esq);println("branca: "++d.toString);imprimeixArbre(dreta);}...
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 íntegreordenateuclidià: 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.
objectMainextendsApp{valllista=(1to3).toList;// List(1,2,3)defdescriuLlista[T](llista:List[T])=llistamatch{caseNil=>println("llista buida");casell@(_::_)=>// as-pattern (variable @ patró): unifica variable i terme encaixatprintln("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 generadorvaliterador=for(i<-Iterator.range(des_de,topall_exclòs)ifi%2==0)yield2*i// el tipus resultant és Iterator[Int]println(iterador.toList)// equivalent amb rangs: (des_de to topall_inclòs)valiterador=(1to12).iterator.filter(_%2==0).map(_*2)println(iterador.toList)
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 primitivesflatMap, 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.
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).
Vegeu presentació "Introduction to Monads in Scala".[27]
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à.
validentificador:Tipus=expressió// amb val: assigna el valor de l'expressió com a constantvaridentificador:Tipus=expressió// amb var: variable mudabledefidentificador: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>varv:Boolean=_v:Boolean=falsescala>varv:Int=_v:Int=0scala>varv: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.
classQ{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 StringvalstrBldr="abc"++:"def"++:newStringBuilder("ghi")valnovaStr=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.[28]
L'herència múltiple es basa en la incorporació de trets.
importjava.awt.{Color};classPunt(xInicial:Int){varx:Double=xInicial.toDouble;defdesplaça(dx:Double):Unit={x=x+dx}defimprimeixPosició:Unit={print("posició: ");println(x)}}// els trets (ang: trait) són com una classe abstractatraitAcoloriment{varcolor:Color;defpinta(nouColor:Color):Unit={color=nouColor}defimprimeixColor:Unit={print("color: ");println(color)}}// incorpora el tret Acoloriment donant valor als membres abstractesclassPuntAcolorit(xInicial:Int,colorInicial:Color)extendsPunt(xInicial)withAcoloriment{varcolor=colorInicial;defdesplaçaIPinta(dx:Double,nouColor:Color)={super[Punt].desplaça(dx);super[Acoloriment].pinta(nouColor);}}objectMainextendsApp{valpunt=newPuntAcolorit(0,Color.WHITE){// extensió en instanciar// només aquesta instància incorpora l'extensiódefdesplaç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;}
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]
abstractclassGraf{typeNode<:NodeBase;// volem que els nodes implementin NodeBaseclassArc(valorigen:Node,valdestí:Node){}classNodeBase{defconnectaAmb(n:Node):Arc=newArc(this,n);// il·legal !! 'this' no és de tipus Node}}
Solució amb self-types
abstractclassGraf{typeNode<:NodeBase;// volem que els nodes implementin NodeBaseclassArc(valorigen:Node,valdestí:Node){}abstractclassNodeBase{self:Node=>// requeriment que el tipus propi sigui subtipus de Node en obtenir-ne una classe concretadefconnectaAmb(n:Node):Arc=newArc(self,n);// ara si que passa la compilació}}// classe concreta de Graf que incorpora la classe concreta NodeclassGrafEtiquetatextendsGraf{classNode(etiq:String)extendsNodeBase{defgetEtiqueta: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.
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.
(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.
objectMainextendsApp{valllista=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) => Intvalresult=llista.foldLeft(0){(x,y)=>x+y};Console.print("resultat: ");Console.println(result);}
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>:typeList[Any](1,2.5,"abc").map(_.toString)List[String]scala>deffloatMes3:(Float=>Float)=(_+3)// cal la restricció de tipusfloatMes3: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 tardscala>defflip[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)Rscala>defcons[A](x:A)(xs:List[A])=x::xscons:[A](x:A)(xs:List[A])List[A]scala>:typecons[Int](5)_// tipus aplicant només un paràmetreList[Int]=>List[Int]scala>:typecons[Int]_// tipus aplicant zero paràmetresInt=>(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)
La 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.
packagetestobjectMainextendsApp{// composició de les funcions d'un arg.defdoble(x:Int)=x*2defsuma3(x:Int)=x+3// f compose g == f. g// f andThen g == g. f// cal especificar '_' per obtenir l'aplicació parcialdefdoblaiSuma3=(doble_).andThen(suma3_)// composició d'esquerra a dretadefdoblaiSuma3=(suma3_).compose(doble_)// composició amb estil matemàtic, resultat idèntic a l'anteriorvalprova=doblaiSuma3(5)// currificació i aplicabilitat als arg. en tupladefcons[A](x:A,xs:List[A])=x::xsdeff[T]=(cons[T]_)// cal especificar '_' per obtenir l'aplicació parcialvalx1=f(1,List(2,3))// currificació (amb 'curried'' la funció s'aplica als param. separatsvalx2=f[Int].curried(1)(List(2,3))// cal explicitar el paràmetre actual de tipus }
// en aquest cas, funció que pren una funció d'argument i en retorna una altradefsum(f:Int=>Int):(Int,Int)=>Int={defsumF(a:Int,b:Int):Int=// suma f(x), x <- [a..b]if(a>b)0elsef(a)+sumF(a+1,b)&sumF}defsumaDobles=&sum(x=>2*x)defsumaQuadrats=&sum(x=>x*x)
Definició per patrons d'una funció - El tret PartialFunction
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 GenTraversabledefcollect[B](pf:PartialFunction[A,B]):Repr[B]// interfície GenTraversableOncedefcollectFirst[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[35] aporta diverses possibilitats addicionals.
gestió automàtica
La 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)
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.
importresource._// dos recursos, còpia de input a output, amb alliberament automàticfor(input<-managed(newjava.io.FileInputStream("test.txt");output<-managed(newjava.io.FileOutputStream("test2.txt")){valbuffer=newArray[Byte](512)defread():Unit=input.read(buffer)match{case-1=>()casen=>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.
Vegeu Assercions[40] i Opció de compilació -enableassertions[5]
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
asserció com a funció sobre el resultat d'un bloc de codi.
// malament a posta, per fer petar la postcondicióscala>defincrementa(x:Int)={x-1}ensuring(_>x,"el resultat no és superior")incr:(x:Int)Intscala>incrementa(5)java.lang.AssertionError:assertionfailed:elresultatnoéssuperioratscala.Predef$Ensuring$.ensuring$extension3(Predef.scala:256)at.incr(<console>:10)...43elided
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.
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.
Admet notació de llenguatge de marquesXML com a literal, amb interpolació d'expressions entre claus "{}". Vegeu[43]
objectMainextendsApp{validioma="ca";valtítol="El meu títol";valxmlTítol=<title>{títol}</title>;// entre claus '{' <expr> '}' interpolació d'expressions.valxmlCapçalera=<head>{xmlTítol}</head>;valxmlCos=<body>Aquestapàginaduupertítol:{títol}</body>;// substitució a l'atribut, hi afegeix les cometes.valxmlPàgina=<htmllang={idioma}>{xmlCapçalera}{xmlCos}</html>;// prefixvalcapsalXml="<?xml version='1.0' encoding='UTF-8' ?>\n";valdoctype="<!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 XMLscala.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 Intimplicitdefchar2int(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[44]
traitSet[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]).
abstractclassSemiGrup[A]{defsuma(x:A,y:A):A}abstractclassMonoid[A]extendsSemiGrup[A]{defzero:A}objectImplicitTestextendsApp{// mòdul instància de Monoid per al tipus StringimplicitobjectStringMonoidextendsMonoid[String]{defsuma(x:String,y:String):String=xconcatydefzero:String=""}// mòdul instància de Monoid per al tipus IntimplicitobjectIntMonoidextendsMonoid[Int]{defsuma(x:Int,y:Int):Int=x+ydefzero: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.defsumaLlista[A](xs:List[A])(implicitm:Monoid[A]):A=xsmatch{caseNil=>m.zerocasey::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.
defsumaLlista[A:Monoid](xs:List[A]):A={valm=implicitly[Monoid[A]]// obté una instància de Monoid per al tipus A xsmatch{caseNil=>m.zerocasey::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
defnomFunció[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.
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.[45]
objectTestextendsApp{objectAugmentador{classToMyInt(s:String){deftoMyInt:Int=java.lang.Integer.parseInt(s)}implicitdefstring2ToMyInt(s:String)=newToMyInt(s)}importAugmentador._// incorpora l'implícit a l'àmbitprintln("12".toMyInt)}
Augment ràpid (en escriure'l però més lent d'execució).[46]
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.[47]
objectDemo{valvalor=...defllegir()=...// efectes col·laterals, cal posar els parèntesisdefobtenirValor=...// no hi ha efectes col·laterals => no s'hi han de posar els parèntesis}
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)
lazyvalpatró=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.
objectMainextendsApp{varcnt=0defperNom(n:=>Int)={println(cnt);println(n);// aquí se substitueix el nom del param. per l'expressió del param. actualprintln(n);// aquí també}perNom({cnt+=1;cnt})}
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.[49]
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.[50]
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).[51]
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 íntegreordenateuclidià: ops. dels enters).
(0to2).toList// interval tancat [0,2]: List(0,1,2)(0until2).toList// interval obert per la dreta [0,2): List(0,1) (2to0by-1).toList// interval amb increment: List(2,1,0)
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í.[52]
L'especificació oficial del llenguatge és aquí.[53]
Els elements predefinits són a l'object Predef[54]
Hi ha un cercador d'índex de biblioteques a la mateixa web anomenat "Scaladex": index.scala-lang.org
La web Scalex.orgArxivat 2020-11-11 a Wayback Machine. permetia, fins fa poc, cercar els identificadors exportats per un paquet, de manera similar a Hayoo[55] o bé Hoogle[56] per al llenguatge Haskell. Tanmateix se'n pot descarregar el codi del Github.[57]
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".[63] El mètode par ofereix implementacions amb paral·lelisme de la majoria de col·leccions que implementen el tret ParIterable[64] amb l'ajut d'un particionador (Splitter) que ofereix un iterador sobre les subcol·leccions disjuntes resultants de la partició.[65]
(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).[66]
operacions comunes dels mòduls estàtics (object) que acompanyen les col·leccions
// Generador de col·lecció buidadefempty[A]:Col·lecció[A]// Generador partint d'elements oferts en una crida de nombre variable de paràmetresdefapply[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
defisEmpty:BooleandefnonEmpty:Boolean// companion: 'Object' fàbrica d'instàncies de la col·lecció (generadors i altres mètodes estàtics)defcompanion:GenericCompanion[Col·lecció]defsameElements(that:GenIterable[A]):Boolean// compara els elements amb la col·lecció 'that'defiterator:Iterator[A]// obté un iterador (mètodes: hasNext(), next()) sobre els elementsdefmkString:String// mostra la col·lecció en una StringdefmkString(sep:String):String// ... intercalant separadordeftoString():String// a StringdeftoArray:Array[A]// a Array, correspon als vectors del Java del tipus (A [])deftoBuffer[B>:A]:Buffer[B]// a Buffer (magatzematge temporal incrementable)deftoSeq:Seq[A]// a seqüència, implementada amb ListdeftoSet[B>:A]:Set[B]// a conjuntdeftoList:List[A]// a llistadeftoIndexedSeq:IndexedSeq[A]// a seqüència d'accés aleatori, implementada amb VectordeftoVector:scala.Vector[A]// a vector, implementació per defecte de les IndexedSeqdeftoMap[T,U]:collection.Map[T,U]// a diccionari, si i només si els elements són parells (A ~ (T, U))deftoIterator:Iterator[A]// obté un iterador (travessable un sol cop), equival al mètode 'iterator'deftoStream:Stream[A]// obté un Stream (travessable més d'un cop) deftoTraversable:collection.Traversable[A]// a Travessable (mètode forEach) // retorna la mateixa instància si la col·lecció ja ho és.defto[Col[_]]:Col[A]// a col·lecció explicitant el paràmetre de tipusscala>(1to3).to[List]res1:List[Int]=List(1,2,3)scala>(1to3).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)defapply(idx:Int):A// obtenir l'element a l'índex 'idx'defisDefinedAt(idx:Int):Boolean// està definit per a l'índex 'idx' ?defreverseIterator:Iterator[A]// iterador en sentit invers
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)[69]
Altres signatures: Traversable, Iterable, Set, Map
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ódeffoldLeft[B](z:B)(op:(B,A)⇒B):B// plegat per l'esquerra sobre valor inicialdeffoldRight[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.deffold[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):BdefreduceLeft[B>:A](op:(B,A)⇒B):B// plegat per l'esquerra de la cua sobre el cap (col·l. no buida); func. parcial !! defreduceLeftOption[B>:A](op:(B,A)⇒B):Option[B]// versió total (col·l. buida => None)defreduce[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 !!defreduceOption[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 unitariordenat[71]
plegats de col·leccions d'elements ordenats, o via projecció a imatge ordenable.[72]
defsum[A:Numeric]:Adefproduct[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.defmin[B>:A](implicitcmp:Ordering[B]):Adefmax[B>:A](implicitcmp:Ordering[B]):A// plegats via funció de projecció amb imatge ordenable quina instància d'ordenació es pot adjuntar com a param.defminBy[B](f:(A)⇒B)(implicitcmp:Ordering[B]):AdefmaxBy[B](f:(A)⇒B)(implicitcmp:Ordering[B]):A
Interfície Traversable
Interfí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
Consulta
// A és el tipus de l'element de la col·lecció de tipus ReprdefisEmpty:Boolean// és buida ?defnonEmpty:Boolean// és no buida ?defsize:Int// midadefhasDefiniteSize:Boolean// és finita la col·lecció ? defisTraversableAgain:Boolean// és travessable altra vegada? (els iteradors només una) // com a seqüènciadefhead:A// cap, funció parcial!!! excepció NoSuchElementException si col. buidadefheadOption:Option[A]// cap, versió totaldeftail:Repr// cua, funció parcial!!! excepció NoSuchElementException si col. buida// quantificadors amb predicatdefexists(predicat:(A)⇒Boolean):Boolean// algun element compleix el predicat?defforall(predicat:(A)⇒Boolean):Boolean// el compleixen tots?defcount(predicat:(A)⇒Boolean):Int// compta els que compleixen// cerca per predicatdeffind(predicat:(A)⇒Boolean):Option[A]// cerca per predicat // altres componentsdeflast:A// darrer. funció parcial!!! excepció NoSuchElementException si col. buidadeflastOption:Option[A]// darrer, versió totaldefinit:Repr// descarta el darrer. funció parcial!!! excepció UnsupportedOperationException si col. buida// tails: successió de subcol·leccions escapçant pel davantdeftails:Iterator[Repr]// equival a ((0 to col.size).map (col.drop))// inits: successió de subcol·leccions escapçant pel darreredefinits: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
defforeach[U](f:Elem=>U):Unit// iteració amb funció d'efectes lateralsdefmap[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 defflatMap[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.defcollect[B](pf:PartialFunction[A,B]):Traversable[B]defcollectFirst[B](pf:PartialFunction[A,B]):Option[B]// obté, la primera de les imatges dels elements vàlids en el subdomini de 'pf'
Partició
defslice(from:Int,until:Int):Repr// subcol·lecció corresp. a l'interval obert per la dreta [from .. until) (exclou límit superior)// a l'índexdeftake(n:Int):Repr// prefix de n elementsdefdrop(n:Int):Repr// escapça el prefix de n elementsdefsplitAt(n:Int):(Repr,Repr)// partició a l'índex, com {col => (col.take(n), col.drop(n))}// segons predicatdeffilter(predicat:(A)⇒Boolean):Repr// subcol·lecció dels que compleixen defpartition(predicat:(A)⇒Boolean):(Repr,Repr)// parteix com {col => (col.filter (predicat), col.filter (not predicat))}deftakeWhile(predicat:(A)⇒Boolean):Repr// prefix mentre elements compleixendefdropWhile(predicat:(A)⇒Boolean):Repr// sufix de takeWhile(predicat)defspan(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·leccionsdefgroupBy[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)defwithFilter(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.defview:TraversableView[A,Repr]// vista d'avaluació diferidadefview(from:Int,until:Int):TraversableView[A,Repr]// llesca d'avaluació diferida
Reducció
// successió de plegats parcials per l'esquerradefscanLeft[B,That](z:B)(op:(B,A)⇒B)(implicitbf:CanBuildFrom[Repr,B,That]):That// successió de plegats parcials per la dretadefscanRight[B,That](z:B)(op:(A,B)⇒B)(implicitbf: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)defscan[B>:A,That](z:B)(op:(B,B)⇒B)(implicitcbf:CanBuildFrom[Repr,B,That]):That// concatena elements de col·lecció de col·leccions travessablesdefflatten[B](implicitasTraversable:(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.[74]
É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[75] afegeix a TraversableLike entre d'altres els següents:
partició:
deftakeRight(n:Int):Repr// obtenir sufix de llargada ndefdropRight(n:Int):Repr// escapçar el sufixdefgrouped(n:Int):Iterator[Repr]// retorna iterador de trams de llargada fixa n
combinació:
defzip[B](that:GenIterable[B]):GenIterable[(A,B)]// aparellament per posició amb altra col·lecció// zipAll: zip igualant llargades amb farcimentdefzipAll[B](that:collection.Iterable[B],thisElem:A,thatElem:B):GenIterable[(A,B)]defzipWithIndex:GenIterable[(A,Int)]// aparella una seqüència amb l'índex correlatiu (partint de zero)
mètodes de consulta específics
// a les seqüènciesdefapply(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 defcorresponds[B](that:GenSeq[B])(predicat:(A,B)⇒Boolean):Boolean// als conjuntsdefcontains(key:A):BooleandefsubsetOf(that:GenSet[A]):Boolean// és subcjt de ?defsubsets(len:Int):Iterator[Set[A]]// subcjts de llarg. específicadefsubsets:Iterator[Set[A]]// tots els subconjunts// als diccionarisdefget(key:A):Option[B]// als opcionals ''Option[+A]''defgetOrElse[B>:A](default:⇒B):B// cas de la variant (Some x) n'extreu el valor, altrament avalua el param.deftoRight[T](left:⇒T):Either[T,A]// cas de la variant (Some x) torna Right(x), altrament Left amb el param.deftoLeft[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 Noneright.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)'' defget:T// cas de la variant (Success resultat) retorna resultat, cas de (Failure excep) llança l'excepciódefgetOrElse[U>:T](default:⇒U):U// cas de la variant (Success r) n'extreu el valor, altrament avalua el param.deftoOption:Option[T]// cas de Success(resultat) retorna Some(resultat) altrament None
defcontains(elem:Any):Boolean// està contingut?defdistinct:Seq[A]// diferents (elimina elements repetits)defdiff(that:Seq[A]):Seq[A]// multi-diferència, per cada elem. del de la dreta, elimina una ocurrència.defunion(that:Seq[A]):Seq[A]// multi-unió (concatena)defintersect(that:Seq[A]):Seq[A]// multi-intersecciódefcombinations(n:Int):Iterator[Seq[A]]defpermutations:Iterator[Seq[A]]
seqüències, accés indexat [0..
defindices: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)defapply(n:Int):A// Funció parcial!!! IndexOutOfBoundsException// cerca posició per valordefindexOf(elem:A[,from:Int]):Int// retorna índex o bé (-1)deflastIndexOf(elem:A[,end:Int]):Int// cerca posició per predicatdefindexWhere(predicat:(A)⇒Boolean[,from:Int]):IntdeflastIndexWhere(predicat:(A)⇒Boolean[,end:Int]):Int// actualització funcional amb associació (índex, elem)defupdated[A1>:A](index:Int,elem:A1):Repr// retorna nova col·lecció actualitzada
seqüències, consulta de subseqüències
defslice(from:Int,until:Int):Seq[A]// subseq. a l'interval obert per la dretadefcontainsSlice[B](that:GenSeq[B]):Boolean// conté subseq. ?defstartsWith[B](that:GenSeq[B],offset:Int):Boolean// comprova prefixdefendsWith[B](that:GenSeq[B]):Boolean// comprova sufix// cerca posició de subseq.defindexOfSlice[B>:A](that:GenSeq[B][,from:Int]):Int// retorna pos. o bé (-1)deflastIndexOfSlice[B>:A](that:GenSeq[B][,end:Int]):Int// retorna pos. o bé (-1)// llarg. de subseq. dels elements que compleixen el predicatdefprefixLength(predicat:(A)⇒Boolean):Int// llargada de `takeWhile(predicat)`defsegmentLength(predicat:(A)⇒Boolean,from:Int):Int// llargada `drop(from).takeWhile(predicat)`// obtenció de subseqüències desplaçant una finestra de la mida esmentadadefsliding(size:Int):Iterator[Seq[A]]defsliding(size:Int,step:Int):Iterator[Seq[A]]// step: especifica la distància entre inicis
seqüències, ordenació
defsortWith(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 especificardefsorted[B>:A,B](implicitcmp:Ordering[B]):Seq[A]// ordena segons la imatge ordenable d'una projecció quina instància d'ordenació podem especificardefsortBy[B](f:(A)⇒B)(implicitcmp: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 finalxs++(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 = Adefupdated[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'' elementsdefpatch(from:Int,that:GenSeq[A],replaced:Int):Repr// combina com a multi-conjuntxsintersectys// multi-interseccióxsdiffys// multi-diferènciaxsunionys// multi-unió (concatenació)defdistinct:Seq[A]// distints (nova seq. eliminant repetits)// transposició d'una seqüència de seqüènciesdeftranspose[B](implicitasTraversable:(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 finalxs-=x// eliminarxs.+=(x,y,...)// afegir diversos (llista d'arguments variable)xs.-=(x,y,...)// eliminar diversosxs++=(ys:TraversableOnce[A])// afegir elems. de col·lecció travessablexs--=(ys:TraversableOnce[A])// eliminar elems de col·lecció travessabledeftransform(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.
Mètodes mutadors als Buffers, addicionals als esmentats prèviament per a les seqüències
defappend(elems:A*):Unit// afegeix diversos (llista d'arguments variable)defappendAll(ys:TraversableOnce[A]):Unit// afegeix de col·lecció defprepend(elems:A*):Unit// prefixa diversosdefprependAll(ys:TraversableOnce[A]):Unit// prefixa de col·lecciódefinsert(n:Int,elems:A*):Unit// insereix diversos a la posició ndefinsertAll(n:Int,ys:Traversable[A]):Unit// insereix de col·lecció a la pos. ndefupdate(n:Int,newelem:A):Unit// actualitza l'enèsimdefremove(n:Int):A// elimina l'enèsimdeftrimStart(n:Int):Unit// retalla pel davant n elems.deftrimEnd(n:Int):Unit// retalla pel darrere n elems.defclear():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 = Adefupdated[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
defupdate(í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 finalx+: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.
defenqueue[B>:A](elem:B):Queue[B]// encua un element retornant nova cuadefenqueue[B>:A](iter:Iterable[B]):Queue[B]// encua els elements d'una col·lecció iterable.defdequeue:(A,Queue[A])// descompon la cua NO BUIDA en parell (cap, resta) // funció parcial !!defdequeueOption:Option[(A,Queue[A])]// versió total
mutadors a les cues
Operadors mutadors de les cues mudables (mutable.Queue[A])
xs+=x// encua al finalx+=: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ó travessabledefenqueue(elems:A*):Unit// encua diversos (llista d'arguments variable)defdequeue:A// desencua el cap i el retorna.// segons predicat.defdequeueFirst(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][77] especifica un anell íntegre que implementa la divisió euclidiana, habitualment enters de qualsevol precisió.
scala>importscala.collection.mutable.{PriorityQueue=>PQ}importscala.collection.mutable.{PriorityQueue=>PQ}// amb ordre implícit, prioritat al més granscala>PQ(3,5,2).headres2:Int=5scala>valintOps=implicitly[Integral[Int]]--Integral[T]:ops.delsíntegres(anelleuclidià)intOps:Integral[Int]=scala.math.Numeric$IntIsIntegral$@3a20f22d// ordenació explícita per establir prioritat al més petitscala>PQ(3,5,2)(intOps.reverse).headres8: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ó travessabledefenqueue(elems:A*):Unit// encua diversos (llista d'arguments variable)defdequeue: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].[78]
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 diversosxs++(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 dretaxs&ys;xsintersectys// intersecció de conjuntsxs&~ys;xsdiffys// diferència de conjuntsxs|ys;xsunionys// unió de conjunts
mutadors als conjunts
Operadors mutadors dels conjunts mudables
xs+=x// inserirxs-=x// eliminarxs.+=(x,y,...)// inserir diversos (llista d'arguments variable)xs.-=(x,y,...)// eliminar diversosxs++=(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].[79]
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 kdicc.+((k1,v1),(k2,v2),...)// unió amb diversos parells (llista d'arguments variable)dicc.-(k1,k2,...)// diferència amb els parells quines claus s'esmentendicc++(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 = Bdicc.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 kdicc.+=((k1,v1),(k2,v2),...)// incorpora diversos parells (llista d'arguments variable)dicc.-=(k1,k2,...)// elimina diverses clausdicc++=(ys:GenTraversableOnce[(A,B)])// incorpora associacions de col·lecció de parellsdicc--=(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[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:
deflines:Iterator[String]// parteix en líniesdeflinesWithSeparators:Iterator[String]// parteix ... incorporant separadors de línia, siguin {LF, CRLF, FF}defr:Regex// interpreta la cadena com a patró d'expressió regular, obtenint-ne un valor Regexdefr(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)defcapitalize: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 Cdefformat(args:Any*):StringdefpadTo(len:Int,elem:A):String[A]// farceix al final fins a llargada 'len' amb 'elem' defstripPrefix(prefix:String):String// escapça el prefixdefstripSuffix(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!!defstripMargin:String// escapça el marge esquerre delimitat per '|', incloent el delimitadordefstripMargin(delimitadorDeMarge:Char):String// igual per al delimitador especificat.defstripLineEnd:String// elimina {LF, CRLF, FF} del final// diversos mètodes de java.lang.CharSequencedefcharAt(arg0:Int):Chardeflength():IntdefsubSequence(arg0:Int,arg1:Int):CharSequence// diversos mètodes de java.lang.Stringdefmatches(arg0:String):BooleandefregionMatches(arg0:Boolean,arg1:Int,arg2:String,arg3:Int,arg4:Int):BooleandefreplaceFirst(arg0:String,arg1:String):Stringdefreplace(arg0:Char,arg1:Char):Stringdefreplace(arg0:CharSequence,arg1:CharSequence):StringdefreplaceAll(arg0:String,arg1:String):Stringdefsubstring(arg0:Int):Stringdefsubstring(arg0:Int,arg1:Int):Stringdeftrim():StringdeftoCharArray():Array[Char]defto{Lower|Upper}Case():String// Minúscules/Maj.defto{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íniacontinuació"""""" 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.[82]
// La plantilla "s" com a: s"Hola, $nom !!"valnom="Jaumet"println(s"Hola $nom !!")// Hola Jaumet !!// si l'expressió a interpolar és més complexa, cal delimitar-la entre clausscala>valbenvinguda="benvingut !!"scala>s"Hola $nom. ${benvinguda.capitalize}"res1:String=HolaJaumet.Benvingut!!// La plantilla "f" permet afegir-hi especificadors de format com els del "printf" del llenguatge Cscala>valedat=10scala>f"En $nom%15s té $edat%3d anys !!"res2:String=EnJaumetté10anys!!
scala>valregexDataISO="""(\d\d\d\d)-(\d\d)-(\d\d)""".r// expr. de tipus RegexregexData:scala.util.matching.Regex=(\d\d\d\d)-(\d\d)-(\d\d)scala>"2004-01-20"match{caseregexDataISO(any,mes,dia)=>s"allò va passar el $dia/$mes/$any.";case_=>"no s'ha trobat cap data!!"}res4:String=allòvapassarel20/01/2004.// per al cas de voler caçar múltiples ocurrènciesscala>(for(m<-regexDataISOfindAllMatchIn"2004-03-01, 2005-12-02")yield{m.subgroupsmatch{caseList(any,mes,dia)=>s"$dia/$mes/$any"}}).toListres5:List[String]=List(01/03/2004,02/12/2005)
Biblioteques rellevants
scala-arm
[39] 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.
importresource._// dos recursos, còpia de input a output, amb alliberament automàticfor(input<-managed(newjava.io.FileInputStream("entrada.txt");output<-managed(newjava.io.FileOutputStream("sortida.txt")){valbuffer=newArray[Byte](512)defread():Unit=input.read(buffer)match{case-1=>()casen=>output.write(buffer,0,n);read()}read()}
[86] aporta col·leccions específiques per a l'accés a BDD relacionals generant SQL a partir de mètodes funcionals.
valexplicitInnerJoin=for{(c,s)<-coffeesjoinsupplierson(_.supID===_.id)// FROM coffees AS c INNER JOIN suppliers AS s ON c.supid = s.idifc.namelike"%expresso%"// WHERE c.name LIKE "%expresso%"sortBy(s.price.desc)// ORDER BY s.price DESCdrop(20)// OFFSET ntake(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).[87]
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í[2] 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[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"
Exemples
Lector amb analitzador gramatical
Basat en la classe JavaTokenParsers[89] per aprofitar la decodificació de literals String.
La classe interna Parser[90] 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 (^^^).
packagetest// fitxer: test/LaMevaApp.scala/* Contingut del fitxer de dades amb el llenguatge de simulació de teclat que pretenem processarStr "abcd"EnterStr "linia2"Move UpMove DnMove LeftBackMove RightDel* /object LaMevaApp extends App {import scala.util.parsing.combinator.JavaTokenParsersobject 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 patronsabstract 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.postfixOpsclass 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
mkdirclasses
scalac-dclassessrc/test/LaMevaApp.scala
# execució
scala-cpclassestest.LaMevaApp
Cues amb prioritat sobre parells (prioritat, valor)
Cal especificar explícitament l'ordenació sobre el primer del parell.
importscala.math.Numeric._importscala.collection.mutable.{PriorityQueue=>PQ}objectMainextendsApp{valintOps=implicitly[Integral[Int]]valcuaDeParellsAmbPrioritatAlPrimerDelParell_DeMajorAMenor=PQ((3,"b"),(1,"c"),(5,"a"))(intOps.on(_._1))// Ordering explícit sobre el primer del parellvalcuaDeParellsAmbPrioritatAlPrimerDelParell_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)}}