Scala unisce i paradigmi di programmazione object-oriented e funzionale, utilizzando una sintassi concisa che è pienamente compatibile con Java e gira su JVM. Il supporto per lo stile di programmazione funzionale, e in particolare le espressioni lambda, che non sono ora attesi da aggiungere a Java fino JavaSE 8 nel 2012, può aiutare a ridurre la quantità di codice refusi che si sono tenuti a scrivere, forse rendendo più facile mettere a fuoco sul compito in mano. Questo articolo fornisce un'introduzione a Scala.
scala> val columbus: Int = 1492 Per iniziare, basta installare l'ultima distribuzione Scala del T pila ypesafe , aprire un prompt dei comandi e digitare 'scala': questo avrà inizio il REPL (read-eval ciclo di stampa) ambiente interattivo di programmazione. Ora siete pronti ad entrare la prima linea Scala:
columbus: Int = 1492
Abbiamo appena dichiarato una variabile di tipo int con valore 1492, proprio come faremmo in Java con "Int columbus = 1492;". La differenza a parte la sintassi inverso di mettere il tipo, dopo la variabile a Scala è che il "val" dichiara esplicitamente la variabile come immutabile. Se proviamo a modificarlo:
scala> columbus = 1500 <console>: 8: errore: riassegnazione a val columbus = 1500 ^Notate come il messaggio indica precisamente dove l'errore sta nella linea. Provare a dichiarare la variabile di nuovo, ma questa volta come "var" per renderlo mutevole. A proposito, il compilatore è abbastanza intelligente per sapere che 1492 è un numero intero, e quindi non è necessario specificare il tipo a tutti:
scala> var = 1492 columbus columbus: Int = 1492 scala> columbus = 1500 columbus: Int = 1500Passando, definiamo una classe:
scala> classe Impiegato caso (name: String = "ospite", età: Int = 30, società: String = "DevCode") classe Employee definitoAbbiamo definito un Dipendente classe con 3 campi immutabile chiamato nome, età e società con i valori di default. La parola "caso" è analoga alla istruzione switch in Java, ma più flessibile.Ciò significa che la classe ha un meccanismo aggiuntivo per il pattern matching, così come le altre cose, tra i quali un metodo factory per creare istanze (non è necessario utilizzare la parola chiave "nuovo" per creare uno). Allo stesso modo non c'è bisogno di creare getter default. A differenza di Java le variabili sono pubbliche per impostazione predefinita (non protetto) e Scala crea un getter per le variabili pubblica denominata dopo il nome della variabile stessa. Si potrebbe fare campi mutevole e / o private se si voleva utilizzando "var" di fronte a dei parametri (ad esempio caso Persona di classe (private var name: String)).
Creiamo alcuni casi in modi diversi per esporre le varie caratteristiche, come il nome e gli argomenti di default (disponibile a partire Scala 2.8):
scala> Val Guest dipendenti = () della clientela: Impiegato Impiegato = (guest, 30, DevCode) scala> val guestAge = guest.age / / (il getter di default per la variabile età) guestAge: Int = 300 scala> val anna dipendenti = ("Anna") anna: Impiegato = dipendenti (Anna, 30, DevCode) scala> val thomas dipendenti = ("Tommaso", 41) thomas: Impiegato Impiegato = (Thomas, 41, DevCode) scala> val luke = dipendenti ("Luca", azienda = "LucasArt") luke: Impiegato Impiegato = (Luca, 30, LucasArt) scala> val yoda = luke.copy ("Yoda", età = 800) Yoda: Impiegato = dipendenti (Yoda, 800, LucasArt)Tuttavia, i seguenti
scala> val darth dipendenti = ("Darth", "DevCode") <console>: 9: errore: tipo non corrispondente; trovato: java.lang.String ("DevCode") richiesto: Int. è verificato un errore in un'applicazione che coinvolgono gli argomenti di default. val darth = dipendenti ("Darth", "DevCode") ^... non funziona (non perché Darth non è impiegato presso DevCode!) ma perché il costruttore si aspetta il parametro dell'età in questa posizione in quanto l'argomento non viene nominato esplicitamente.
Ora ci sposteremo a collezioni, poiché è qui che le cose stanno diventando davvero emozionante.
Con Generics (Java 5 in poi), Java possono ad esempio iterare su una lista di interi, scrivendo quanto segue:
Lista numeri <integer> = new <integer> ArrayList (); numbers.add (1); numbers.add (2); numbers.add (3); per (Integer n: i numeri) { System.out.println (" numero "+ n); }che producono
Numero 1
Numero 2
Numero 3
Collezioni Scala sistematicamente distinguere tra immutabile e mutevole collezioni, ma incoraggiare immutabilità con la costruzione di collezioni immutabile di default. Essi simulano integrazioni, aggiornamenti o rimozioni ritornando nuove collezioni da tali operazioni, invece di modificarli.
L'equivalente del codice Scala al codice precedente Java può essere scritto:
scala> Numeri val = Lista (1,2,3) numeri: Lista [Int.] = Lista (1, 2, 3) scala> per (n <- numeri) println ("Number" + n) Numero 1 Numero 2 Numero 3Questo costrutto ciclo "for" è molto vicino allo stile imperativo di programmazione Java. Un altro modo per scrivere a Scala (e molte lingue sulla JVM come Groovy, JRuby o Jython) comporta uno stile più funzionale, usando le espressioni lambda (a volte indicato come chiusure). In breve lambda sono solo le funzioni che è possibile passare in giro come parametri. Queste funzioni prendono come parametri di input (nel nostro caso la "n" numero intero) e ritorno come il loro risultato l'ultima affermazione del loro corpo. Sono in forma
functionName {ingresso = corpo>} scala> numbers.foreach {n: Int => / / basta premere Invio per proseguire sulla riga successiva | println ("Number" + n) |} Numero 1 Numero 2 Numero 3In tal caso il corpo ha una sola istruzione (println. ..) e restituisce quindi Unità cioè un "risultato vuoto" o meno equivalente a vuoto in Java ad eccezione di "vuoto" non restituisce nulla.
Invece di limitarsi a stampare la nostra lista di numeri, diciamo che vogliamo manipolare e trasformare gli elementi, in questo caso vogliamo richiamare i metodi che produrrà un elenco risulta che si può riutilizzare in seguito. Proviamo con alcuni esempi:
scala> val reversedList = numbers.reverse reversedList: Lista [Int.] = Lista (3, 2, 1) scala> val numbersLessThan3 numbers.filter = {n => n <3} numbersLessThan3: Lista [Int.] = Lista (1, 2) scala> val oddNumbers numbers.filterNot = {n => n% 2 == 0} oddNumbers: Lista [Int.] = Lista (1, 3) scala> val higherNumbers numbers.map = {n => n + 10} higherNumbers: Lista [Int.] = List (11, 12, 13)Questa ultima trasformazione "mappa" è molto utile, si applica la chiusura ad ogni elemento della lista e il suo risultato è una lista delle stesse dimensioni che contiene ogni elemento trasformato.
Un ultimo metodo che vorremmo introdurre qui è il metodo "foldLeft", che si propaga stato da un elemento all'altro. Ad esempio, per somma elementi di una lista è necessario accumulare loro e tenere traccia del contatore intermedio da un elemento all'altro:
scala> val sumOfNumbers = numbers.foldLeft (0) {(totale, elemento) => | totale + elemento |} sumOfNumbers: Int = 6Il valore 0 dato come primo argomento a foldLeft è il valore iniziale (il che significa totale = 0 quando si applica la funzione per l'elemento primo elenco). La notazione (totale, elemento) rappresenta una Tuple2, che è a Scala una tupla di 2 elementi (ad esempio, per rappresentare uno spazio 3D coordinate spesso è utile fare riferimento a una Tuple3 (x, y, z), ecc ..). Si noti che per somma l'API Scala fornisce in realtà un metodo di "somma", così l'ultima affermazione avrebbe potuto essere scritto:
scala> val sumOfNumbers = numbers.sum sumOfNumbers: Int = 6Ci sono più molti di questi metodi di trasformazione di raccolta che è possibile verificare dalla API scaladoc . È anche possibile catena di questi metodi (ad esempio numbers.reverse.filter ...) per ottenere codice più conciso, anche se può influenzare la leggibilità.
Infine, una notazione più breve equivalente a {n => n + 10} esiste nella forma di (_ + 10), il che significa che non si deve dichiarare il parametro di ingresso se è solo implicito il metodo che si sta invocando; nel nostro caso "n" è chiamata una variabile anonimo perché si può chiamare qualcosa che ti piace come "x" o "numero", in modo da sottolineare significa un vuoto hai bisogno di riempire con ogni elemento della vostra collezione. (Groovy si riserva la parola "si" al posto di _, e Python usa "sé").
scala> val higherNumbers = numbers.map (_ +10) higherNumbers: Lista [Int.] = List (11, 12, 13)Dopo le manipolazioni di base su numeri interi siamo pronti a saltare in collezione trasformazioni che coinvolgono più oggetti complessi, per esempio utilizzando la classe dei dipendenti che abbiamo definito in precedenza:
scala> val allEmployees = Lista (Luca, anna, ospite, Yoda, thomas) allEmployees: Lista [dipendenti] = Lista (Employee (Luca, 30, LucasArt), dipendenti (Anna, 30, DevCode), dipendenti (guest, 30, DevCode), dipendenti (Yoda, 800, LucasArt), dipendenti (Thomas, 41, DevCode))Da questo elenco di 5 elementi possiamo mantenere, ad esempio, i dipendenti DevCode applicando un filtro che non mancherà di tenere i dipendenti per i quali la funzione anonima restituisce True:
scala> val devcodeEmployees = {allEmployees.filter _.company == "DevCode"} devcodeEmployees: Lista [Employee] = Lista (Employee (Anna, 30, DevCode), dipendenti (guest, 30, DevCode), dipendenti (Thomas, 41 , DevCode)) . scala> val oldEmployees = allEmployees.filter (_.age> 100) mappa (_.name) oldEmployees: Lista [String] = Lista (Yoda)Immaginate la raccolta di allEmployees che abbiamo è il set di risultati che abbiamo ottenuto da una query SQL simile a "SELECT * FROM Impiegati WHERE azienda = 'DevCode'". Ora siamo in grado di ordinare i dipendenti per azienda, trasformando la nostra lista [Employee] in una mappa in cui la chiave è il nome della società e il valore è un elenco di tutti i dipendenti che appartengono a tale società:
scala> val sortedEmployees = allEmployees.groupBy (_.company) sortedEmployees: scala.collection.immutable.Map [String, Lista [Employee]] = Map (DevCode - > List (dipendenti (Anna, 30, DevCode), dipendenti (ospite , 30, DevCode), dipendenti (Thomas, 41, DevCode)), LucasArt -> Lista (Employee (Luca, 30, LucasArt), dipendenti (Yoda, 800, LucasArt)))Come esempio di processo di ogni ulteriore elenco memorizzato come un valore di questo (chiave-> valore) HashMap, potremmo immaginare di calcolo l'età media dei dipendenti per ogni azienda.
In pratica, questo significa che dobbiamo sommare il campo 'età' per tutti i dipendenti di ciascuna lista e dividerlo per il numero di dipendenti in tale elenco. Facciamo che prima solo per DevCode:
scala> devcodeEmployees res4: Lista [Employee] = Lista (Employee (Anna, 30, DevCode), dipendenti (guest, 30, DevCode), dipendenti (Thomas, 41, DevCode)) scala> val devcodeAges = devcodeEmployees.map (_.age) devcodeAges: Lista [Int.] = List (30, 30, 41) scala> val devcodeAverageAge = devcodeAges.sum / devcodeAges.size devcodeAverageAge: Int = 33Tornando al caso più generale della nostra Carta (tasto: String -> valore: Lista [dipendenti]), ora possiamo aggregare e calcolare l'età media per ogni azienda semplicemente scrivere un paio di righe:
scala> val averageAgeByCompany sortedEmployees.map = {case (chiave, valore) => |. valore (0) copia (name = "media", età = (value.map (_.age) somma) / value.size.) } averageAgeByCompany: scala.collection.immutable.Iterable [Employee] = List (dipendenti (media, 33, DevCode), dipendenti (in media, 415, LucasArt))Il "caso (chiave, valore)" è un esempio del meccanismo di modello molto potente di corrispondenza che fornisce Scala. Vedere la documentazione Scala per ulteriori chiarimenti.
Ora abbiamo finito. Quello che abbiamo appena scritto è un po '"Map Reduce" algoritmo. Dal momento che l'aggregazione dei dipendenti per ogni azienda è totalmente indipendente da altre aziende, questo algoritmo è molto semplice da parallelizzare.
Implementazioni equivalente dell'algoritmo, sia in Java e Scala, sono riportati in appendice.
Referenze
Il typesafe stack.Appendice
Ridurre la mappa: Javaclasse Impiegato pubblico { final String nome; finale età Integer; società String finale; dipendenti pubblici (String name, età Integer, società String) { this.name name = == null? "Guest": nome, età this.age = == null? 30: età; this.company = società == null? "DevCode": società; } String getName pubblico () { return name; } pubblico getAge int () { età di ritorno; } public String getCompany () { società ritorno; } @ Override public String toString () { "Employee [name =" ritorno + nome + ", età =" + età + ", azienda = " + azienda + "]"; } } classe Builder { String name, società; età Integer; Builder (String name) { this.name = nome; } Dipendente build () { ritorno new Employee (nome, età, società); } Costruttore di età (età Integer) { this.age età =; return this; } Builder società (società String) { this.company società =; return this; } } importazione java.util.ArrayList; java.util.Collection importazione; java.util.List importazione; com.google.common.base.Function importazione; com.google.common.collect.ImmutableListMultimap importazione; com.google.common importazione. collect.ImmutableSet; com.google.common.collect.Multimaps importazione; public class {MapReduce public static final void main (String [] args) { ospite dipendenti = new Builder ("Guest") build ();. . dipendenti anna = new Builder ("Anna") build (); dipendenti thomas = new Builder ("Thomas "..) età (41) build (); dipendenti luke = new Builder ("Luca LucasArt") build ().; ") società (". Impiegato yoda = new Builder ("Yoda"), età (800).. . società ("LucasArt") build (); Collezione <Employee> dipendenti = new ImmutableListMultimap <String, Employee> personsGroupByCompany = Multimaps.index (dipendenti, new Function <dipendenti, String> () { String pubblici si applica (a persona per i dipendenti) { person.getCompany ritorno (); } }); ImmutableSet <String> companyNamesFromMap = personsGroupByCompany.keySet (); Lista <Employee> averageAgeByCompany = new ArrayList <Employee> (); for (String società: companyNamesFromMap) { Lista <Employee> employeesForThisCompany = personsGroupByCompany.get (società); int somma = 0; per (dipendente dipendenti: employeesForThisCompany) { somma + = "+ AverageAgeByCompany); } }MapReduce.scala:
caso dei dipendenti di classe (name: String = "ospite", età: Int = 30, società: String = "DevCode") MapReduce oggetto { def main (args: Array [String]): Unità = { Val Guest = dipendenti () val anna dipendenti = ("Anna") = val thomas dipendenti ("Tommaso", 41) val luke dipendenti = ("Luca", azienda = "LucasArt") val = luke.copy Yoda ("Yoda ", età = 800) val allEmployees = Lista (Luca, anna, ospite, Yoda, thomas) val sortedEmployees = allEmployees.groupBy (_.company) val averageAgeByCompany sortedEmployees.map = {case (chiave, valore) => valore (0). copia (nome = "media", età = (. value.map (_.age) somma) / value.size) } println ("Risultato:" + averageAgeByCompany) } }
Nessun commento:
Posta un commento
Nota. Solo i membri di questo blog possono postare un commento.