Предисловие
Это второе из запланированной серии учебных пособий по программированию в Scala для начинающих программистов, со специальной ссылкой на мой курс «Осень 2011 года по вычислительной лингвистике». Вы можете увидеть другие учебники здесь в этом блоге; они также перечислены на странице ссылок курса .
Этот урок посвящен кортежам и спискам, которые являются двумя конструкциями для работы с группами элементов. Вы не добьетесь большого успеха без последнего, и первые настолько невероятно полезны, что вы, вероятно, будете часто их использовать.
Кортеж
В предыдущем уроке мы видели, как одно значение может быть присвоено переменной, а затем использовано в различных контекстах. Кортеж является обобщением этого: коллекция из двух, трех, четырех и более значений. Каждое значение может иметь свой собственный тип.
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 | scala> valtwoInts =(3,9)twoInts:(Int, Int) =(3,9) scala> valtwoStrings =("hello", "world")twoStrings:(java.lang.String, java.lang.String) =(hello,world) scala> valthreeDoubles =(3.14, 11.29, 1.5)threeDoubles:(Double, Double, Double) =(3.14,11.29,1.5) scala> valintAndString =(7, "lucky number")intAndString:(Int, java.lang.String) =(7,lucky number) scala> valmixedUp =(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> defrangeAround(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> valgroceryList =List("apples", "milk", "butter")groceryList:List[java.lang.String] =List(apples, milk, butter) scala> valodds =List(1,3,5,7,9)odds:List[Int] =List(1, 3, 5, 7, 9) scala> valmultinomial =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> valintsAndDoubles =List(1, 1.5, 2, 2.5)intsAndDoubles:List[Double] =List(1.0, 1.5, 2.0, 2.5) scala> valtoday =List("August", 23, 2011)today:List[Any] =List(August, 23, 2011) | 
Типы иногда автоматически конвертируются, например, конвертирование Ints в Double для intsAndDoubles , но часто нет очевидного обобщаемого типа. Например, сегодня это List [Any] , что означает, что это List of Anys — и Any — это самый общий тип в Scala, супертип всех типов. Это как сказать: «Да, у меня есть список… ну, вы знаете… вещи».
Списки могут также содержать списки (и списки списков, и списки списков списков …).
| 1 2 | scala> valembedded =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> valevens =List(2,4,6,8)evens:List[Int] =List(2, 4, 6, 8) scala> valnums =odds ++ evensnums:List[Int] =List(1, 3, 5, 7, 9, 2, 4, 6, 8) | 
Мы можем добавить один элемент в начало списка с помощью « :: ».
| 1 2 | scala> valzeroToNine =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> valfixed =List(1,2)fixed:List[Int] =List(1, 2) scala> fixed =List(3,4)<console>:8:error:reassignment to valfixed =List(3,4)^ scala> varreassignable =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> valindex =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> valartist ="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 implicitvalue forparameter 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> valodds =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
- Вещи, которые должен знать каждый программист