Предисловие
Это второе из запланированной серии учебных пособий по программированию в Scala для начинающих программистов, со специальной ссылкой на мой курс «Осень 2011 года по вычислительной лингвистике». Вы можете увидеть другие учебники здесь в этом блоге; они также перечислены на странице ссылок курса .
Этот урок посвящен кортежам и спискам, которые являются двумя конструкциями для работы с группами элементов. Вы не добьетесь большого успеха без последнего, и первые настолько невероятно полезны, что вы, вероятно, будете часто их использовать.
Кортеж
В предыдущем уроке мы видели, как одно значение может быть присвоено переменной, а затем использовано в различных контекстах. Кортеж является обобщением этого: коллекция из двух, трех, четырех и более значений. Каждое значение может иметь свой собственный тип.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
scala> val twoInts = (3,9)twoInts: (Int, Int) = (3,9) scala> val twoStrings = ("hello", "world")twoStrings: (java.lang.String, java.lang.String) = (hello,world) scala> val threeDoubles = (3.14, 11.29, 1.5)threeDoubles: (Double, Double, Double) = (3.14,11.29,1.5) scala> val intAndString = (7, "lucky number")intAndString: (Int, java.lang.String) = (7,lucky number) scala> val mixedUp = (1, "hello", 1.16)mixedUp: (Int, java.lang.String, Double) = (1,hello,1.16) |
Элементы кортежа могут быть восстановлены несколькими различными способами. Одним из способов является использование кортежа при инициализации некоторых переменных, каждая из которых принимает значение соответствующей позиции в кортеже справа от знака равенства.
|
1
2
3
4
5
6
7
8
|
scala> val (first, second) = twoIntsfirst: Int = 3second: Int = 9 scala> val (numTimes, thingToSay, price) = mixedUpnumTimes: Int = 1thingToSay: java.lang.String = helloprice: Double = 1.16 |
Scala снимает значения и присваивает их каждой отдельной переменной. Это становится очень полезным в контексте функций, которые возвращают кортежи. Например, рассмотрим функцию, которая предоставляет левый и правый края диапазона, когда вы задаете ему среднюю точку диапазона и размер интервала с каждой стороны от средней точки.
|
1
2
|
scala> def rangeAround(midpoint: Int, size: Int) = (midpoint - size, midpoint + size)rangeAround: (midpoint: Int, size: Int)(Int, Int) |
Так как rangeAround возвращает Tuple (в частности, пару), мы можем вызвать его и установить переменные для левой и правой сторон непосредственно из вызова функции.
|
1
2
3
|
scala> val (left, right) = rangeAround(21, 3)left: Int = 18right: Int = 24 |
Другой способ получить доступ к значениям в кортеже — через индексацию, используя « _n », где n — это индекс элемента, который вы хотите.
|
1
2
3
4
5
6
|
scala> print(mixedUp._1)1scala> print(mixedUp._2)helloscala> print(mixedUp._3)1.16 |
Синтаксис этого немного странный, но вы к этому привыкнете.
Кортежи — удивительно полезная функция в языке программирования. По мере продвижения вы увидите несколько примеров их полезности.
Списки
Списки — это коллекции заказанных товаров, которые будут знакомы всем, кто совершал покупки. Очевидно, что кортежи связаны со списками, но они менее универсальны в том смысле, что их необходимо создавать в одном выражении, они имеют ограниченную длину (около 20 или около того) и не поддерживают операции, выполняющие вычисления над всеми их элементами. ,
В Scala мы можем создавать списки Strings, Ints и Doubles (и многое другое).
|
1
2
3
4
5
6
7
8
|
scala> val groceryList = List("apples", "milk", "butter")groceryList: List[java.lang.String] = List(apples, milk, butter) scala> val odds = List(1,3,5,7,9)odds: List[Int] = List(1, 3, 5, 7, 9) scala> val multinomial = List(.2, .4, .15, .25)multinomial: List[Double] = List(0.2, 0.4, 0.15, 0.25) |
Мы видим, что Scala отвечает, что список был создан вместе со скобками вокруг типа элементов, которые он содержит. Итак, List [Int] читается как «Список целых» и так далее. Это означает, что List является параметризованной структурой данных: это контейнер, который содержит элементы определенных типов. Мы увидим, как знание этого позволяет нам делать разные вещи со списками, параметризованными разными типами.
Мы также можем создавать списки со смесью типов.
|
1
2
3
4
5
|
scala> val intsAndDoubles = List(1, 1.5, 2, 2.5)intsAndDoubles: List[Double] = List(1.0, 1.5, 2.0, 2.5) scala> val today = List("August", 23, 2011)today: List[Any] = List(August, 23, 2011) |
Типы иногда автоматически конвертируются, например, конвертирование Ints в Double для intsAndDoubles , но часто нет очевидного обобщаемого типа. Например, сегодня это List [Any] , что означает, что это List of Anys — и Any — это самый общий тип в Scala, супертип всех типов. Это как сказать: «Да, у меня есть список… ну, вы знаете… вещи».
Списки могут также содержать списки (и списки списков, и списки списков списков …).
|
1
2
|
scala> val embedded = List(List(1,2,3), List(10,30,50), List(200,400), List(1000))embedded: List[List[Int]] = List(List(1, 2, 3), List(10, 30, 50), List(200, 400), List(1000)) |
Тип внедренного списка — List [List [Int]] , который можно прочитать как «Список списков целых».
Список методов
Хорошо, теперь, когда у нас есть несколько списков, что мы можем с ними сделать? На самом деле, много. Одним из основных свойств списка является его длина, которую вы можете получить, используя « .length » после переменной, которая ссылается на список.
|
1
2
3
4
5
6
7
8
|
scala> groceryList.lengthres19: Int = 3 scala> odds.lengthres20: Int = 5 scala> embedded.lengthres21: Int = 4 |
Обратите внимание, что длина внедренного составляет 4, что является количеством списков, которые он содержит (не количество элементов в этих списках).
Обозначение variable.method указывает, что вы вызываете функцию, специфичную для типа этой переменной для значения в этой переменной. Хорошо, это был полный рот. Scala является объектно-ориентированным языком, что означает, что каждое значение имеет набор действий, которые идут с ним. Какие действия доступны, зависит от его типа. Итак, выше, мы вызывали метод длины, который доступен спискам для каждого из значений списка, приведенных выше. Вы не поняли этого в предыдущем уроке, но использовали методы при добавлении Ints или объединенных строк — просто Scala позволяет нам обходиться без «.» и паретезы в некоторых случаях. Если мы не уроним их, вот как это выглядит.
|
1
2
3
4
5
|
scala> 2.+(3)res25: Double = 5.0 scala> "Portis".+("head")res26: java.lang.String = Portishead |
То, что происходит, заключается в том, что у Ints есть метод с именем « + », а у Strings есть другой метод с именем « + ». Их можно было бы назвать «Билл» и «Боб», но это будет сложнее запомнить, среди прочего. У Ints есть другие методы, такие как « — », « * » и « / », которых нет в строках. (Примечание: сейчас я возвращаюсь к пропущенному «.» И паретезам.)
|
1
2
3
4
5
6
|
scala> 5-3res27: Int = 2 scala> "walked" - "ed"<console>:8: error: value - is not a member of java.lang.String"walked" - "ed" |
Scala жалуется, что мы пытались использовать метод « — » в строке, поскольку в строке нет такого метода. С другой стороны, у Ints нет метода с именем length , в то время как у Strings есть.
|
1
2
3
4
5
6
7
|
scala> 5.length<console>:8: error: value length is not a member of Int5.length^ scala> "walked".lengthres31: Int = 6 |
В случае Strings длина возвращает количество символов, а в списках — количество элементов. Метод длины строки можно было бы назвать «numberOfCharacters», но «длину» легче запомнить, и он позволяет нам обрабатывать строки как другие последовательности и думать о них аналогично.
Вернемся к спискам и что мы можем с ними сделать. «Добавление» двух списков является их объединением и обозначается « ++ ».
|
1
2
3
4
5
|
scala> val evens = List(2,4,6,8)evens: List[Int] = List(2, 4, 6, 8) scala> val nums = odds ++ evensnums: List[Int] = List(1, 3, 5, 7, 9, 2, 4, 6, 8) |
Мы можем добавить один элемент в начало списка с помощью « :: ».
|
1
2
|
scala> val zeroToNine = 0 :: numszeroToNine: List[Int] = List(0, 1, 3, 5, 7, 9, 2, 4, 6, 8) |
И отсортируйте список с отсортированным , и переверните его с обратным , и сделайте оба в последовательности.
|
1
2
3
4
5
6
7
8
|
scala> zeroToNine.sortedres42: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) scala> zeroToNine.reverseres43: List[Int] = List(8, 6, 4, 2, 9, 7, 5, 3, 1, 0) scala> zeroToNine.sorted.reverseres44: List[Int] = List(9, 8, 7, 6, 5, 4, 3, 2, 1, 0) |
Последняя строка говорит: «возьмите zeroToNine , получите из него новый отсортированный список, а затем переверните этот список». Обратите внимание, что вызов этих функций никогда не изменяет сам zeroToNine ! Это потому, что List является неизменным : вы не можете изменить его, поэтому все эти операции возвращают новые списки. Это свойство списков дает много преимуществ, к которым мы вернемся позже.
Примечание: неизменность отличается от различия val / var . Обычно считается, что переменная val является неизменной, но это не так — она является фиксированной и не может быть переназначена. Все следующие примеры включают неизменяемые списки, но фиксированная переменная — это значение val, а переназначаемая переменная — это переменная.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
scala> val fixed = List(1,2)fixed: List[Int] = List(1, 2) scala> fixed = List(3,4)<console>:8: error: reassignment to valfixed = List(3,4)^ scala> var reassignable = List(5,6)reassignable: List[Int] = List(5, 6) scala> reassignable = List(7,8)reassignable: List[Int] = List(7, 8) |
Одна из вещей, которую часто хочется сделать со списком, — это прямой доступ к его элементам. Это делается с помощью индексации в списке, начиная с 0 для первого элемента, 1 для второго элемента и так далее.
|
1
2
3
4
5
6
7
8
|
scala> oddsres48: List[Int] = List(1, 3, 5, 7, 9) scala> odds(0)res49: Int = 1 scala> odds(1)res50: Int = 3 |
Начиная с 0 для индекса первого элемента является стандартной практикой в области компьютерных наук. Поначалу это может показаться странным, но вы привыкнете к этому довольно быстро.
Конечно, мы можем использовать любое выражение Int для доступа к элементу в списке.
|
01
02
03
04
05
06
07
08
09
10
11
|
scala> zeroToNine(3)res63: Int = 5 scala> zeroToNine(5-2)res64: Int = 5 scala> val index = 3index: Int = 3 scala> zeroToNine(index)res65: Int = 5 |
Если мы запрашиваем индекс, равный или превышающий количество элементов в списке, мы получаем ошибку.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
scala> odds(10)java.lang.IndexOutOfBoundsException: 10at scala.collection.LinearSeqOptimized$class.apply(LinearSeqOptimized.scala:51)at scala.collection.immutable.List.apply(List.scala:45)at .<init>(<console>:9)at .<clinit>(<console>)at .<init>(<console>:11)at .<clinit>(<console>)at $export(<console>)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)at java.lang.reflect.Method.invoke(Method.java:597)at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:592)at scala.tools.nsc.interpreter.IMain$Request$$anonfun$10.apply(IMain.scala:828)at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)at scala.tools.nsc.io.package$$anon$2.run(package.scala:31)at java.lang.Thread.run(Thread.java:680) |
Глядя на все это, вы можете подумать «WTF?» Он называется трассировкой стека и дает подробное описание того, где возникли проблемы в части кода. Для начинающих программистов это, вероятно, выглядит ошеломляющим и пугающим — вы можете пока спокойно смотреть на это, но вскоре понадобится возможность использовать трассировку стека для выявления проблем в вашем коде и их устранения.
Еще один полезный метод — это слайс , который дает вам подсписок от одного индекса до другого, но не включая его.
|
1
2
3
4
5
|
scala> zeroToNineres55: List[Int] = List(0, 1, 3, 5, 7, 9, 2, 4, 6, 8) scala> zeroToNine.slice(2,6)res56: List[Int] = List(3, 5, 7, 9) |
Итак, срез дал нам список с элементами от индекса 2 (третий элемент) до индекса 5 (шестой элемент).
Кратко вернемся к Strings — с ними работают и другие методы List, кроме length .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
scala> val artist = "DJ Shadow"artist: java.lang.String = DJ Shadow scala> artist(3)res0: Char = S scala> artist.slice(3,6)res1: String = Sha scala> artist.reverseres2: String = wodahS JD scala> artist.sortedres3: String = " DJSadhow" |
В списках, которые содержат числа, мы можем использовать метод суммы .
|
1
2
3
4
5
|
scala> odds.sumres59: Int = 25 scala> multinomial.sumres60: Double = 1.0 |
Однако, если список содержит нечисловые значения, сумма недействительна.
|
1
2
3
4
|
scala> groceryList.sum<console>:9: error: could not find implicit value for parameter num: Numeric[java.lang.String]groceryList.sum^ |
То, что происходит, — это какое-то очень классное и полезное автоматическое поведение Scala, включающее в себя последствия. Мы вернемся к этому позже, но сейчас вы можете с радостью использовать сумму в списках Ints и Doubles.
Одна вещь, которую мы часто хотим сделать со списками, это получить представление String их содержимого некоторым визуально полезным способом. Например, нам может потребоваться, чтобы список покупок представлял собой строку с одним элементом в строке, или список целых чисел с запятой между каждым элементом. Метод mkString делает именно то, что нам нужно.
|
1
2
3
4
5
6
7
8
|
scala> groceryList.mkString("\n")res22: String =applesmilkbutter scala> odds.mkString(",")res23: String = 1,3,5,7,9 |
Хотите знать, если список содержит определенный элемент? Использование содержится в списке.
|
1
2
3
4
5
|
scala> groceryList.contains("milk")res4: Boolean = true scala> groceryList.contains("coffee")res5: Boolean = false |
И теперь мы приходим к Booleans , еще одному из наиболее важных базовых типов. Они играют главную роль в условном исполнении, о котором мы расскажем в следующем уроке.
На самом деле для списков доступно гораздо больше методов, которые вы можете увидеть, перейдя к записи List в Scala API . API означает интерфейс прикладного программирования — иными словами, набор спецификаций для того, что вы можете делать с различными компонентами языка программирования Scala. Я сделаю все возможное, чтобы дать вам методы, которые вам нужны на данный момент, но в конечном итоге вам нужно будет просмотреть записи API для типов Scala, чтобы увидеть, какие методы доступны, что они делают и как их использовать. ,
Некоторые из наиболее важных методов в списках, которые мы не рассмотрели, это map , filter , foldLeft и Reduce . Мы еще вернемся к ним позже, но сейчас есть тизер, который должен дать вам интуитивное представление о том, что они делают.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
scala> val odds = List(1,3,5,7,9)odds: List[Int] = List(1, 3, 5, 7, 9) scala> odds.map(1+)res6: List[Int] = List(2, 4, 6, 8, 10) scala> odds.filter(4<)res7: List[Int] = List(5, 7, 9) scala> odds.foldLeft(10)(_ + _)res8: Int = 35 scala> odds.filter(6>).map(_.toString).reduce(_ + "," + _)res9: java.lang.String = 1,3,5 |
Теперь мы работаем. ?
Ссылка: Первые шаги в Scala для начинающих программистов, часть 2 от нашего партнера по JCG Джейсона Болдриджа в блоге Bcomposes .
Статьи по Теме :
- Scala Tutorial — Scala REPL, выражения, переменные, основные типы, простые функции, сохранение и запуск программ, комментарии
- Scala Tutorial — условное исполнение с блоками if-else и соответствием
- Scala Tutorial — итерация, для выражений, yield, map, filter, count
- Scala Tutorial — регулярные выражения, сопоставление
- Scala Tutorial — регулярные выражения, сопоставления и замены с помощью API scala.util.matching
- Учебник по Scala — Карты, Наборы, groupBy, Параметры, Flatten, FlatMap
- Scala Tutorial — scala.io.Source, доступ к файлам, flatMap, изменяемые Карты
- Scala Tutorial — объекты, классы, наследование, черты, списки с несколькими связанными типами, применение
- Scala Tutorial — скриптинг, компиляция, основные методы, возвращаемые значения функций
- Scala Tutorial — SBT, scalabha, пакеты, системы сборки
- Scala Tutorial — блоки кода, стиль кодирования, замыкания, проект документации scala
- Веселье с функцией композиции в Scala
- Как Scala изменил мой взгляд на мой Java-код
- Какие функции Java были исключены в Scala?
- Тестирование с помощью Scala
- Вещи, которые должен знать каждый программист