Я новичок в 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





В 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
потому что newCars в этом случае является списком. Что делает оба эти эквивалента. В вашем примере есть только 1 экземпляр Foo.
Ну да ... Операторная ассоциативность не имеет отношения к ассоциативным операциям вообще, в этом весь смысл. Но по (x :: (x :: x)) ::: (x :: x) совершенно невозможно сделать вывод, какая ассоциативность у операторов :: и :::. Это просто какие-то, казалось бы, случайные скобки, которые нужно компилировать только с помощью случается. В тех же скобках (1 :: (2 :: 3)) :: (4 :: Nil) вообще не компилируется.
Это не случайно ... Просто в этом случае ваш 3 должен быть List(3). Поскольку :: определен на List, а не на Int
«3» ничего не «нужно», ассоциативность операторов в Scala не зависит от операндов. Каждый оператор, заканчивающийся на :, является правоассоциативным, будь то (1 :: (2 :: (3 :: (4 :: Nil)))) или (1 :: (2 :: (List(3) ::: (List(4) :: Nil)))) или что-то еще. Просто scala -print вашей собственной программы, вы увидите, что это вообще-то нет то, что вы там написали.
Я говорил о части (1 :: (2 :: 3)) в (1 :: (2 :: 3)) :: (4 :: Nil) ... это не будет компилироваться, потому что :: не определен для 3, который является Int
используйте
val allCars = "Toyota" :: "Innova" :: newCars ::: newCars ::: newCarsвместоval allCars = "Toyota" :: "Innova" :: newCars ::: newCars :: newCars. обратите внимание, что, наконец, вы используете :: вместо ::: для добавления элементов в список. Использование :: добавит весь список в ваш новый список. Если вы просто хотите добавить элементы в список, используйте ::: вместо ::