Примеры кода Scala и Java, где код Scala выглядит проще / имеет меньше строк?


Мне нужны некоторые примеры кода (и мне также очень любопытно о них) кода Scala и Java, которые показывают, что код Scala более прост и лаконичен, чем код, написанный на Java (конечно, оба образца должны решить одну и ту же проблему).

Если есть только образец Scala с комментарием типа" это абстрактная фабрика в Scala, в Java она будет выглядеть гораздо более громоздкой", то это также приемлемо.

спасибо!

Мне больше всего нравится принятое и это ответы

18 91

18 ответов:

давайте улучшим штабелера и использовать Scala case classes:

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

приведенный выше класс Scala содержит все функции приведенного ниже класса Java,и несколько - например, он поддерживает шаблоны (который Java не имеет). Scala 2.8 добавляет именованные и стандартные аргументы, которые используются для создания способ копирование для классов case, что дает ту же способность, что и методы 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)

Я нашел это впечатляющим

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)

а также Эти (извините за не вставку, я не хотел красть код)

задачи: напишите программу для индексирования списка ключевых слов (например, книг).

объяснение:

  • Input: List
  • Вывод: Карта>
  • ключ карты от " А " до "З"
  • каждый список на карте отсортированы.

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

задачи:

у вас есть список 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 8 (внесено @fredoverflow):

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

хотя этот код почти такой же короткий, он работает не так элегантно, как Scala.

в решении Scala,Seq[A]#sortBy метод принимает функцию A => B здесь B требуется есть an 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.

ссылки на лямбды и методы-это только верхушка айсберга, который является функциональным программированием. :)

задачи:

у вас есть XML-файл "компания.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 поля всех сотрудников.


Java: [ взято из здесь]

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: [ взято из здесь, слайд #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)
        } 
      }
    }
  }
}

[ EDIT by Билл; Проверьте комментарии для обсуждения ] --

Хм, как это сделать, не отвечая в неформатированном разделе ответа... Хм. Думаю, я отредактирую ваш ответ и позволю вам удалить его, если он вас беспокоит.

вот как я бы сделал это в 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 имеет довольно плохую поддержку библиотеки в целом, но давайте-чтобы сравнить ужасное использование десятилетия Java(?) старая библиотека XML для реализации, выполненной на основе краткости, просто не является справедливо-и далеко не сравнение языков!

карта действий для выполнения в зависимости от строки.

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

мне понравился этот простой пример сортировки и преобразования, взятый из книги Дэвида Поллака "начало Scala":

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

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

сейчас я пишу игру в блэкджек в скале. Вот как мой метод 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, но он выполняет ту же задачу с тем же 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)
}

мне очень нравится метод 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;
}

да-счет слов, и вот в 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)
    } 

это очень простой пример: квадратные целые числа, а затем добавить их


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

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

Compact map применяет функцию ко всем элементам массива, поэтому:

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

Fold left is будет начинаться с 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)

Как насчет Quicksort?


Java

ниже приведен пример 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. Открытый сезон для улучшителей кода;@)

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

: вам нужно создать метод, который будет выполнять любой код в асинхронном режиме.

Решение в 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
            }
});

то же самое Скала (через актеров):

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 делать ВСЮ работу.

/**
 * 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:

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

116 символов.

Scala:

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

56 символов.

Я готовлю документ, который дает несколько примеров кода Java и Scala, используя только простые для понимания функции Scala:

Scala: лучше Java

Если вы хотите, чтобы я что-то добавил к нему, пожалуйста, ответьте в комментариях.

лениво оцененные бесконечные потоки являются хорошим примером:

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 не может сделать, это запустить код из переводчик / РЕПЛ. Я нахожу это очень полезным для быстрого тестирования фрагментов кода.

этот код 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, если это вообще возможно.