Образцы кода Scala и Java, где код Scala выглядит проще / имеет меньше строк?
Мне нужны некоторые примеры кода (и мне тоже очень любопытно) кода Scala и Java, которые показывают, что код Scala более прост и лаконичен, чем код, написанный на Java (конечно, оба примера должны решить одну и ту же проблему).
Если есть только пример Scala с комментарием типа "это абстрактная фабрика в Scala, в Java это будет выглядеть гораздо более громоздким", то это также приемлемо.
Спасибо!
Мне нравится больше всего принято и это ответы
18 ответов
Давайте улучшим пример стека и используем классы случая Scala:
case class Person(firstName: String, lastName: String)
Приведенный выше класс Scala содержит все функции приведенного ниже Java-класса и некоторые другие - например, он поддерживает сопоставление с образцом (которого нет в Java). Scala 2.8 добавляет именованные аргументы и аргументы по умолчанию, которые используются для генерации метода копирования для классов дел, который дает ту же возможность, что и методы with * следующего 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 + ")";
}
}
Тогда при использовании имеем (конечно):
Person mr = new Person("Bob", "Dobbelina");
Person miss = new Person("Roberta", "MacSweeney");
Person mrs = miss.withLastName(mr.getLastName());
против
val mr = Person("Bob", "Dobbelina")
val miss = Person("Roberta", "MacSweeney")
val mrs = miss copy (lastName = mr.lastName)
Я нашел этот впечатляющий
Джава
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)
Как и эти (извините, что не вставил, я не хотел украсть код)
Задача: написать программу для индексирования списка ключевых слов (например, книг).
Объяснение:
- Вход: список
- Вывод: карта<символ, список <строка >>
- Ключ карты от 'A' до 'Z'
- Каждый список на карте отсортирован.
Джава:
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)
}
Задача:
У вас есть список people
объектов класса Person
это имеет поля name
а также age
, Ваша задача сначала отсортировать этот список по name
а затем 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))
Обновить
С тех пор как я написал этот ответ, был достигнут некоторый прогресс. Лямбды (и ссылки на методы) наконец-то приземлились в Java, и они штурмом захватывают мир Java.
Вот как будет выглядеть приведенный выше код с Java 8 (предоставлено @fredoverflow):
people.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
Хотя этот код почти такой же короткий, он работает не так элегантно, как Scala.
В решении Scala, Seq[A]#sortBy
метод принимает функцию A => B
где B
требуется иметь Ordering
, Ordering
это тип-класс. Подумайте лучше о обоих мирах: как Comparable
, это неявно для рассматриваемого типа, но как Comparator
, он расширяемый и может быть добавлен ретроспективно к типам, у которых его нет. Поскольку в Java отсутствуют классы типов, он должен дублировать каждый такой метод, один раз для Comparable
тогда для Comparator
, Например, см. comparing
а также thenComparing
здесь
Классы типов позволяют писать правила, такие как "Если A имеет порядок, а B имеет порядок, то их кортеж (A, B) также имеет порядок". В коде это:
implicit def pairOrdering[A : Ordering, B : Ordering]: Ordering[(A, B)] = // impl
Вот как sortBy
в нашем коде можно сравнить по имени, а затем по возрасту. Эта семантика будет закодирована с указанным выше "правилом". Программист Scala интуитивно ожидает, что это будет работать именно так. Нет специальных методов, таких как comparing
должен был быть добавлен к Ordering
,
Лямбды и ссылки на методы - лишь вершина айсберга, который является функциональным программированием.:)
Карта действий для выполнения в зависимости от строки.
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()
И все это сделано в лучшем вкусе!
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();
Задача:
У вас есть файл XML "company.xml", который выглядит следующим образом:
<?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>
Вы должны прочитать этот файл и распечатать firstName
а также lastName
поля всех сотрудников.
Ява: [взято отсюда ]
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();
}
}
}
Скала: [взято отсюда, слайд № 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)
}
}
}
}
}
[РЕДАКТИРОВАТЬ Биллом; Проверьте комментарии для обсуждения] -
Хм, как это сделать, не отвечая в неформатированном разделе ответов... Хмф. Я думаю, я отредактирую ваш ответ и позволю вам удалить его, если он вас не устраивает.
Вот как я бы сделал это на Java с лучшими библиотеками:
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]);
}
}
Это просто быстрый взлом без магии и всех повторно используемых компонентов. Если бы я хотел добавить немного магии, я мог бы сделать что-то лучше, чем возвращать массив строковых массивов, но даже при этом GoodXMLLib можно было бы использовать повторно. Первым параметром scanFor является раздел, все будущие параметры будут пунктами, которые нужно найти, которые ограничены, но интерфейс может быть слегка отшлифован, чтобы добавить несколько уровней соответствия без реальной проблемы.
Я признаю, что у Java в целом довольно плохая поддержка библиотек, но давайте - сравнивать ужасное использование десятилетней (?) Старой библиотеки XML с реализацией, основанной на краткости, просто несправедливо - и далеко из сравнения языков!
Мне понравился этот простой пример сортировки и преобразования, взятый из книги Дэвида Поллака "Начинающая Скала":
В Скала:
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)))
В 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)
Сейчас я пишу игру в блэкджек в Scala. Вот как мой метод dealerWins будет выглядеть в Java:
boolean dealerWins() {
for(Player player : players)
if (player.beats(dealer))
return false;
return true;
}
Вот как это выглядит в Scala:
def dealerWins = !(players.exists(_.beats(dealer)))
Ура для функций высшего порядка!
Решение Java 8:
boolean dealerWins() {
return players.stream().noneMatch(player -> player.beats(dealer));
}
Как насчет быстрой сортировки?
Джава
Ниже приведен пример Java, найденный с помощью поиска Google,
URL-адрес - 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
Быстрая попытка версии Scala. Открытый сезон для улучшителей кода;@)
def qsort(l: List[Int]): List[Int] = {
l match {
case Nil => Nil
case pivot::tail => qsort(tail.filter(_ < pivot)) ::: pivot :: qsort(tail.filter(_ >= pivot))
}
}
Мне очень нравится метод getOrElseUpdate, найденный в mutableMap и показанный здесь, сначала на Java, без:
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;
}
да - это WordCount, а здесь в 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
}
И вот это в 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;
}
И если вы хотите перейти на 100% функциональность:
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
а также sort
уже было показано, но посмотрите, как легко они интегрируются с картой:
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)
}
Мне так понравился ответ user unknown, что я постараюсь улучшить его. Приведенный ниже код не является прямым переводом примера Java, но он выполняет ту же задачу с тем же 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)
}
Это очень простой пример: квадратные целые числа, а затем добавить их
public int sumSquare(int[] list) {
int s = 0;
for(int i = 0; i < list.length; i++) {
s += list[i] * list[i];
}
return s;
}
В скале:
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)
Компактная карта применяет функцию ко всем элементам массива, поэтому:
Array(1,2,3).map(square)
Array[Int] = Array(1, 4, 9)
Сложите влево, начнется с 0 в качестве аккумулятора (ов) и применить add(s,i)
ко всем элементам (i) массива, так что:
Array(1,4,9).foldLeft(0)(add) // return 14 form 0 + 1 + 4 + 9
Теперь это может быть дополнительно сжато:
Array(1,2,3).map(x => x * x ).foldLeft(0)((s,i) => s + i )
Это я не буду пытаться в Java (чтобы много работать), превратить XML в карту:
<a>
<b id="a10">Scala</b>
<b id="b20">rules</b>
</a>
Еще один лайнер, чтобы получить карту из 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)
Проблема: вам нужно разработать метод, который будет выполнять любой данный код асинхронно.
Решение в 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
}
});
То же самое в Scala (с использованием актеров):
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
}
Шаблон выключателя из релиза Майкла Найгарда в FaKods ( ссылка на код)
реализация выглядит так в Scala:
. . .
addCircuitBreaker("test", CircuitBreakerConfiguration(100,10))
. . .
class Test extends UsingCircuitBreaker {
def myMethodWorkingFine = {
withCircuitBreaker("test") {
. . .
}
}
def myMethodDoingWrong = {
withCircuitBreaker("test") {
require(false,"FUBAR!!!")
}
}
}
Который я считаю супер хорошим. Он выглядит просто как кусочек языка, но это простой микс в CircuitBreaker Object, выполняющий всю работу.
/**
* Basic MixIn for using CircuitBreaker Scope method
*
* @author Christopher Schmidt
*/
trait UsingCircuitBreaker {
def withCircuitBreaker[T](name: String)(f: => T): T = {
CircuitBreaker(name).invoke(f)
}
}
Ссылка на других языках Google для "Автоматический выключатель" + ваш язык.
Я готовлю документ, который дает несколько примеров кода Java и Scala, используя только простые для понимания функции Scala:
Если вы хотите, чтобы я добавил что-то к этому, пожалуйста, ответьте в комментариях.
Лениво оцениваемые бесконечные потоки являются хорошим примером:
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
}
Вот вопрос, обращающийся к бесконечным потокам в Java: плохой дизайн бесконечного итератора?
Еще один хороший пример - функции первого класса и замыкания:
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 не поддерживает функции первого класса, и имитация замыканий с анонимными внутренними классами не очень элегантна. В этом примере показано, что java не может сделать это запуск кода из интерпретатора /REPL. Я считаю это чрезвычайно полезным для быстрого тестирования фрагментов кода.
Почему никто не опубликовал это раньше:
Джава:
class Hello {
public static void main( String [] args ) {
System.out.println("Hello world");
}
}
116 символов.
Scala:
object Hello extends App {
println("Hello world")
}
56 символов.
Этот код 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))
})
}
... было бы совершенно нечитаемым в Java, если вообще возможно.