Объединение списков в Scala

Я новичок в Scala и изучаю язык с помощью Программирование на Scala (второе издание). Я наткнулся на эту тему конкатенации списков, я играл с языком и кое-что получил:

val scooters = List("Activa")
val newCars = List("Ritz")
val allCars = "Toyota" :: "Innova" :: newCars ::: newCars :: newCars
allCars.foreach(println)

Выход:

Toyota
Innova
Ritz
List(Ritz)
Ritz

Почему это не выходит как

Toyota
Innova
Ritz
Ritz
Ritz

используйте val allCars = "Toyota" :: "Innova" :: newCars ::: newCars ::: newCars вместо val allCars = "Toyota" :: "Innova" :: newCars ::: newCars :: newCars. обратите внимание, что, наконец, вы используете :: вместо ::: для добавления элементов в список. Использование :: добавит весь список в ваш новый список. Если вы просто хотите добавить элементы в список, используйте ::: вместо ::

vindev 16.03.2018 12:14
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
255
3

Ответы 3

В Scala ассоциативность метода определяется его последним символом. Если он заканчивается двоеточием (:), он правоассоциативен. Это означает, что в 1 :: Nil вы вызываете метод :: для Nil, а не для 1. Вот почему 1 :: 2 не компилируется, а 1 :: 2 :: Nil - компилируется.

  • :: - это метод списка для добавления одного элемента в начало списка. Затем, если у вас есть List[Int] размера 1 и вы попытаетесь добавить другой список (как один элемент), вы получите список размера 2 и наберете List[Any].
  • ::: - это метод списка для добавления нескольких элементов.

Учитывая все это, вы получите следующий код:

val allCars = "Toyota" :: "Innova" :: newCars ::: newCars ::: newCars

Так как

"Toyota" :: "Innova" :: newCars ::: newCars :: newCars

обессахаривает в

(((newCars.::(newCars)).:::(newCars)).::("Innova")).::("Toyota")

Обратите внимание на то, что происходит на самом первом этапе:

newCars.::(newCars)

он создает список с двумя элементами: String "Ritz" и List("Ritz") типа List[String], поэтому вы получаете

List(List("Ritz"), "Ritz")

на первом этапе.

Обратите внимание, что тип всего выражения становится совершенно бессмысленным для этого контекста (List[Serializable], потому что Serializable является наименьшей верхней границей как для String, так и для List[String]), и компилятор может сообщить вам об этом, просто явно объявив тип ожидаемого результата :

val newCars = List("Ritz")
val allCars: List[String] = "Toyota" :: "Innova" :: newCars ::: newCars :: newCars
allCars.foreach(println)

Когда вы объявляете allCars с явным типом, компилятор немедленно сообщит вам, что пошло не так:

error: type mismatch;
 found   : List[java.io.Serializable]
 required: List[String]
    val allCars: List[String] = "Toyota" :: "Innova" :: newCars ::: newCars :: newCars

Таким образом, вам не нужно запускать его и смотреть на результат или покрывать смехотворным количеством тестов: ошибки такого рода могут быть обнаружены во время компиляции. Очевидное исправление, конечно:

val allCars: List[String] = 
  "Toyota" :: "Innova" :: newCars ::: newCars ::: newCars

Что ж ... здесь нужно учитывать ассоциативность операторов.

val newCars = List("Ritz")

Когда вы пишете что-то вроде следующего,

val allCars = "Toyota" :: "Innova" :: newCars ::: newCars :: newCars

На самом деле,

val allCars = ("Toyota" :: ("Innova" :: newCars)) ::: (newCars :: newCars)

Что эквивалентно,

scala> val ll1 = "Innova" :: newCars
// Or
scala> val ll1 = newCars.::("Innova")
// ll1: List[String] = List(Innova, Ritz)

scala> val ll2 = "Toyota" :: ll1
// Or
scala> val ll2 = ll1.::("Toyota")
// ll2: List[String] = List(Toyota, Innova, Ritz)

scala> val rl1 = newCars :: newCars
// Or
scala> val rl1 = newCars.::(newCars)
// rl1: List[java.io.Serializable] = List(List(Ritz), Ritz)

scala> val allCars = ll1 ::: rl1
// Or
scala> val allCars = rl1.:::(ll1)
// allCars: List[java.io.Serializable] = List(Innova, Ritz, List(Ritz), Ritz)

Надеюсь, это проясняет ситуацию.

Почему (x :: (x :: x)) ::: (x :: x), а не (x :: (x :: (x ::: (x :: x))))? Протестируйте на object Foo { def ::(i: Int) = { println(i); this } def :::(i: Int) = { println(i); this } }; 4 :: 3 :: 2 ::: 1 :: Foo

Andrey Tyukin 16.03.2018 12:44

потому что newCars в этом случае является списком. Что делает оба эти эквивалента. В вашем примере есть только 1 экземпляр Foo.

sarveshseri 16.03.2018 13:38

Ну да ... Операторная ассоциативность не имеет отношения к ассоциативным операциям вообще, в этом весь смысл. Но по (x :: (x :: x)) ::: (x :: x) совершенно невозможно сделать вывод, какая ассоциативность у операторов :: и :::. Это просто какие-то, казалось бы, случайные скобки, которые нужно компилировать только с помощью случается. В тех же скобках (1 :: (2 :: 3)) :: (4 :: Nil) вообще не компилируется.

Andrey Tyukin 16.03.2018 13:48

Это не случайно ... Просто в этом случае ваш 3 должен быть List(3). Поскольку :: определен на List, а не на Int

sarveshseri 19.03.2018 09:42

«3» ничего не «нужно», ассоциативность операторов в Scala не зависит от операндов. Каждый оператор, заканчивающийся на :, является правоассоциативным, будь то (1 :: (2 :: (3 :: (4 :: Nil)))) или (1 :: (2 :: (List(3) ::: (List(4) :: Nil)))) или что-то еще. Просто scala -print вашей собственной программы, вы увидите, что это вообще-то нет то, что вы там написали.

Andrey Tyukin 19.03.2018 14:12

Я говорил о части (1 :: (2 :: 3)) в (1 :: (2 :: 3)) :: (4 :: Nil) ... это не будет компилироваться, потому что :: не определен для 3, который является Int

sarveshseri 20.03.2018 06:38

Другие вопросы по теме