Предисловие
Это второе из запланированной серии учебных пособий по программированию в 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) = twoInts first : Int = 3 second : Int = 9 scala> val (numTimes, thingToSay, price) = mixedUp numTimes : Int = 1 thingToSay : java.lang.String = hello price : 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 = 18 right : Int = 24 |
Другой способ получить доступ к значениям в кортеже — через индексацию, используя « _n », где n — это индекс элемента, который вы хотите.
1
2
3
4
5
6
|
scala> print(mixedUp. _ 1 ) 1 scala> print(mixedUp. _ 2 ) hello scala> 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.length res 19 : Int = 3 scala> odds.length res 20 : Int = 5 scala> embedded.length res 21 : Int = 4 |
Обратите внимание, что длина внедренного составляет 4, что является количеством списков, которые он содержит (не количество элементов в этих списках).
Обозначение variable.method указывает, что вы вызываете функцию, специфичную для типа этой переменной для значения в этой переменной. Хорошо, это был полный рот. Scala является объектно-ориентированным языком, что означает, что каждое значение имеет набор действий, которые идут с ним. Какие действия доступны, зависит от его типа. Итак, выше, мы вызывали метод длины, который доступен спискам для каждого из значений списка, приведенных выше. Вы не поняли этого в предыдущем уроке, но использовали методы при добавлении Ints или объединенных строк — просто Scala позволяет нам обходиться без «.» и паретезы в некоторых случаях. Если мы не уроним их, вот как это выглядит.
1
2
3
4
5
|
scala> 2 .+( 3 ) res 25 : Double = 5.0 scala> "Portis" .+( "head" ) res 26 : java.lang.String = Portishead |
То, что происходит, заключается в том, что у Ints есть метод с именем « + », а у Strings есть другой метод с именем « + ». Их можно было бы назвать «Билл» и «Боб», но это будет сложнее запомнить, среди прочего. У Ints есть другие методы, такие как « — », « * » и « / », которых нет в строках. (Примечание: сейчас я возвращаюсь к пропущенному «.» И паретезам.)
1
2
3
4
5
6
|
scala> 5 - 3 res 27 : 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 Int 5 .length ^ scala> "walked" .length res 31 : 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 ++ evens nums : List[Int] = List( 1 , 3 , 5 , 7 , 9 , 2 , 4 , 6 , 8 ) |
Мы можем добавить один элемент в начало списка с помощью « :: ».
1
2
|
scala> val zeroToNine = 0 :: nums zeroToNine : List[Int] = List( 0 , 1 , 3 , 5 , 7 , 9 , 2 , 4 , 6 , 8 ) |
И отсортируйте список с отсортированным , и переверните его с обратным , и сделайте оба в последовательности.
1
2
3
4
5
6
7
8
|
scala> zeroToNine.sorted res 42 : List[Int] = List( 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ) scala> zeroToNine.reverse res 43 : List[Int] = List( 8 , 6 , 4 , 2 , 9 , 7 , 5 , 3 , 1 , 0 ) scala> zeroToNine.sorted.reverse res 44 : 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 val fixed = 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> odds res 48 : List[Int] = List( 1 , 3 , 5 , 7 , 9 ) scala> odds( 0 ) res 49 : Int = 1 scala> odds( 1 ) res 50 : Int = 3 |
Начиная с 0 для индекса первого элемента является стандартной практикой в области компьютерных наук. Поначалу это может показаться странным, но вы привыкнете к этому довольно быстро.
Конечно, мы можем использовать любое выражение Int для доступа к элементу в списке.
01
02
03
04
05
06
07
08
09
10
11
|
scala> zeroToNine( 3 ) res 63 : Int = 5 scala> zeroToNine( 5 - 2 ) res 64 : Int = 5 scala> val index = 3 index : Int = 3 scala> zeroToNine(index) res 65 : 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 : 10 at 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.invoke 0 (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> zeroToNine res 55 : List[Int] = List( 0 , 1 , 3 , 5 , 7 , 9 , 2 , 4 , 6 , 8 ) scala> zeroToNine.slice( 2 , 6 ) res 56 : 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 ) res 0 : Char = S scala> artist.slice( 3 , 6 ) res 1 : String = Sha scala> artist.reverse res 2 : String = wodahS JD scala> artist.sorted res 3 : String = " DJSadhow" |
В списках, которые содержат числа, мы можем использовать метод суммы .
1
2
3
4
5
|
scala> odds.sum res 59 : Int = 25 scala> multinomial.sum res 60 : 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" ) res 22 : String = apples milk butter scala> odds.mkString( "," ) res 23 : String = 1 , 3 , 5 , 7 , 9 |
Хотите знать, если список содержит определенный элемент? Использование содержится в списке.
1
2
3
4
5
|
scala> groceryList.contains( "milk" ) res 4 : Boolean = true scala> groceryList.contains( "coffee" ) res 5 : 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 +) res 6 : List[Int] = List( 2 , 4 , 6 , 8 , 10 ) scala> odds.filter( 4 <) res 7 : List[Int] = List( 5 , 7 , 9 ) scala> odds.foldLeft( 10 )( _ + _ ) res 8 : Int = 35 scala> odds.filter( 6 >).map( _ .toString).reduce( _ + "," + _ ) res 9 : 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
- Вещи, которые должен знать каждый программист