web-dev-qa-db-fra.com

Exemples de Scala et Java où Scala semble plus simple / a moins de lignes?)

J'ai besoin de quelques exemples de code (et je suis aussi très curieux à leur sujet) de Scala et Java code qui montre que Scala le code est plus simple et concis que le code écrit en Java (bien sûr, les deux exemples devraient résoudre le même problème).

S'il n'y a que Scala échantillon avec un commentaire comme "ceci est une fabrique abstraite à Scala, dans Java cela aura l'air beaucoup plus lourd"), cela est également acceptable.

Merci!

J'aime surtout accepté et this réponses

91
Roman

Améliorons l'exemple de l'empileur et utilisons les classes de cas de Scala :

case class Person(firstName: String, lastName: String)

La classe Scala ci-dessus contient toutes les fonctionnalités de la classe Java, et bien d'autres - par exemple, il prend en charge la correspondance de motifs (que Java n'a pas). Scala 2.8 ajoute des arguments nommés et par défaut, qui sont utilisés pour générer une méthode de copie pour les classes de cas, ce qui donne la même capacité que les méthodes with * des Java classe.

public class Person implements Serializable {
    private final String firstName;
    private final String lastName;

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public Person withFirstName(String firstName) {
        return new Person(firstName, lastName);
    }

    public Person withLastName(String lastName) {
        return new Person(firstName, lastName);
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Person person = (Person) o;
        if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) {
            return false;
        }
        if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null) {
            return false;
        }
        return true;
    }

    public int hashCode() {
        int result = firstName != null ? firstName.hashCode() : 0;
        result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
        return result;
    }

    public String toString() {
        return "Person(" + firstName + "," + lastName + ")";
    }
}

Ensuite, en utilisation, nous avons (bien sûr):

Person mr = new Person("Bob", "Dobbelina");
Person miss = new Person("Roberta", "MacSweeney");
Person mrs = miss.withLastName(mr.getLastName());

Contre

val mr = Person("Bob", "Dobbelina")
val miss = Person("Roberta", "MacSweeney")
val mrs = miss copy (lastName = mr.lastName)
75
Esko Luontola

J'ai trouvé celui-ci impressionnant

Java

public class Person {
    private final String firstName;
    private final String lastName;
    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    public String getFirstName() {
        return firstName;
    }
    public String getLastName() {
        return lastName;
    }
}

Scala

class Person(val firstName: String, val lastName: String)

Ainsi que ceux-ci (désolé de ne pas avoir collé, je ne voulais pas voler le code)

45
stacker

Tâche: Écrire un programme pour indexer une liste de mots clés (comme des livres).

Explication:

  • Entrée: liste <String>
  • Sortie: carte <caractère, liste <chaîne>>
  • La clé de la carte est 'A' à 'Z'
  • Chaque liste de la carte est triée.

Java:

import Java.util.*;

class Main {
  public static void main(String[] args) {
    List<String> keywords = Arrays.asList("Apple", "Ananas", "Mango", "Banana", "Beer"); 
    Map<Character, List<String>> result = new HashMap<Character, List<String>>(); 
    for(String k : keywords) {   
      char firstChar = k.charAt(0);     
      if(!result.containsKey(firstChar)) {     
        result.put(firstChar, new  ArrayList<String>());   
      }     
      result.get(firstChar).add(k); 
    } 
    for(List<String> list : result.values()) {   
      Collections.sort(list); 
    }
    System.out.println(result);         
  }
}

Scala:

object Main extends App {
  val keywords = List("Apple", "Ananas", "Mango", "Banana", "Beer")
  val result = keywords.sorted.groupBy(_.head)
  println(result)
}
23
missingfaktor

Tâche:

Vous avez une liste people d'objets de classe Person qui contient les champs name et age. Votre tâche consiste à trier cette liste d'abord par name, puis par age.

Java 7:

Collections.sort(people, new Comparator<Person>() {
  public int compare(Person a, Person b) {
    return a.getName().compare(b.getName());
  }
});
Collections.sort(people, new Comparator<Person>() {
  public int compare(Person a, Person b) {
    return Integer.valueOf(a.getAge()).compare(b.getAge());
  }
});

Scala:

val sortedPeople = people.sortBy(p => (p.name, p.age))

Mise à jour

Depuis que j'ai écrit cette réponse, il y a eu pas mal de progrès. Les lambdas (et les références de méthode) ont finalement atterri à Java, et ils prennent d'assaut le monde Java).

Voici à quoi ressemblera le code ci-dessus avec Java 8 (contribué par @fredoverflow):

people.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));

Bien que ce code soit presque aussi court, il ne fonctionne pas aussi élégamment que celui Scala.

Dans Scala, le Seq[A]#sortBy la méthode accepte une fonction A => BB est requis pour avoir un Ordering. Ordering est une classe de type. Pensez mieux aux deux mondes: comme Comparable, il est implicite pour le type en question, mais comme Comparator, il est extensible et peut être ajouté rétrospectivement aux types qui ne l'ont pas. Puisque Java manque de classes de types, il doit dupliquer chacune de ces méthodes, une fois pour Comparable, puis pour Comparator. Par exemple, voir comparing et thenComparingici .

Les classes de types permettent d'écrire des règles telles que "Si A a ordonné et B a ordonné, alors leur Tuple (A, B) a également ordonné". En code, c'est:

implicit def pairOrdering[A : Ordering, B : Ordering]: Ordering[(A, B)] = // impl

C'est ainsi que le sortBy dans notre code peut se comparer par nom puis par âge. Ces sémantiques seront encodées avec la "règle" ci-dessus. Un programmeur Scala s'attendrait intuitivement à ce que cela fonctionne de cette façon. Aucune méthode spéciale comme comparing n'a dû être ajoutée à Ordering.

Les lambdas et les références de méthode ne sont que la pointe d'un iceberg qui est une programmation fonctionnelle. :)

11
missingfaktor

Tâche:

Vous avez un fichier XML "company.xml" qui ressemble à ceci:

<?xml version="1.0"?>
<company>
    <employee>
        <firstname>Tom</firstname>
        <lastname>Cruise</lastname>
    </employee>
    <employee>
        <firstname>Paul</firstname>
        <lastname>Enderson</lastname>
    </employee>
    <employee>
        <firstname>George</firstname>
        <lastname>Bush</lastname>
    </employee>
</company>

Vous devez lire ce fichier et imprimer les champs firstName et lastName de tous les employés.


Java: [extrait de ici ]

import Java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class XmlReader {
  public static void main(String[] args) {   
    try {
      File file = new File("company.xml");
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      DocumentBuilder db = dbf.newDocumentBuilder();
      Document doc = db.parse(file);
      doc.getDocumentElement().normalize();
      NodeList nodeLst = doc.getElementsByTagName("employee");
      for (int s = 0; s < nodeLst.getLength(); s++) {  
        Node fstNode = nodeLst.item(s); 
        if (fstNode.getNodeType() == Node.ELEMENT_NODE) {         
          Element fstElmnt = (Element) fstNode;
          NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("firstname");
          Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
          NodeList fstNm = fstNmElmnt.getChildNodes();
          System.out.println("First Name: "  + ((Node) fstNm.item(0)).getNodeValue());
          NodeList lstNmElmntLst = fstElmnt.getElementsByTagName("lastname");
          Element lstNmElmnt = (Element) lstNmElmntLst.item(0);
          NodeList lstNm = lstNmElmnt.getChildNodes();
          System.out.println("Last Name: " + ((Node) lstNm.item(0)).getNodeValue());
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}



Scala: [extrait de ici , diapo # 19]

import xml.XML

object XmlReader {
  def main(args: Array[String]): Unit = {
    XML.loadFile("company.xml") match {
      case <employee> { employees @ _* } </employee> => {
        for(e <- employees) {
          println("First Name: " + (e \ "firstname").text)
          println("Last Name: " + (e \ "lastname").text)
        } 
      }
    }
  }
}

[MODIFIER par Bill; Vérifiez les commentaires pour la discussion] -

Hmm, comment le faire sans répondre dans une section de réponse non formatée ... Hmph. Je suppose que je vais modifier votre réponse et vous laisser la supprimer si cela vous dérange.

Voici comment je le ferais dans Java avec de meilleures bibliothèques:

public scanForEmployees(String filename) {
    GoodXMLLib source=new GoodXMLLib(filename);
    while( String[] employee: source.scanFor("employee", "firstname", "lastname") )
    {
          System.out.println("First Name: " + employee[0]);
          System.out.println("Last Name: " + employee[1]);
    }
} 

Ceci est juste un hack rapide n'impliquant aucune magie et tous les composants réutilisables. Si je voulais ajouter un peu de magie, je pourrais faire quelque chose de mieux que de renvoyer un tableau de tableaux de chaînes, mais même comme cela, GoodXMLLib serait complètement réutilisable. Le premier paramètre de scanFor est la section, tous les paramètres futurs seraient les éléments à trouver, ce qui est limité, mais l'interface pourrait être légèrement améliorée pour ajouter plusieurs niveaux de correspondance sans problème réel.

Je vais admettre que Java a un support de bibliothèque assez médiocre en général, mais allez - pour comparer une utilisation horrible de la bibliothèque XML de la décennie (?) De Java à une implémentation basée sur une concision n'est tout simplement pas juste - et est loin d'être une comparaison des langues!

10
missingfaktor

Une carte des actions à effectuer en fonction d'une chaîne.

Java 7:

// strategy pattern = syntactic cruft resulting from lack of closures
public interface Todo {   
  public void perform();
}

final Map<String, Todo> todos = new HashMap<String,Todo>();
todos.put("hi", new Todo() { 
    public void perform() { 
        System.out.println("Good morning!");
    } 
} );

final Todo todo = todos.get("hi");
if (todo != null)
    todo.perform();
else
    System.out.println("task not found");

Scala:

val todos = Map( "hi" -> { () => println("Good morning!") } )
val defaultFun = () => println("task not found")
todos.getOrElse("hi", defaultFun).apply()

Et tout est fait dans le meilleur goût possible!

Java 8:

Map<String, Runnable> todos = new HashMap<>();
todos.put("hi", () -> System.out.println("Good morning!"));
Runnable defaultFun = () -> System.out.println("task not found");
todos.getOrDefault("hi", defaultFun).run();
9
Ben Hardy

J'écris un jeu de Blackjack en Scala maintenant. Voici à quoi ressemblerait ma méthode dealerWins en Java:

boolean dealerWins() {
    for(Player player : players)
        if (player.beats(dealer))
            return false;
    return true;
}

Voici à quoi cela ressemble à Scala:

def dealerWins = !(players.exists(_.beats(dealer)))

Hourra pour les fonctions d'ordre supérieur!

Solution Java 8:

boolean dealerWins() {
    return players.stream().noneMatch(player -> player.beats(dealer));
}
7
MikeFHay

J'ai aimé cet exemple simple de tri et de transformation, tiré du livre `` Beginning Scala '' de David Pollak:

À Scala:

def validByAge(in: List[Person]) = in.filter(_.valid).sortBy(_.age).map(_.first)
case class Person(val first: String, val last: String, val age: Int) {def valid: Boolean = age > 18}
validByAge(List(Person("John", "Valid", 32), Person("John", "Invalid", 17), Person("OtherJohn", "Valid", 19)))

En Java:

public static List<String> validByAge(List<Person> in) {
   List<Person> people = new ArrayList<Person>();
   for (Person p: in) {
     if (p.valid()) people.add(p);
   }
   Collections.sort(people, new Comparator<Person>() {
      public int compare(Person a, Person b) {
        return a.age() - b.age();
      } 
   } );
   List<String> ret = new ArrayList<String>();
     for (Person p: people) {
       ret.add(p.first);
     }
   return ret;
}

public class Person {
    private final String firstName;
    private final String lastName;
    private final Integer age;
    public Person(String firstName, String lastName, Integer age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
    public String getFirst() {
        return firstName;
    }
    public String getLast() {
        return lastName;
    }
    public Integer getAge() {
       return age;
    }
    public Boolean valid() {
       return age > 18;
    }
}

List<Person> input = new ArrayList<Person>();
input.add(new Person("John", "Valid", 32));
input.add(new Person("John", "InValid", 17));
input.add(new Person("OtherJohn", "Valid", 19));

List<Person> output = validByAge(input)
7
Arjan Blokzijl

J'aime beaucoup la méthode getOrElseUpdate, trouvée dans mutableMap et montrée ici, premier Java, sans:

public static Map <String, Integer> wordCount (Scanner sc, String delimiters) {
    Map <String, Integer> dict = new HashMap <String, Integer> ();
            while (sc.hasNextLine ()) {
                    String[] words = sc.nextLine ().split (delimiters);
                    for (String Word: words) {
                        if (dict.containsKey (Word)) {
                            int count = dict.get (Word);
                            dict.put (Word, count + 1);
                        } else
                            dict.put (Word, 1);
                    }
            }       
    return dict;
}

oui - un WordCount, et ici en scala:

def wordCount (sc: Scanner, delimiter: String) = {
        val dict = new scala.collection.mutable.HashMap [String, Int]()
        while (sc.hasNextLine ()) {
                val words = sc.nextLine.split (delimiter)
                words.foreach (Word =>
                      dict.update (Word, dict.getOrElseUpdate (Word, 0) + 1))
        }
        dict
}

Et le voici en Java 8:

public static Map<String, Integer> wordCount(Scanner sc, String delimiters)
{
    Map<String, Integer> dict = new HashMap<>();
    while (sc.hasNextLine())
    {
        String[] words = sc.nextLine().split(delimiters);
        Stream.of(words).forEach(Word -> dict.merge(Word, 1, Integer::sum));
    }
    return dict;
}

Et si vous voulez devenir 100% fonctionnel:

import static Java.util.function.Function.identity;
import static Java.util.stream.Collectors.*;

public static Map<String, Long> wordCount(Scanner sc, String delimiters)
{
    Stream<String> stream = stream(sc.useDelimiter(delimiters));
    return stream.collect(groupingBy(identity(), counting()));
}

public static <T> Stream<T> stream(Iterator<T> iterator)
{
    Spliterator<T> spliterator = Spliterators.spliteratorUnknownSize(iterator, 0);
    return StreamSupport.stream(spliterator, false);
}

filter et sort ont déjà été affichés, mais regardez comme ils sont faciles à intégrer à la carte:

    def filterKeywords (sc: Scanner, keywords: List[String]) = {
            val dict = wordCount (sc, "[^A-Za-z]")
            dict.filter (e => keywords.contains (e._1)).toList . sort (_._2 < _._2)
    } 
6
user unknown

J'ai aimé tilisateur inconnréponse tellement je vais essayer de l'améliorer. Le code ci-dessous n'est pas une traduction directe de l'exemple Java, mais il accomplit la même tâche avec la même API .

def wordCount (sc: Scanner, delimiter: String) = {
  val it = new Iterator[String] {
    def next = sc.nextLine()
    def hasNext = sc.hasNextLine()
  }
  val words = it flatMap (_ split delimiter iterator)
  words.toTraversable groupBy identity mapValues (_.size)
}
6
Daniel C. Sobral

Que diriez-vous de Quicksort?


Java

Ce qui suit est un Java trouvé via une recherche google,

l'URL est http://www.mycstutorials.com/articles/sorting/quicksort

public void quickSort(int array[]) 
// pre: array is full, all elements are non-null integers
// post: the array is sorted in ascending order
{
   quickSort(array, 0, array.length - 1);   // quicksort all the elements in the array
}


public void quickSort(int array[], int start, int end)
{
   int i = start;      // index of left-to-right scan
   int k = end;        // index of right-to-left scan

   if (end - start >= 1)               // check that there are at least two elements to sort
   {
       int pivot = array[start];       // set the pivot as the first element in the partition

       while (k > i)                   // while the scan indices from left and right have not met,
       {
           while (array[i] <= pivot && i <= end && k > i) // from the left, look for the first
              i++;                                        // element greater than the pivot
           while (array[k] > pivot && k >= start && k >= i) // from the right, look for the first
              k--;                                          // element not greater than the pivot
           if (k > i)                  // if the left seekindex is still smaller than
               swap(array, i, k);      // the right index, swap the corresponding elements
       }
       swap(array, start, k);          // after the indices have crossed, swap the last element in
                                       // the left partition with the pivot 
       quickSort(array, start, k - 1); // quicksort the left partition
       quickSort(array, k + 1, end);   // quicksort the right partition
    }
    else // if there is only one element in the partition, do not do any sorting
    {
        return;                        // the array is sorted, so exit
    }
}

public void swap(int array[], int index1, int index2) 
// pre: array is full and index1, index2 < array.length
// post: the values at indices 1 and 2 have been swapped
{
   int temp      = array[index1];      // store the first value in a temp
   array[index1] = array[index2];      // copy the value of the second into the first
   array[index2] = temp;               // copy the value of the temp into the second
}

Scala

Un essai rapide d'une version Scala. Open season for code improvers; @)

def qsort(l: List[Int]): List[Int] = {
  l match {
    case Nil         => Nil
    case pivot::tail => qsort(tail.filter(_ < pivot)) ::: pivot :: qsort(tail.filter(_ >= pivot))
  }
}
6
Don Mackenzie

Problème: vous devez concevoir une méthode qui exécutera tout code donné de manière asynchrone.

Solution en Java:

/**
* This method fires runnables asynchronously
*/
void execAsync(Runnable runnable){
    Executor executor = new Executor() {
        public void execute(Runnable r) {
            new Thread(r).start();
        }
    };
    executor.execute(runnable);
}

...

execAsync(new Runnable() {
            public void run() {
                ...   // put here the code, that need to be executed asynchronously
            }
});

La même chose dans Scala (en utilisant des acteurs):

def execAsync(body: => Unit): Unit = {
  case object ExecAsync    
  actor {
    start; this ! ExecAsync
    loop {
      react {           
        case ExecAsync => body; stop
      }
    }
  }    
}

...

execAsync{  // expressive syntax - don't need to create anonymous classes
  ...  // put here the code, that need to be executed asynchronously    
}
5
Vasil Remeniuk

Ceci est un exemple très simple: des entiers carrés, puis les ajouter


    public int sumSquare(int[] list) {
        int s = 0;
        for(int i = 0; i < list.length; i++) {
            s += list[i] * list[i]; 
        }
        return s;
    }

En scala:


val ar = Array(1,2,3)
def square(x:Int) = x * x
def add(s:Int,i:Int) = s+i

ar.map(square).foldLeft(0)(add)

La carte compacte applique la fonction à tous les éléments du tableau, donc:

Array(1,2,3).map(square)
Array[Int] = Array(1, 4, 9)

Plier à gauche commence par 0 comme accumulateur (s) et applique add(s,i) à tous les éléments (i) du tableau, de sorte que:

 Array(1,4,9).foldLeft(0)(add)  // return 14 form 0 + 1 + 4 + 9

Maintenant, cela peut être encore plus compacté pour:

Array(1,2,3).map(x => x * x ).foldLeft(0)((s,i) => s + i )

Celui-ci, je ne vais pas essayer dans Java (pour beaucoup de travail), transformer XML en une carte:


<a>
   <b id="a10">Scala</b>
   <b id="b20">rules</b>
</a>

Un autre liner pour obtenir la carte du XML:


val xml = <a><b id="a10">Scala</b><b id="b20">rules</b></a>

val map = xml.child.map( n => (n \ "@id").text -> n.child.text).toMap
// Just to dump it.
for( (k,v) <- map) println(k + " --> " + v)
5
Thomas

Le modèle de disjoncteur de Michael Nygard's Release It in FaKods ( lien vers le code )

l'implémentation ressemble à ceci dans Scala:

. . .
addCircuitBreaker("test", CircuitBreakerConfiguration(100,10))
. . .


class Test extends UsingCircuitBreaker {
  def myMethodWorkingFine = {
    withCircuitBreaker("test") {
      . . .
    }
  }

  def myMethodDoingWrong = {
    withCircuitBreaker("test") {
      require(false,"FUBAR!!!")
    }
  }
}

Je pense que c'est super sympa. Cela ressemble à un morceau du langage mais c'est un simple mixin dans CircuitBreaker Object faisant tout le travail.

/**
 * Basic MixIn for using CircuitBreaker Scope method
 *
 * @author Christopher Schmidt
 */
trait UsingCircuitBreaker {
  def withCircuitBreaker[T](name: String)(f: => T): T = {
    CircuitBreaker(name).invoke(f)
  }
}

Référence dans d'autres langues Google pour "Disjoncteur" + votre langue.

3
oluies

Je prépare un document qui donne plusieurs exemples de Java et Scala, utilisant uniquement les fonctionnalités simples à comprendre de Scala:

Scala: un meilleur Java

Si vous souhaitez que j'y ajoute quelque chose, veuillez répondre dans les commentaires.

2
HRJ

Pourquoi personne n'a posté ça avant:

Java:

class Hello {
     public static void main( String [] args ) {
          System.out.println("Hello world");
     }
}

116 caractères.

Scala:

object Hello extends App {
     println("Hello world")
}

56 caractères.

2
OscarRyz

Les flux infinis évalués paresseusement en sont un bon exemple:

object Main extends Application {

   def from(n: Int): Stream[Int] = Stream.cons(n, from(n + 1))

   def sieve(s: Stream[Int]): Stream[Int] =
     Stream.cons(s.head, sieve(s.tail filter { _ % s.head != 0 }))

   def primes = sieve(from(2))

   primes take 10 print

}

Voici une question concernant les flux infinis en Java: n itérateur infini est-il une mauvaise conception?

Un autre bon exemple sont les fonctions et fermetures de première classe:

scala> def f1(w:Double) = (d:Double) => math.sin(d) * w
f1: (w: Double)(Double) => Double

scala> def f2(w:Double, q:Double) = (d:Double) => d * q * w
f2: (w: Double,q: Double)(Double) => Double

scala> val l = List(f1(3.0), f2(4.0, 0.5))
l: List[(Double) => Double] = List(<function1>, <function1>)

scala> l.map(_(2))
res0: List[Double] = List(2.727892280477045, 4.0)

Java ne prend pas en charge les fonctions de première classe et imiter les fermetures avec des classes internes anonymes n'est pas très élégant. Une autre chose que cet exemple montre que Java ne peut pas faire est d'exécuter du code à partir d'un interpréteur/REPL. Je trouve cela extrêmement utile pour tester rapidement des extraits de code.

1
dbyrne