Во время стадии проектирования моих программ я часто ломаю проблему сначала , и как только мне сломали его тогда, я могу легко начать распознавать шаблоны, свойственные в рамках дизайна. В той точке я могу начать применять известные шаблоны разработки так, чтобы, когда она прибывает время, чтобы передать или реализовать дизайн, у меня был общий язык, который я могу использовать для коммуникации, и надо надеяться я могу снова использовать некоторые родовые объекты, которые были ранее реализованы во время реализации подобного шаблона.
Часто времена, когда шаблоны разработки применяются неправильно, это - потому что процесс происходит наоборот. Программист Joe (мои извинения тем из Вас назвали Joe) читает книгу по Шаблонам разработки и говорит "Хорошо, я понимаю Шаблон разработки X, теперь как я могу применить его к своему приложению?" Это неправильно.
Шаблоны разработки могут быть могущественным оружием, но как что-либо еще, они должны использоваться соответственно, и программист должен всегда быть готов включить некоторую исходную мысль в свой дизайн, также.
Чтобы отфильтровать список:
val list = List(1,2,3,4,5)
//only evens
val evens = list.filter(e=>e%2 == 0)
println(list)
//--> List(1, 2, 3, 4, 5)
println(evens)
//--> List(2, 4)
Вы также можете использовать подстановочный знак для сохранения нескольких символов:
val evens = list.filter(_%2==0)
Обратите внимание, что, как указано выше, списки неизменяемы. Это означает, что эти операции не изменяют исходный список, а фактически создают новый список.
Вот не ответ: не делайте этого. Список
неизменяем, поэтому нет абсолютно никакого смысла копировать его.
Давайте рассмотрим несколько операций:
val list = List(1,2,3)
val l1 = 0 :: list
val l2 = "a" :: list
Ни l1
, ни l2
не изменяются list
, но они оба создают новые списки, ссылающиеся на list
.
Поясним это подробнее. Конструктор List (1,2,3)
создает три элемента и также использует одноэлементный объект. В частности, он создает экземпляры этих элементов:
::(3, Nil)
::(2, reference to the previous element)
::(1, reference to the previous element)
И Nil
- одноэлементный объект. Идентификатор list
фактически указывает на последний элемент.
Теперь, когда вы назначаете 0 :: list
на l1
, вы создаете один новый экземпляр object:
::(0, reference to ::(1, etc))
Конечно, поскольку есть ссылка на list
, вы можете думать о l1
как о списке из четырех элементов (или пяти, если считать Nil
).
Теперь l2
даже не относится к тому же типу списка
, но ТАКЖЕ ссылается на него! Здесь:
::("a", reference to ::(1, etc))
Однако важным моментом в отношении всех этих объектов является то, что их нельзя изменить. Нет сеттеров или каких-либо методов, которые изменяли бы какие-либо их свойства. У них всегда будут одни и те же значения / ссылки в их «голове» (это то, что мы называем первым элементом), и те же ссылки в их «хвосте» (это то, что мы называем вторым элементом).
Однако есть методы, которые выглядят так, как будто они меняют список. Но будьте уверены, они создают новых списков. Например:
val l3 = list map (n => n + 1)
Карта методов создает полностью новый список того же размера, где новый элемент может быть вычислен из соответствующего элемента в list
(но вы также можете игнорировать старый элемент).
val l4 = l2 filter (n => n.isInstanceOf[Int])
Хотя l4
имеет те же элементы, что и list
(но другого типа), это также совершенно новый список. Метод filter
создает новый список на основе правила, которое вы передаете, чтобы сообщить ему, какие элементы входят, а какие нет. Он не пытается оптимизировать, если может вернуть существующий список.
val l5 = list.tail
Это не создает новый список. Вместо этого он просто присваивает 15
существующий элемент из list
.
val l6 = list drop 2
Опять же, новый список не создается.
val l7 = list take 1
Это, однако, создает новый список именно потому, что он не может изменить первый элемент списка
так, чтобы его хвост указывал на Nil
.
Вот несколько дополнительных деталей реализации:
List
- абстрактный класс . У него два потомка: класс ::
(да, это имя класса) и одноэлементный объект Nil
. Список
запечатан, поэтому вы не можете добавлять в него новые подклассы, а ::
является окончательным, поэтому вы не можете создать подклассы.
Хотя вы не можете этого сделать. что угодно, чтобы изменить список, он использует изменяемое состояние внутри некоторых операций. Это помогает с производительностью, но он локализован, так что ни одна программа, которую вы пишете, никогда не сможет обнаружить его и не пострадает от этого. Вы можете передавать списки по своему усмотрению, независимо от того, что с ними делают другие функции или сколько потоков используют их одновременно.