Есть минус метод или класс?

Мартинг Одерски в своей книге пишет:

Класс::, выраженный "cons" для "construct", представляет собой непустые списки.

и

Методы построения списка:: и: являются специальными. Потому что они заканчиваются в двоеточие, они связаны с их правым операндом. То есть, операция, такая как x:: xs, рассматривается как вызов метода xs.::(x), а не x.::(xs). Фактически, x.::(xs) не имеет смысла, так как x имеет список тип элемента, который может быть произвольным, поэтому мы не можем предположить, что это type будет иметь метод:: . По этой причине метод:: должен взять значение элемента и получить новый список

Итак, :: метод или класс?

Ответ 1

Это класс и . Минусы - это тип параметризованного класса. Список [A] имеет два подкласса: Cons и Nil. Поскольку Cons является классом, он может быть создан его конструктором следующим образом:

val s = new ::[Int](4, Nil)

Концы - это класс case, и мы используем конструктор, когда мы выполняем сопоставление шаблонов. Минусы также являются методом класса list, который реализуется в двух его подклассах. Следовательно, мы можем использовать метод cons для класса cons, который мы создали выше.

val s1 = s.::(5)

Может возникнуть путаница, потому что мы обычно создаем списки, используя метод apply объекта List:

val s2 = List(1, 2, 3)

Обычно метод apply объекта возвращает новый экземпляр класса с тем же именем, что и объект. Это, однако, просто конвенция. В этом конкретном случае объект List возвращает новый экземпляр подкласса Cons. Сам класс List является закрытым абстрактным классом, поэтому его нельзя создать. Таким образом, применяемый выше метод выполняет следующие действия:

val s2 = 1 :: 2 :: 3 :: Nil

Любой метод, который заканчивается на ":", - это метод в операнде справа, поэтому его можно переписать как

val s2 = 1 :: (2 :: (3 :: Nil)) //or as
val s2 = Nil.::(3).::(2).::(1)

Таким образом, метод Cons (::) на объекте Nil принимает значение 3 в качестве параметра и создает анонимную копию класса Cons с 3 в качестве главы и ссылкой на объект Nil как его хвост. Позволяет называть этот анонимный объект c1. Затем метод Cons затем вызывается на c1, принимающем 2 в качестве параметра, возвращающего новый анонимный экземпляр Cons, позволяет называть его c2, у которого есть 2 для его головы и ссылка на c1 как его хвост. Затем, наконец, метод cons на объекте c2 принимает значение 1 в качестве параметра и возвращает названный объект s2 с 1 в качестве главы и ссылкой на c2 в качестве его хвоста.

Вторая точка путаницы заключается в том, что таблицы REPL и Scala используют методы класса toString для отображения результатов. Итак, рабочий лист дает нам:

val s3 = List(5, 6, 7)     // s3  : List[Int] = List(5, 6, 7)
val s4 = List()            // s4  : List[Nothing] = List()
val s5: List[Int] = List() // s5  : List[Int] = List()
s3.getClass.getName        // res3: String = scala.collection.immutable.$colon$colon
s4.getClass.getName        // res4: String = scala.collection.immutable.Nil$
s5.getClass.getName        // res5: String = scala.collection.immutable.Nil$

Как сказано выше, список закрыт, поэтому новые подэлементы не могут быть созданы, поскольку Nil является объектом, а Cons - окончательным. Поскольку Nil является объектом, он не может быть параметризован. Ниль наследует от List [Nothing]. На первый взгляд это не звучит так полезно, но помните, что эти структуры данных неизменяемы, поэтому мы никогда не можем добавлять их напрямую, а Nothing - подкласс каждого класса. Таким образом, мы можем добавить класс Nil (косвенно) в любой Список без проблем. В классе Cons есть два члена - голова и другой список. Это довольно аккуратное решение, когда вы его часы.

Я не уверен, что это практическое применение, но вы можете использовать Cons как тип:

var list: ::[Int] = List (1,2,3).asInstanceOf[::[Int]]
println("Initialised List")
val list1 = Nil
list = list1.asInstanceOf[::[Int]] //this will throw a java class cast exception at run time
println("Reset List")

Ответ 2

Короткий ответ: оба.

Существует подкласс списка ::, но вы не будете ссылаться на него явно очень часто.

Когда вы пишете, например. 1 :: 2 :: Nil, :: метод в List, который создает экземпляр класса :: за кулисами.

:: лучше всего рассматривать не как метод или класс, а как конструктор в алгебраическом типе данных (ADT ) смысл. Википедия называет конструкторы ADT "квази-функциональными entit [ies]", что делает их более сложными, чем они есть, но не обязательно является плохим способом думать о них.

List имеет два конструктора, :: (называемый cons на некоторых языках) и Nil. Статья Википедии, приведенная выше, дает хорошее представление о идеях списков как алгебраических типов данных.

Стоит отметить, что в некоторых языках (например, Haskell) конструкторы ADT не имеют собственных типов, связанных с ними, это просто функции, которые создают экземпляр ADT. Обычно это обычно имеет место и в Scala, где довольно редко ссылаться на тип конструктора ADT, например ::. Возможно, однако, мы можем написать это:

def foo(xs: ::[Int]) = ???

Или это (где Some является одним из конструкторов для Option ADT).

def bar(s: Some[Int]) = ???

Но это вообще не очень полезно и может считаться артефактом того, как Scala реализует алгебраические типы данных.