Пробую Scala
Пробую Scala на простой задачке "[подсчета цыплят]".
Три фермера продавали кур на местном рынке. У одного было 10 кур, у второго — 16, у третьего — 26. Чтобы не конкурировать между собой, они договорились продавать кур по одной цене. К обеду они решили, что продажи идут не так уж хорошо, поэтому они все одинаково понизили цену. К концу дня они продали всех кур. Оказалось, что каждый из фермеров за этот день выручил 35 долларов. Какова была цена за курицу до обеда и после обеда?
Для начала целей будет три:
- оценить легкость установки и конфигурации среды
- простота написания достаточно примитивного кода
- оценка быстродействия в сравнении с Си
Установка и конфигурация
Под Ubuntu 14.04 особенных проблем нет
$ sudo apt-get install scala scala-library
Установка Скалы уже дает возможность работы с интерпретатором в интерактивном режиме (REPL), если запустить его из терминала.
$ scala
Среда Geany понимает синтаксис Скалы, но компиляцию и запуск делать не умеет. Лезем в документацию по Geany, оттуда через примеры запуска компилятора в меню Build -- Set build commands и в окошечке вводим примерно такие параметры.
Всё, среда и компилятор готовы. Время потрачено немного, менее часа, однако ссылок о настройке Geany для Скалы в гугле всего одна, да и та для пакета из 12.04. Сообщество кажется не очень большим.
Пишем текст
Для пробы возьмем что-то посложнее, чем вывод "Привет, мир!". Пусть это будет вычисление корня методом Ньютона из викиучебника по Скале. Для простоты - на русском языке.
Первая проблема - в учебнике отсутствует основополагающее понятие структуры программы. Лезем в гугл за помощью, но уже к англоязычным источникам. Находится "Scala for Java programmers". Вполне подойдет. Находим, что нужно писать все тот же class Main
, т.е. те же яйца, но вид в профиль. Значит структура линейного текста будет громоздкой.
object Hello
{
def square(x: Double) = x * x
def improve(guess: Double, x: Double) =
(guess + x / guess) / 2
def isGoodEnough(guess: Double, x: Double) =
abs(square(guess) - x) < 0.001
def sqrtIter(guess: Double, x: Double): Double =
if (isGoodEnough(guess, x))
guess
else
sqrtIter(improve(guess, x), x)
def sqrt(x: Double) = sqrtIter(1.0, x)
def main(args: Array[String])
{
var x = 123
println("Корень из " + x + " is " + sqrt(x))
}
}
Запускаем, компилятор ругается на отсутствие функции abs. Ну, это как раз очевидно, математические библиотеки в универсальных языках по умолчанию подключаются редко. Снова поиск, добавляем нужный import, все работает.
import math._
Времени потрачено менее часа, приемлемо.
Подсчет цыплят
Переходим к цыплятам. За основу беру Си-шный исходник.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
time_t t1, t2;
time(&t1);
int a1, a2, a3;
int x, y;
for (x = 1; x < 3500; x++)
for (y = 1; y < x; y++)
for (a1 = 0; a1 <= 10; a1++)
for (a2 = 0; a2 <= 16; a2++)
for (a3 = 0; a3 <= 26; a3++)
if ((a1 * x + (10 - a1) * y == 3500) &&
(a2 * x + (16 - a2) * y == 3500) &&
(a3 * x + (26 - a3) * y == 3500))
printf("x = %d, y = %d, a1 = %d, b1 = %d, a2 = %d, b2 = %d, a3 = %d, b3 = %d\n",
x, y, a1, 10 - a1, a2, 16 - a2, a3, 26 - a3);
time(&t2);
int seconds = difftime(t2, t1);
printf("Finished. Time elapsed: %d sec\n", seconds);
return 0;
}
Возникает первая проблема, не относящаяся, собственно, к задаче. Как найти разницу между двумя датами в секундах?
Поиск в гугле "scala date difference" выдает несколько источников, он они оказываются не вполне релевантными. Готовых примеров нет. Обнаруживается дискуссия о порочности "родного" Java API по части обработки дат. Пара примеров для org.jedi.time
вроде бы подходит. Но "из коробки" Скала не знает о существовании этих пакетов.
В документации Scala Library API по словам "time" и "date" ничего нужного не находится. Вот так раз, столь базовые уровни - и не предусмотрены.
Хорошо, будем использовать библиотеку Явы. В книжке находится объяснение, как их использовать в Скале. Найденный Ява-пример определения разницы между двумя датами в секундах оказывается выражением. Видимо, за 20 лет не дошли руки у разработчиков добавить функцию. Ладно, не суть.
Начинаем переписывать Си-шный исходник, сказывается отсутствие цикла for (в итоге оказалось, что он есть). Можно оформить его пользовательской функцией? Да, можно, но в другой раз, когда будет более насущная необходимость, а пока возьмем что есть - while.
import java.util.Date
object Chickens
{
def findAndPrint()
{
var x = 1
while (x < 3500)
{
var y = 1
while (y < x)
{
var a1 = 0
while (a1 <= 10)
{
var a2 = 0
while (a2 <= 16)
{
var a3 = 0
while (a3 <= 26)
{
if ((a1 * x + (10 - a1) * y == 3500) &&
(a2 * x + (16 - a2) * y == 3500) &&
(a3 * x + (26 - a3) * y == 3500))
println("x = " + x + ", y = " + y +
", a1 = " + a1 + ", b1 = " + (10 - a1) +
", a2 = " + a2 + ", b2 = " + (16 - a2) +
", a3 = " + a3 + ", b3 = " + (26 - a3))
a3 += 1
}
a2 += 1
}
a1 += 1
}
y += 1
}
x += 1
}
}
def main(args: Array[String])
{
val t1 = new Date
findAndPrint()
val t2 = new Date
val seconds = (t2.getTime() - t1.getTime()) / 1000
println("Finished. Time elapsed " + seconds + " sec")
}
}
Получается довольно громоздко. С оператором for запись явно лаконичнее.
Небольшой скоростемер
Запускаем из терминала подряд по 3 раза, фиксируем время (компьютер довольно слабенький, но цель в относительном сопоставлении).
Запуск | Результат, сек | |
---|---|---|
Скала | Си | |
1 | 156 | 76 |
2 | 156 | 75 |
3 | 156 | 75 |
Среднее | 156 | 75 |
Из цифр следует, что аналогичная программа на Скале примерно в 2 раза медленнее, но другого я и не ожидал, так что никаких претензий по этому критерию. Ну, медленнее и медленнее.
Выводы по первым пробам
Первое впечатление от языка - эклектичность. Ожидал более стройную структуру. Возможно, Скала создавалась по принципу "Си++ для программистов на Си", т.е. для программистов на Яве добавили новые концепты. Возможно также, что это впечатление вызвано поверхностным знакомством, не настаиваю.
Второй вывод: для полноценной работы со Скалой нужен хороший опыт работы с Явой, точнее даже, с Java API. Здесь, видимо, тот же принцип другой парочки "C# - F#".
Обнаруженные неудобства:
- отсутствует цикл for (меня поправили, цикл есть)
- зрительно отличать val и var трудно, const был бы намного лучше
- собственные библиотеки пока не содержат функций работы с датами
- не вполне понятна модульность. Опять модулем является класс?
- сообщество кажется малочисленным
Из достоинств, наличие интерпретатора командной строки, простота установки, и, конечно, бэкграунд Явы - если не хватает функционала, всегда можно вставить костыль на Java API с его 7-милионным сообществом разработчиков.
Задачи, на которых пробовалась Скала не выявили каких-либо преимуществ именно языка. Рекурсивные функции? Это есть и в процедурном программировании, классика жанра - обход дерева или рекурсивный спуск в синтаксическом анализе. Анонимные функции? Есть в расширениях ООП.
Следующей целью должно быть нахождение небольшой задачи, на которой Скала могла бы продемонстрировать свои преимущества прежде всего по лаконичности записи. Вроде 30 строк SQL вместо 300 строк на C#. Тогда станет яснее область применения языка.
blog comments powered by Disqus