Scala в примерах: различия между версиями

Содержимое удалено Содержимое добавлено
Дополнен перевод Главы 5
Строка 1:
''Здесь ведётсяведется перевод книги Martin'а Odersky [http://www.scala-lang.org/docu/files/ScalaByExample.pdf Scala by examples], которая описывает основные приемы программирования на языке [[w:Scala (язык программирования)|Scala<small><sup>↑</sup></small>]]. Присоединяйтесь!''
 
= Введение =
Строка 12:
Эта книга представляет Scala неформально, на примерах.
 
Главы [[Scala в примерах#Первый пример|2]] и [[Scala в примерах#Программирование с акторами и сообщениями|3]] обращают внимание на некоторые возможности, из-за которых Scala особенно интересен. Последующие главы представляют конструкты языка более подробно, начиная с простых выражений и функций, через объекты и классы, списки и потоки, изменяемые состояния, сопоставление с образцом, к более сложным примерам, иллюстрирующим интересные приемы программирования. Это неформальное изложение должно быть дополнено документом [http://www.scala-lang.org/docu/files/ScalaReference.pdf Scala Language Reference Manual], который специфицирует Scala более точно и детально.
 
 
Строка 80:
Обе реализации, и императивная, и функциональная, имеют одинаковую асимптотическую сложность — ''O(N log (N))'' в среднем и ''O(N²)'' в худшем случае. Но если императивная версия непосредственно оперирует элементами массива, используя прямую адресацию, то функциональная при каждом рекурсивном вызове возвращает новый отсортированный массив, оставляя без изменения массив, переданный как аргумент. Следовательно, функциональная реализация требует больше памяти для выполнения.
 
Функциональная реализация создаётсоздает впечатление, что Scala это специализированный язык для функциональных операций на массивах. На самом деле все операции, использовавшиеся в примере, это просто библиотечные методы класса ''последовательности'' <tt>Seq[t]</tt> из стандартной библиотеки Scala, которая сама реализована на Scala. Поскольку массивы — это экземпляры класса <tt>Seq</tt>, все его методы доступны им.
 
В частности, метод <tt>filter</tt>, который принимает в качестве аргумента функцию — ''предикатная функция''. Предикатная функция должна переводить элементы массива в булевские значения. Результат выполнения <tt>filter</tt> — массив, состоящий из тех элементов исходного массива, которые удовлетворяют предикату, то есть на которых предикатная функция возвращает true. Метод <tt>filter</tt> класса <tt>Array[t]</tt>, следовательно, имеет сигнатуру
Строка 122:
</font>
 
Результат этой функции — просто еёее последнее выражение, при этом ключевое слово <tt>'''return'''</tt> указывать не обязательно. Обратите внимание, что для функций, возвращающих явное значение, всегда необходимо cтавить '=' перед их телом или определяющим выражением.
 
= Программирование с акторами и сообщениями =
Строка 158:
* даты завершения аукциона.
 
Ход торгов определёнопределен методом <tt>act</tt>. Этот метод периодически отбирает (используя метод [http://www.daimi.au.dk/~eernst/dProgSprog06/share/doc/scala-1.4.0.4/api/scala/concurrent/Actor-class.html#1 <tt>receiveWithin</tt>]) поступающие сообщения и реагирует на них, пока аукцион не будет закрыт, что сигнализируется сообщением <tt>TIMEOUT</tt>. Перед завершением актор остается активным в течение периода времени, определенным константой <tt>timeToShutdown</tt>, и отвечает на дальнейшие предложения, что аукцион закрыт.
 
<font size=3><syntaxhighlight lang=Scala>
Строка 207:
 
Предыдущее обсуждение показало возможности распределенного программирования на Scala. Может показаться, что в Scala есть богатый набор языковых конструкций, поддерживающих процессы-акторы, обмен сообщениями, программирование с тайм-аутами, и т.п. В действительности это не так. Все конструкции, рассмотренные выше, вводятся как методы в библиотечном классе <tt>Actor</tt>.
Сам класс написан на Scala и базируется на модели многопоточности, используемой в нижележащей платформе (напр. Java или .NET). Все возможности класса <tt>Actor</tt>, использованные здесь, рассмотрены в [[Scala в примерах#Акторы|РазделеПараграфе 17.11]].
 
Преимущество подобного библиотечного подхода к многопоточности ''(см. также [http://lamp.epfl.ch/~cremet/publications/pilib.pdf PiLib]) '' — в относительной простоте основного языка и гибкости для разработчиков библиотек. Поскольку основному языку не нужно определять детали высокоуровневого обмена информацией между процессами, он может оставаться простым и более общим. Поскольку конкретная модель сообщений в почтовом ящике — библиотечный модуль, она может свободно быть модифицирована, если это требуется в каких-то приложениях. Впрочем, для такого подхода необходимо, чтобы базовый язык был достаточно выразителен, чтобы он мог обеспечивать необходимые языковые абстракции удобным способом. Scala разработан с учётомучетом этого требования; одна из основных целей при проектировании заключалась в том, чтобы сделать его достаточно гибким, чтобы он мог выступать удобной платформой для доменно-специфичных языков, реализованных как библиотечные модули. Например, представленные выше конструкции для коммуникации между акторами могут рассматриваться как доменно-специфичный язык, который, в принципе, расширяет ядро Scala.
 
= Выражения и простые функции =
Строка 628:
Параметризация по функциям располагает к созданию множества маленьких функций. В предыдущем примере мы определили <tt>id</tt>, <tt>square</tt> и <tt>power</tt> как отдельные функции, так что их можно было передавать в качестве аргументов в <tt>sum</tt>.
 
Вместо того, чтобы определять поименованные функции для этих небольших функций-аргументов, можно сформулировать их короче как ''анонимные функции''. Анонимная функция это выражение, вычисляющееся в функцию,; функция, определенная без имени. Для примера рассмотрим анонимную функцию возведения в квадрат:
 
<font size=3><syntaxhighlight lang=Scala>
Строка 656:
Выражение Scala <tt>(x<sub>1</sub>: T<sub>1</sub>, …, x<sub>n</sub>: T<sub>n</sub>) => E</tt> определяет функцию, которая соотносит свои параметры <tt>x<sub>1</sub>, …, x<sub>n</sub></tt> с результатами выражения <tt>E</tt> (где <tt>E</tt> может ссылаться на <tt>x<sub>1</sub>, …, x<sub>n</sub></tt>). Анонимные функции — не основные элементы языка Scala, поскольку они всегда могут быть выражены через именованные функции. Действительно, анонимная функция
 
<codepre>
(x<sub>1</sub>: T<sub>1</sub>, …, x<sub>n</sub>: T<sub>n</sub>) => E
</codepre>
 
эквивалентна блоку
 
<codepre>
{ def f (x<sub>1</sub>: T<sub>1</sub>, …, x<sub>n</sub>: T<sub>n</sub>) = E ; f _ }
</codepre>
 
где <tt>f</tt> — свежее имя, которое больше не используется нигде в программе. Можно сказать, что анонимные функции это "синтаксический сахар".
Строка 850:
</syntaxhighlight></font>
 
На самом деле, расширение функции <tt>fixedPoint</tt> дает в точности определение неподвижной точки из [[Scala в примерах#Пример: квадратные корни по методу Ньютона|Параграфа 4.4]].
 
Предыдущий пример показал, что выразительная мощность языка значительно увеличивается, если функции можно передавать как аргументы. Следующий пример показывает, что функции, возвращающие функции, тоже могут быть полезны.
Строка 871:
 
== Заключение ==
 
В предыдущей главе мы видели, что функции — это очень важные абстракции, потому что они позволяют нам вводить общие методы вычислений как явные, именованные элементы нашего языка программирования. Настоящая глава показала, что эти абстракции могут быть скомбинированы при помощи функций высшего порядка, для создания дальнейших абстракций. Как программисты, мы должны искать возможности абстрагировать и переиспользовать. Самый высокий возможный уровень асбтракции не всегда самый лучший, но важно знать техники абстракции, чтобы уметь использовать их, где это уместно.
 
== Использованные элементы языка ==
 
Главы [[Scala в примерах#Выражения и простые функции|4]] и [[Scala в примерах#Функции первого класса|5]] покрывают элементы языка Scala для выражения выражений и типов, включая примитивные типы и функции. Ниже дан контекстно-свободный синтаксис этих языковых элементов в расширенной форме Бэкуса-Наура, где '<tt>|</tt>' обозначает альтернативы, <tt>[...]</tt> обозначает опцию (вхождение 1 или 0 раз), и <tt>{...}</tt> обозначает повтороение (0 или больше вхождений).
 
=== Символы ===
 
Программы Scala — это последовательности символов Unicode. Мы различаем следующие множества символов:
* пробел, табуляция или символ новой строки,
* буквы от '<tt>a</tt>' до '<tt>z</tt>', '<tt>A</tt>' до '<tt>Z</tt>',
* цифры от '<tt>0</tt>' до '<tt>9</tt>',
* разделители: <tt>. , ; ( ) { } [ ] \ " '</tt>,
* символы операторов, такие как '<tt>#</tt>', '<tt>+</tt>', '<tt>:</tt>'. По сути, это символы, котрые не перечислены в множествах выше.
 
=== Лексемы ===
 
<pre>
ident = letter {letter | digit}
| operator { operator }
| ident '_' ident
literal = "as in Java"
</pre>
 
Литералы — те же, что и в Java. Они определяют числа, символы, строки и булевские величины. Примеры литералов: <tt>0</tt>, <tt>1.0e10</tt>, <tt>'x'</tt>, <tt>"he said "hi!""</tt> или <tt>'''true'''</tt>.
 
= Классы и объекты =
Строка 1332 ⟶ 1357 :
= Вычисления с потоками =
 
В предыдущих главах рассматривались переменные, присваивание и объекты, сохраняющие состояние. Мы видели, как объекты реального мира, которые изменяются со временем, могут быть смоделированы изменением состояния переменных в вычислении. Таким образом временные изменения в реальном мире моделируются временными изменениями в выполнении программы. Конечно, такие временные изменения обычно сокращены или растянуты, но их относительный порядок остаётсяостается неизменным. Такой подход представляется вполне естественным, но за него приходится платить: наша простая и выразительная модель
подстановок для функционального вычисления будет более не применима, если мы введёмвведем переменные
и присваивание.
 
Строка 1340 ⟶ 1365 :
 
<font size=3><syntaxhighlight lang=Scala>
def sumPrimes(start: intInt, end: intInt): intInt = {
var i = start
var acc = 0
Строка 1356 ⟶ 1381 :
 
<font size=3><syntaxhighlight lang=Scala>
def sumPrimes(start: intInt, end: intInt) =
sum(range(start, end) filter isPrime)
</syntaxhighlight></font>
 
Бесспорно, вторая программа короче и яснее! Однако функциональная программа также и менее эффективна, поскольку она конструирует список список чисел из интервала а затем ещёеще один для простых чисел.
ЕщёЕще хуже с точки зрения эффективности следующий пример.
 
Требуется найти второе простое число между 1000 и 10000:
Строка 1371 ⟶ 1396 :
Здесь конструируется список всех чисел между 1000 and 10000. Но большая часть этого списка не будет использована! Но мы можем достичь эффективного выполнения для аналогичных случаев с помощью уловки:''избегайте вычисления хвоста последовательности до тех пор, пока он действительно не понадобится.''
 
Определим новый класс <tt>Stream</tt> (поток) для таких последовательностей. Потоки создаются с помощью константы <tt>empty</tt> и конструктора <tt>cons</tt>, которые определены в модуле <tt>scala.Stream</tt>. Например, следующее выражение создаётсоздает поток с элементами <tt>1</tt> и <tt>2</tt>:
 
<font size=3><syntaxhighlight lang=Scala>
Строка 1380 ⟶ 1405 :
 
<font size=3><syntaxhighlight lang=Scala>
def range(start: intInt, end: intInt): Stream[intInt] =
if (start >= end) Stream.empty
else Stream.cons(start, range(start + 1, end))
Строка 1586 ⟶ 1611 :
def synchronized[a] (exec: => a): a
def wait(): unit
def wait(msec: longLong): unit
def notify(): unit
def notifyAll(): unit
Строка 1941 ⟶ 1966 :
def send(msg: Any): unit
def receive[A](f: PartialFunction[Any, A]): A
def receiveWithin[A](msec: longLong)(f: PartialFunction[Any, A]): A
}
</syntaxhighlight></font>
Строка 2035 ⟶ 2060 :
 
<font size=3><syntaxhighlight lang=Scala>
def receiveWithin[A](msec: longLong)(f: PartialFunction[Any, A]): A = {
val msg: Any = synchronized {
var s = sent, s1 = s.next
Строка 2075 ⟶ 2100 :
}
</syntaxhighlight></font>
 
[[Категория:Языки программирования]]
[[Категория:Функциональное программирование]]
[[Категория:Объектно-ориентированное программирование]]