Przykłady kodu Scala i Java, w których kod Scala wygląda na prostszy / ma mniej wierszy?


94

Potrzebuję kilku próbek kodu (i też jestem ich bardzo ciekawy) kodu Scala i Java, które pokazują, że kod Scala jest prostszy i bardziej zwięzły niż kod napisany w Javie (oczywiście oba przykłady powinny rozwiązać ten sam problem).

Jeśli istnieje tylko próbka Scala z komentarzem typu „to jest fabryka abstrakcyjna w Scali, w Javie będzie to wyglądać znacznie bardziej uciążliwie”, to również jest to dopuszczalne.

Dzięki!

Najbardziej podobają mi się akceptowane i takie odpowiedzi


3
Przy odrobinie pracy nóg możesz znaleźć mnóstwo próbek na rosettacode.org
nicerobot

4
Jak może być jedna poprawna odpowiedź na tego rodzaju pytanie?
polygenelubricants

@polygenelubricants: i co sugerujesz?
Roman

10
@Roman: Oczekujemy, że Scala będzie bardziej zwięzła. Byłoby ciekawiej, gdybyś znalazł coś, co jest bardziej zwięzłe wyrażone w Javie niż w Scali.
Randall Schulz

1
@Randall Schulz: wszyscy wiedzą, że Scala jest bardziej zwięzła, ale czasami w celach akademickich potrzebujemy dowodu z przykładami i teorią podstawową.
Roman

Odpowiedzi:


76

Poprawmy przykład układarki i używać Scala klas przypadków :

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

Powyższa klasa Scala zawiera wszystkie cechy poniższej klasy Java, a także kilka innych - na przykład obsługuje dopasowywanie wzorców (czego Java nie ma). Scala 2.8 dodaje nazwane i domyślne argumenty, które są używane do generowania metody kopiowania dla klas przypadków, co daje taką samą możliwość, jak metody with * w następującej klasie Java.

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 + ")";
    }
}

Następnie w użyciu mamy (oczywiście):

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

Przeciwko

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

2
W 2.7.x i 2.8.0 jedyne opakowanie znajduje się w konstruktorze, polu lub akcesorium, productElementsa unapplynie w konstruktorze, polu lub akcesorium: gist.github.com/424375
retronym

2
Zachęca do wszelkiego rodzaju zła getter / setter. Setery powinny być dodawane z wyjątkową niechęcią, gettery powinny być dodawane tylko w razie potrzeby. Dobry przykład tego, jak dodanie słowa „Prostota” prowadzi do złych nawyków.
Bill K

7
@Bill K: OK, to będziemy mieć. I case class Person(val firstName: String, val lastName: String) co z tego? Uczynienie tego czymś prywatnym byłoby również możliwe, ale nie ma sensu z powodu braku zastosowania itp.
soc.

1
@shiva case class Person(private val firstName: String), ale nie powinieneś wtedy używać klas przypadków. Zamiast tego zrób to class Person(firstName: String)i firstNamedomyślnie jest prywatne.
nilskp

1
@shiva Nie. Różnica między vali private valpolega na tym, że metody akcesorów, tj. firstName()i firstName(String), są publiczne czy prywatne. W Scali pola są zawsze prywatne. Aby Scala generowała metody get / set w stylu Java (oprócz metod dostępu w stylu Scala), istnieje @BeanPropertyadnotacja.
Esko Luontola,

45

Ten wydał mi się imponujący

Jawa

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)

Oprócz tych (przepraszam, że nie wklejam, nie chciałem kraść kodu)


Ten kod scala nie będzie generował getFirstNamei getLastNamemetod. Aby scala.reflect.BeanPropertyto zrobić, musisz opisać parametry adnotacjami.
Abhinav Sarkar

7
@ abhin4v: Tak, ale konwencja kodu w Scali nie ma prefiksów przedrostków get. Idiomatyczny kod Java różni się od idiomatycznego kodu Scala. Czasami isprzedrostek używany dla wartości logicznych. davetron5000.github.com/scala-style/naming_conventions/methods/…
Esko Luontola

7
Możesz zrobić to case classi dostać toString, equalsi hashCodeza darmo (i nie musisz też valjawnie przedstawiać argumentów ):case class Person(firstName: String, lastName: String)
Jesper

@shiva, case classnie tylko class.
nilskp

23

Zadanie: napisać program do indeksowania listy słów kluczowych (np. Książek).

Wyjaśnienie:

  • Dane wejściowe: Lista <Ciąg>
  • Dane wyjściowe: Mapa <znak, lista <ciąg znaków>>
  • Kluczem mapy jest „A” do „Z”
  • Każda lista na mapie jest posortowana.

Jawa:

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)
}

Możesz użyć v.sorted zamiast (v sortBy identity).
Eastsun

1
A w Scali 2.8 możesz użyć mapValues ​​(_.sorted) zamiast map {case ...}
Alex Boisvert

10
W Javie 8 kod jest prawie identyczny jak w Scalach: words.stream (). Sort (). Collect (Collectors.groupingBy (it -> it.charAt (0))); Zrób sztuczkę!
Koordynator

11

Zadanie:

Masz listę peopleobiektów klasy, Personktóra ma pola namei age. Twoim zadaniem jest posortowanie tej listy najpierw według name, a następnie według 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))

Aktualizacja

Odkąd napisałem tę odpowiedź, nastąpił spory postęp. Lambdy (i odwołania do metod) w końcu wylądowały w Javie i szturmem podbijają świat Javy.

Tak będzie wyglądał powyższy kod z Javą 8 (nadesłana przez @fredoverflow):

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

Chociaż ten kod jest prawie tak krótki, nie działa tak elegancko jak kod Scala.

W roztworze Scala The Seq[A]#sortBysposób przyjmuje funkcję A => B, gdzie Bjest wymagane, aby miećOrdering . Orderingjest klasą typu. Pomyśl najlepiej o obu światach: tak jak Comparable, jest to niejawne dla danego typu, ale podobnie Comparator, jest rozszerzalne i może być dodane retrospektywnie do typów, które go nie miały. Ponieważ w Javie brakuje klas typów, musi ona powielać każdą taką metodę, raz na Comparable, potem na Comparator. Na przykład zobacz comparingi thenComparing tutaj .

Klasy typów pozwalają na pisanie reguł, takich jak „Jeśli A ma porządek, a B ma porządek, to ich krotka (A, B) również ma uporządkowanie”. W kodzie to znaczy:

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

W ten sposób sortByw naszym kodzie można porównać nazwę, a następnie wiek. Ta semantyka zostanie zakodowana powyższą „regułą”. Programista Scala intuicyjnie spodziewałby się, że to zadziała w ten sposób. Żadnych specjalnych metod, takich jak comparingnie trzeba było dodawaćOrdering .

Lambdy i odwołania do metod to tylko wierzchołek góry lodowej, jaką jest programowanie funkcjonalne. :)


Brakujące lambdy (lub przynajmniej odwołania do metod) to najważniejsza cecha, której brakuje mi w Javie.
Petr Gladkikh

@fredoverflow Dziękujemy za dodanie przykładu Java 8. Wciąż pokazuje, dlaczego podejście Scali jest lepsze. Dodam więcej później.
missingfaktor

@rakemous, kolego, odpowiedź została napisana ponad sześć lat temu.
missingfaktor

10

Zadanie:

Masz plik XML „company.xml”, który wygląda następująco:

<?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>

Musisz przeczytać ten plik i wydrukować pola firstNamei lastNamewszystkich pracowników.


Java: [pobrane stąd ]

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: [pobrane stąd , slajd nr 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)
        } 
      }
    }
  }
}

[EDYCJA przez Bill; Sprawdź komentarze do dyskusji] -

Hmm, jak to zrobić bez odpowiedzi w niesformatowanej sekcji odpowiedzi ... Hmph. Myślę, że zmienię twoją odpowiedź i pozwolę ci ją usunąć, jeśli ci to przeszkadza.

Oto jak zrobiłbym to w Javie z lepszymi bibliotekami:

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]);
    }
} 

To tylko szybki hack bez użycia magii i wszystkich elementów wielokrotnego użytku. Gdybym chciał dodać trochę magii, mógłbym zrobić coś lepszego niż zwrócenie tablicy tablic ciągów, ale nawet jeśli to jest, GoodXMLLib byłby całkowicie wielokrotnego użytku. Pierwszym parametrem scanFor jest sekcja, wszystkie przyszłe parametry będą elementami do znalezienia, które są ograniczone, ale interfejs mógłby zostać nieznacznie wzmocniony, aby dodać wiele poziomów dopasowania bez żadnego problemu.

Przyznam, że Java ma ogólnie dość słabe wsparcie dla bibliotek, ale daj spokój - porównanie okropnego użycia dziesięcioletniej (?) Biblioteki Java Java z implementacją opartą na zwięzłości jest po prostu niesprawiedliwe - i jest daleko z porównania języków!


hmm, przykład w Javie byłby krótszy i lepiej wyglądałby z parserem SAX lub StAX. Ale wciąż SCALA jest naprawdę fajna
oluies

5
Kod Java jest napisany dokładnie w celu przeanalizowania tego konkretnego pliku XML bez próby ponownego użycia i dużej ilości zduplikowanego kodu. Ktokolwiek to napisał, albo celowo starał się wyglądać, jakby nie rozumiał kodowania lub nie rozumiał kodowania.
Bill K

@ Bill K: Nigdy nie robiłem analizowania XML w Javie, więc wybrałem ten przykład z jakiejś przypadkowej witryny. Zapraszam do edycji części odpowiedzi dotyczącej języka Java, nie mam nic przeciwko.
missingfaktor

Cóż, załóżmy, że mówisz o różnicach językowych, a nie o różnicach między bibliotekami - w takim przypadku oba byłyby prawie identyczne. Jedyną różnicą językową w drugim przykładzie jest funkcja dopasowania / wielkości liter, która mogłaby zostać wykonana w pojedynczym wierszu jako pętla for, gdyby została zaimplementowana w ten sposób przez bibliotekę.
Bill K,

@Bill K: Nie, całkowicie się mylisz. Działają tu dwie bardzo potężne funkcje Scala: 1. Literały XML 2. Dopasowywanie wzorców. Java nie ma żadnego z nich. Zatem równoważny kod Java napisany w jakiejś hipotetycznej bibliotece z pewnością NIE będzie identyczny. (Spróbuj napisać, wiesz.)
missingfaktor

10

Mapa działań do wykonania w zależności od ciągu.

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()

A wszystko to w najlepszym możliwym guście!

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();

@Rahul G, myślę, że twoja zmiana jest nieprawidłowa. todos.get("hi")zwroty, Option[()=>Unit]które są potrzebne do prawidłowego dopasowania.
huynhjl

@huynhjl, My bad. Odwróciłem to.
missingfaktor

3
Może być jeszcze krótszy:val defaultFun = {() => println("task not found")}; todos.getOrElse("hi", defaultFun).apply()
Geoff Reedy

2
Jeszcze krócej: val todos = Map("hi" -> { () => println("Good morning!") }) withDefaultValue { () => println("task not found") }a potemtodos("hi")()
Martin Ring

8

Piszę teraz grę w blackjacka w Scali. Oto jak wyglądałaby moja metoda dealerWins w Javie:

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

Oto jak to wygląda w Scali:

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

Brawo dla funkcji wyższego rzędu!

Rozwiązanie Java 8:

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

scala ma bardzo trudną składnię. trzeba dużo pamiętać :-(
AZ_

Scala jest jak CSS, z wieloma atrybutami i właściwościami do zapamiętania
AZ_

1
lepiej:def dealerWins = !(players exists (_ beats dealer))
Kevin Wright

7

Podobał mi się ten prosty przykład sortowania i transformacji zaczerpnięty z książki „Beginning Scala” Davida Pollaka:

W Scali:

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)))

W Javie:

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)

6

A co z Quicksort?


Jawa

Poniżej znajduje się przykład Java znaleziony przez wyszukiwarkę Google,

adres URL to 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

Szybka próba wersji Scala. Otwarty sezon na ulepszacze kodu; @)

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

1
Czy to szybkie sortowanie na połączonych listach ma złożoność czasową O (n ^ 2), czy nie? Zwykle w przypadku list połączonych stosuje się scalanie lub podobne.
Esko Luontola

3
Nie jest również rekurencyjny i dlatego nie nadaje się jako algorytm wydajnościowy (lub taki, który nie przepełni stosu)
oxbow_lakes

Dzięki za przydatne komentarze. Widziałem gdzieś quicksort napisane w ten sposób i byłem pod wrażeniem jego zwartości, najwyraźniej nie poświęcałem mu zbyt wiele uwagi. Dałem się ponieść porównaniu LOC, które zawsze jest kuszące w przypadku Scala v Java.
Don Mackenzie

2
Quicksort nie jest O (n ^ 2) na listach funkcjonalnych, ale z pewnością ma to niebezpieczeństwo. Asymptotycznie nadal jest to średnie O (n log n) , ale istnieje większe statystyczne prawdopodobieństwo trafienia w najgorszy przypadek O (n ^ 2), ponieważ zawsze wybieramy punkt obrotu na początku listy, zamiast wybierać jeden losowo .
Daniel Spiewak

Podwójne filtrowanie jest złe. Zobacz w mojej odpowiedzi na twoje pytanie, jak tego partitionuniknąć.
Daniel C. Sobral

6

Lubiłem nieznanego użytkownika za odpowiedź tyle mam zamiar spróbować poprawić na nim. Poniższy kod nie jest bezpośrednim tłumaczeniem przykładu Java, ale wykonuje to samo zadanie przy użyciu tego samego interfejsu 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)
}

Nie mam jeszcze zainstalowanej scala-2.8, aby przetestować ten fragment, ale myślę, że widzę, co jest intendet - po prostu „słowa kluczowe” nie są w ogóle używane. Tworzy mapę wszystkich strun i ich częstotliwości, prawda?
użytkownik nieznany

@user Tak, właśnie to robi. Czy nie tego właśnie dokonuje twój kod? Rozumiem. Skopiowałem niewłaściwą linię. Naprawię to teraz. :-)
Daniel C. Sobral

6

Bardzo podoba mi się metoda getOrElseUpdate, znaleziona w mutableMap i pokazana tutaj, pierwsza Java, bez:

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;
}

tak - WordCount, a tu w scali:

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
}

A oto w Javie 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;
}

A jeśli chcesz być w 100% funkcjonalny:

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);
}

filteri sortzostały już pokazane, ale zobacz, jak łatwo są zintegrowane z mapą:

    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)
    } 

Bardzo mi się podoba ten przykład. Unika łatwej drogi porównywania klas przypadków i nie popełnia błędu pokazując kod Scala, a nie jego odpowiednik w Javie.
Daniel C. Sobral

5

Oto bardzo prosty przykład: Kwadratowe liczby całkowite, a następnie je dodaj


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

W scali:


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)

Mapa kompaktowa stosuje funkcję do wszystkich elementów tablicy, więc:

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

Zwinięcie w lewo rozpocznie się od 0 jako akumulatora (akumulatorów) i zastosuje się add(s,i)do wszystkich elementów (i) tablicy, więc:

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

Teraz można to dalej zagęścić, aby:

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

Tego nie spróbuję w Javie (za dużo pracy), zamień XML na mapę:


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

Kolejna linijka do pobrania mapy z 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)

Problem z twoim sumSquarew Scali polega na tym, że wygląda bardzo tajemniczo dla programisty Java, który da im amunicję przeciwko tobie, aby narzekać, że Scala jest niejasna i skomplikowana ...
Jesper

Trochę sformatowałem, aby ulepszyć przykład. Mam nadzieję, że to nie zaszkodzi Scali.
Thomas

5
scala> 1 do 10 map (x => x * x) sum res0: Int = 385 Zobaczmy, jak programista java nazywa to tajemnicze. W tym momencie palce w uszach mówią nah-nah-nah.
PSP

3
@Jesper Dla programisty spoza Javy, Java wygląda jak ogromna ilość szumów standardowych i liniowych. To nie znaczy, że nie możesz wykonać prawdziwej pracy w języku.
James Moore

Mógłbyś użyć removeLeft (add) zamiast foldLeft (0) (add). Myślę, że jest to łatwiejsze do odczytania, gdy element początkowy jest elementem zerowym / tożsamości grupy.
Dębilski

5

Problem: musisz zaprojektować metodę, która będzie wykonywała dowolny kod asynchronicznie.

Rozwiązanie w Javie :

/**
* 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
            }
});

To samo w Scali (przy użyciu aktorów):

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    
}

6
Od 2.8 można to zapisać jako Futures.future {body} i jest w rzeczywistości silniejsze, ponieważ przyszłość zwrócona przez to może zostać połączona, aby uzyskać wartość, do której ostatecznie oszacuje.
Dave Griffith

3

Wzorzec wyłącznika obwodu z wydania Michaela Nygarda w FaKods ( link do kodu )

implementacja wygląda tak w Scali:

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


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

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

Co moim zdaniem jest super miłe. Wygląda jak część języka, ale jest prostym połączeniem w obiekcie CircuitBreaker wykonującym całą pracę.

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

Odniesienie w innych językach Google dla „Wyłącznik” + Twój język.


3

Przygotowuję dokument zawierający kilka przykładów kodu Java i Scala, wykorzystując tylko proste do zrozumienia funkcje Scala:

Scala: Lepsza Java

Jeśli chciałbyś abym coś do niego dodała, proszę o odpowiedź w komentarzach.


Tytuł „Scala: A better Java” jest chybiony
duckhunt

2

Dobrym przykładem są leniwie oceniane nieskończone strumienie:

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

}

Oto pytanie dotyczące nieskończonych strumieni w Javie: Czy nieskończony iterator jest złym projektem?

Innym dobrym przykładem są funkcje i zamknięcia pierwszej klasy:

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 nie obsługuje funkcji pierwszej klasy, a naśladowanie domknięć za pomocą anonimowych klas wewnętrznych nie jest zbyt eleganckie. Inną rzeczą, której ten przykład nie może zrobić, jest uruchomienie kodu z interpretera / REPL. Uważam, że jest to niezwykle przydatne do szybkiego testowania fragmentów kodu.


Należy pamiętać, że sito jest zbyt wolne, aby było praktyczne.
Elazar Leibovich

@oxbow_lakes nie ma odpowiednika Java dla tych przykładów.
dbyrne

@dbyme Nieprawda. Możesz łatwo tworzyć podklasy Javy Iterablei Iteratortworzyć nieskończone strumienie.
Daniel C. Sobral

@dbyrne "Kolejną rzeczą, której ten przykład nie może zrobić, jest uruchomienie kodu z interpretera / REPL. Uważam to za niezwykle przydatne do szybkiego testowania fragmentów kodu." Używam strony z notatnikiem w Eclipse do wypróbowania fragmentów Java. Większość, jeśli nie cała Java, działa w tym IDE, więc nie potrzebuję REPL. Korzystałem z notepad.exe i javac w moich pierwszych dniach, kiedy nie byłem pewien języka lub funkcji biblioteki, i po krótkim czasie poszło bardzo dobrze i szybko - chociaż REPL jest nieco łatwiejszy w użyciu - i szybszy. Mogłem całkowicie uniknąć włamania do notatnika, instalując VisualAge, który już mieliśmy

2

Dlaczego nikt tego wcześniej nie opublikował:

Jawa:

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

116 znaków.

Scala:

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

56 znaków.


1
Applicationcecha uważana za szkodliwą ... scala-blogs.org/2008/07/…
missingfaktor

0

Ten kod Scala ...

def partition[T](items: List[T], p: (T, T) => Boolean): List[List[T]] = {
  items.foldRight[List[List[T]]](Nil)((item: T, items: List[List[T]]) => items match {
    case (first :: rest) :: last if p (first, item) =>
      (List(item)) :: (first :: rest) :: last
    case (first :: rest) :: last =>
      (item :: first :: rest) :: last
    case _ => List(List(item))
  })
}

... byłoby całkowicie nieczytelne w Javie, jeśli to w ogóle możliwe.


10
MOJE poprawne OPINIO: dzięki za odpowiedź! ale czy mógłbyś wyjaśnić, co się tam dzieje? Nie jestem jeszcze zaznajomiony ze składnią Scala i (to jest możliwy powód, dla którego) wygląda ona dla mnie całkowicie nieczytelna.
Roman

Jest to partycjonowanie ogólnej listy typu T przy użyciu dostarczonej funkcji partycjonowania jako zabezpieczenia w klauzulach dopasowania wzorców instrukcji case.
TYLKO MOJA poprawna OPINIA

3
Dziwne. Nie jestem nawet ekspertem od Scala i mogę to rozgryźć.
TYLKO MOJA poprawna OPINIA
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.