Playframework 2.0 уже не за горами, а его ядро запрограммировано в Scala , так что это прекрасная возможность попробовать этот объектно-функциональный гибридный зверь…
Как и многие другие, я выберу очень простой сценарий, чтобы дать свои первые шаги …
Найти повод, чтобы попробовать Scala
Вместе с парой друзей мы находимся на пути перевода документации по игровым фреймворкам на испанский язык (посмотрите на http://playdoces.appspot.com/ , кстати, вы можете сотрудничать с нами )
Документация состоит из нескольких файлов .textile, и у меня был очень простой и глупый bash-скрипт для отслеживания нашего прогресса. Каждый файл, который еще не был переведен, имеет в первой строке фразу «todavía no ha sido traducida».
1
|
echo pending: `grep "todavía no ha sido traducida" * | wc -l` / `ls | wc -l` |
Который произвел что-то вроде
1
|
pending: 40 / 63 |
Довольно просто, правда?
Я просто хотел разработать простой скрипт scala для подсчета переведенных файлов, а также его размера, чтобы узнать, сколько работы у нас впереди.
Scala как язык сценариев
Использовать scala в качестве языка сценариев довольно просто. Просто введите некоторый код scala в текстовый файл и выполните его с помощью « scala file.scala «. Вы также можете попробовать его с помощью интерактивного интерпретатора, лучше известного как REPL (ну, на самом деле это не интерпретатор, а цикл чтения-оценки-печати, отсюда и название REPL).
В linux вы также можете извинить их непосредственно из оболочки, отметив файл scala как исполняемый и добавив эти строки в начало файла.
1
2
3
|
#!/bin/sh exec scala "$0" "$@" !# |
Совет: вы можете ускорить выполнение LOT- скрипта, добавив — savecompiled, как сказано на странице руководства команды scala , например:
1
2
3
4
5
|
#!/bin/sh 2 exec scala -savecompiled "$0" "$@" 3 !# |
Классы и вывод типов в scala
Поэтому я создал DocumentationFile с именем, длиной и свойством isTranslated.
1
2
3
4
5
6
7
8
9
|
class DocumentationFile(val file: File) { val name = file.getName val length = file.length val isTranslated = (firstLine.indexOf( "Esta página todavía no ha sido traducida al castellano" ) == - 1 ) def firstLine = new BufferedReader( new FileReader(file)).readLine } |
Scala забирает много стандартного кода. Конструктор прямо здесь, вместе с объявлением класса. В нашем случае конструктор DocumentationFile принимает в качестве аргумента файл java.io.File.
Scala также активно использует вывод типов, чтобы избавить нас от необходимости объявлять тип каждой переменной. Вот почему вам не нужно указывать, что имя — это String, длина Long и isTranslated — логическое значение. Вы все еще должны объявлять типы в аргументах метода, но обычно вы можете опустить их везде.
Работа с коллекциями
Затем мне нужно было получить все текстильные файлы из текущего каталога, создать экземпляр DocumentationFile для каждого из них и сохранить их в массиве для последующей обработки.
1
2
3
4
5
|
import java.io._ val docs = new File( "." ).listFiles .filter(_.getName.endsWith( ".textile" )) // process only textile files .map( new DocumentationFile(_)) |
Технически говоря, это всего лишь одна строка кода. «_» — это просто синтаксический сахар, мы могли бы написать его более многословно:
1
2
3
|
val docs = new File( "." ).listFiles .filter( file => file.getName.endsWith( ".textile" ) ) // process only textile files .map( file => new DocumentationFile(file) ) |
Или если вы в фигурных скобках весело
1
2
3
4
5
6
7
|
val docs = new File( "." ).listFiles .filter { file => file.getName.endsWith( ".textile" ) // process only textile files } .map { file => new DocumentationFile(file) } |
Функции высшего порядка
Как только у нас будут все текстильные файлы, нам понадобятся переведенные.
1
|
val translated = docs.filter(_.isTranslated) |
Здесь мы передаем методу фильтра функцию в качестве параметра (это то, что называется функцией более высокого порядка). Эта функция оценивается для каждого элемента в массиве, и если она возвращает значение true, этот элемент добавляется в результирующий массив. Материал «_.isTranslated» опять-таки просто синтаксический сахар. Мы могли бы также написать функцию следующим образом:
1
|
val translated = docs.filter( (doc: DocumentationFile) => doc.isTranslated ) |
Функциональные и императивные: варьировать или не менять
Теперь мне нужно рассчитать количество и размер переведенных и еще не переведенных файлов. Подсчет файлов довольно прост, нужно просто использовать «translation.length», чтобы узнать, сколько файлов уже переведено. Но для подсчета их размера я должен суммировать размер каждого из них.
Это была моя первая попытка:
1
2
|
var translatedLength = 0L translated.foreach( translatedLength += _.length ) |
В scala мы можем объявлять переменные с ключевыми словами « var » и « val », первые изменчивы, а последние — неизменяемы. Изменяемые переменные предназначены для чтения и записи, в то время как неизменяемые переменные не могут быть переназначены после того, как их значение установлено (представьте их как окончательные переменные в Java).
Хотя scala позволяет вам работать в императивном или функциональном стиле, он действительно поощряет более поздний. Программирование на языке scala , своего рода библиотека Библии, даже учит, как реорганизовать ваш код, чтобы избежать использования изменяемых переменных, и привыкнуть к более функциональному стилю программирования.
Я нашел несколько способов рассчитать его в более функциональном стиле (благодаря переполнению стека !)
01
02
03
04
05
06
07
08
09
10
|
val translatedLength: Long = translated.fold(0L)( (acum: Long, element: DocumentFile) => acum + element.length ) //type inference to the rescue val translatedLength = translated.foldLeft(0L)( (acum, element) => acum + element.length ) //syntactic sugar val translatedLength = translated.foldLeft(0L)( _ + _.length ) // yes, if statement is also an expression, just like the a ? b : c java operator. val translatedLength = if (translated.length == 0 ) 0 else translated.map(_.length).sum |
Я наконец решил эту простую и короткую форму:
1
2
|
val translatedLength = translated.map(_.length).sum val docsLength = docs.map(_.length).sum |
Параметры по умолчанию и передача функций в качестве аргументов
Теперь у меня есть вся необходимая информация, поэтому мне просто нужно показать ее на экране. Я также хотел показать размер файла в килобайтах.
Еще раз это была моя первая попытка:
01
02
03
04
05
06
07
08
09
10
11
|
println( "translated size: " + asKB(translatedLength) + "/" + asKB(docsLength) + " " + translatedLength * 100 / docsLength + "% " ) println( "translated files: " + translated.length + "/" + docs.length + " " + translated.length * 100 / docs.length + "% " ) def asKB(length: Long) = (length / 1000 ) + "kb" |
И это был выход:
1
2
3
|
translated size: 256kb/612kb 41 % translated files: 24 / 64 37 % |
Ну, это сработало, но это определенно можно улучшить, слишком много дублирования кода.
Поэтому я создал функцию, которая позаботилась обо всем этом:
01
02
03
04
05
06
07
08
09
10
11
12
|
def status( title: String = "status" , current: Long, total: Long, format: (Long) => String = (x) => x.toString): String = { val percent = current * 100 / total title + ": " + format(current) + "/" + format(total) + " " + percent + "%" + " (pending " + format(total - current) + " " + ( 100 -percent) + "%)" } |
Единственная сложная часть — это параметр формата. Это просто функция более высокого порядка, которая по умолчанию просто преобразует переданное число в строку.
Мы используем эту функцию следующим образом:
1
2
3
4
5
6
7
|
println( status( "translated size" , translatedLength, docsLength, (length) => asKB(length) ) ) println( status( "translated files" , translated.length, docs.length) ) |
Вот и все.
Достигнуть такого рода вещей действительно легко, используя scala в качестве языка сценариев, и по пути вы можете выучить пару интересных концепций и сделать первые шаги в функциональном программировании.
Это полный сценарий, здесь у вас есть GitHub Gist, и вы также можете найти его в проекте документации Play Испанский .
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
#!/bin/sh exec scala "$0" "$@" !# import java.io._ val docs = new File( "." ).listFiles .filter(_.getName.endsWith( ".textile" )) // process only textile files .map( new DocumentationFile(_)) val translated = docs.filter(_.isTranslated) // only already translated files val translatedLength = translated.map(_.length).sum val docsLength = docs.map(_.length).sum println( status( "translated size" , translatedLength, docsLength, (length) => asKB(length) ) ) println( status( "translated files" , translated.length, docs.length) ) def status( title: String = "status" , current: Long, total: Long, format: (Long) => String = (x) => x.toString): String = { val percent = current * 100 / total title + ": " + format(current) + "/" + format(total) + " " + percent + "%" + " (pending " + format(total - current) + " " + ( 100 -percent) + "%)" } def asKB(length: Long) = (length / 1000 ) + "kb" class DocumentationFile(val file: File) { val name = file.getName val length = file.length val isTranslated = (firstLine.indexOf( "Esta página todavía no ha sido traducida al castellano" ) == - 1 ) override def toString = "name: " + name + ", length: " + length + ", isTranslated: " + isTranslated def firstLine = new BufferedReader( new FileReader(file)).readLine } |
Ссылка: Первые шаги со Scala, попрощайтесь с bash-сценариями… от нашего партнера по JCG Себастьяна Скарано на платформе « Развлекайся с Play»! блог
Статьи по Теме :
- Scala Tutorial — блоки кода, стиль кодирования, замыкания, проект документации scala
- Scala Tutorial — SBT, scalabha, пакеты, системы сборки
- Веселье с функцией композиции в Scala
- Использование Scala менее эффективно, чем использование Java, по крайней мере, для половины всех проектов Java.
- Да, Вирджиния, Скала тяжело