алгоритм сортировочной станции является правильным инструментом для этого. Википедия действительно сбивает с толку об этом, но в основном работах алгоритма как это:
Говорят, Вы хотите оценить 1 + 2 * 3 + 4. Интуитивно, Вы "знаете", что необходимо сделать 2 * 3 первых, но как Вы получаете этот результат? Ключ должен понять при сканировании строки слева направо оценку оператора, когда оператор, который следует , это имеет более низкое (или равный) приоритет. В контексте примера вот то, что Вы хотите сделать:
, Как Вы реализуете это?
Вы хотите иметь два стека, один для чисел и другого для операторов. Вы продвигаете числа на стек все время. Вы сравниваете каждый новый оператор с тем наверху стека, если тот сверху стека имеет более высокий приоритет, Вы выталкиваете его от стопки оператора, выталкиваете операнды от стопки числа, применяете оператор и продвигаете результат на стопку числа. Теперь Вы повторяете сравнение с вершиной оператора стека.
Возвращение к примеру, это работает как это:
Н = [] операция в секунду = []
*
. N = [1 2], операция в секунду = [+ *] *
3 и продвигает результат на N. N = [1 6], операция в секунду = [+] +
левоассоциативна, таким образом, Вы хотите появиться 1, 6 прочь также и выполниться +. N = [7], операция в секунду = []. Там, это не так трудно, не так ли? И это не делает вызовов ни к каким грамматикам или парсерам-генераторам.
Ответ находится в определении карты
:
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
Обратите внимание, что у него два параметра. Первая - это ваша функция, а вторая - неявная. Если вы не укажете это неявно, Scala выберет наиболее конкретный из доступных.
О breakOut
Итак, какова цель breakOut
? Рассмотрим пример, приведенный для вопроса. Вы берете список строк, преобразуете каждую строку в кортеж (Int, String)
, а затем создаете из него Map
. Наиболее очевидный способ сделать это - создать промежуточную коллекцию List [(Int, String)]
, а затем преобразовать ее.
Учитывая, что карта
использует Builder
для создания результирующей коллекции, нельзя ли пропустить промежуточный List
и собрать результаты непосредственно в a Карта
? Очевидно, что да. Однако для этого нам нужно передать правильную CanBuildFrom
в карту
, и это именно то, что делает breakOut
.
Давайте посмотрим, в определении breakOut
:
def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) =
new CanBuildFrom[From, T, To] {
def apply(from: From) = b.apply() ; def apply() = b.apply()
}
Обратите внимание, что breakOut
параметризован и возвращает экземпляр CanBuildFrom
. Так получилось, что типы From
, T
и To
уже были выведены, потому что мы знаем, что map
ожидает CanBuildFrom [Список [String], (Int, String), Map [Int, String]]
. Поэтому:
From = List[String]
T = (Int, String)
To = Map[Int, String]
В заключение рассмотрим неявное выражение, полученное самим breakOut
. Он имеет тип CanBuildFrom [Nothing, T, To]
. Мы уже знаем все эти типы, поэтому можем определить, что нам нужен неявный тип CanBuildFrom [Nothing, (Int, String), Map [Int, String]]
. Но существует ли такое определение?
Давайте посмотрим на определение CanBuildFrom
:
trait CanBuildFrom[-From, -Elem, +To]
extends AnyRef
Итак, CanBuildFrom
является контрвариантным по своему первому параметру типа. Поскольку Nothing
является нижним классом (т. Е. Подклассом всего), это означает, что любой класс может использоваться вместо Nothing
.
Поскольку такой конструктор существует, Scala может использовать его для получения желаемого результата.
О конструкторах
Многие методы из библиотеки коллекций Scala состоят из взятия исходной коллекции, ее обработки (в случае map
, преобразования каждого элемента) и сохранения результатов в новая коллекция.
Чтобы максимально увеличить повторное использование кода, это сохранение результатов осуществляется с помощью построителя ( scala.collection.mutable.Builder
), который в основном поддерживает две операции: добавление элементов , и возвращая получившуюся коллекцию. Тип этой результирующей коллекции будет зависеть от типа построителя. Таким образом, построитель List
вернет List
, построитель Map
вернет Map
и так далее. Реализация метода map
не должна интересоваться типом результата: об этом позаботится строитель.
С другой стороны, это означает, что карта
каким-то образом должна получить этого строителя. Проблема, с которой столкнулись при разработке коллекций Scala 2.8, заключалась в том, как выбрать лучший из возможных построителей. Например, если бы я написал Map ('a' -> 1) .map (_. Swap)
, я бы хотел получить Map (1 -> 'a')
назад. С другой стороны, Map ('a' -> 1) .map (_._ 1)
не может возвращать Map
(он возвращает Iterable
]).
Магия создания наилучшего возможного Builder
из известных типов выражений осуществляется посредством неявного CanBuildFrom
.
О CanBuildFrom
] Чтобы лучше объяснить, что происходит, я Я приведу пример, в котором отображаемая коллекция представляет собой Map
вместо List
. Я вернусь к Списку
позже. А пока рассмотрим эти два выражения:
Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
Map(1 -> "one", 2 -> "two") map (_._2)
Первое возвращает Map
, а второе возвращает Iterable
. Магия возврата подходящей коллекции - это работа CanBuildFrom
. Давайте снова рассмотрим определение map
, чтобы понять его.
Метод map
унаследован от TraversableLike
. Он параметризован на B
и That
и использует параметры типа A
и Repr
, которые параметризуют класс. Давайте вместе посмотрим оба определения:
Класс TraversableLike
определяется как:
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
Чтобы понять, откуда берутся A
и Repr
, давайте рассмотрим определение Map
:
trait Map[A, +B]
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
Поскольку TraversableLike
наследуется всеми чертами, которые расширяют Map
, A
и Repr
могут быть унаследованы от любого их. Однако предпочтение отдается последнему. Итак, следуя определению неизменяемой Map
и всех свойств, которые связывают ее с TraversableLike
, мы имеем:
trait Map[A, +B]
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]]
extends MapLike[A, B, This]
trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]]
extends PartialFunction[A, B] with IterableLike[(A, B), This] with Subtractable[A, This]
trait IterableLike[+A, +Repr]
extends Equals with TraversableLike[A, Repr]
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
Если вы передадите параметры типа Map [Int , String]
по всей цепочке, мы обнаруживаем, что типы, переданные в TraversableLike
и, таким образом, используемые map
, следующие:
A = (Int,String)
Repr = Map[Int, String]
Возвращаясь к пример, первая карта получает функцию типа ((Int, String)) => (Int, Int)
, а вторая карта получает функцию типа ((Int, String)) = > Строка
. Я использую двойные скобки, чтобы подчеркнуть, что это полученный кортеж, поскольку это тип A
, как мы видели.
Имея эту информацию, давайте рассмотрим другие типы.
map Function.tupled(_ -> _.length):
B = (Int, Int)
map (_._2):
B = String
Мы видим, что тип, возвращаемый первой картой
, - Map [Int, Int]
, а второй - Iterable [String]
. Глядя на определение map
, легко увидеть, что это значения That
. Но откуда они?
Если мы заглянем внутрь сопутствующих объектов задействованных классов, мы увидим некоторые неявные объявления, предоставляющие их. На объекте Карта
:
implicit def canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]]
И для объекта Iterable
, класс которого расширен Map
:
implicit def canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]]
Эти определения предоставляют фабрики для параметризованного CanBuildFrom
.
Scala выберет самый конкретный неявный из имеющихся. В первом случае это был первый CanBuildFrom
. Во втором случае, поскольку первое не соответствовало, было выбрано второе CanBuildFrom
.
Назад к вопросу
Давайте посмотрим на код вопроса, Список
' s и определение map
(снова), чтобы увидеть, как выводятся типы:
val map : Map[Int,String] = List("London", "Paris").map(x => (x.length, x))(breakOut)
sealed abstract class List[+A]
extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]]
trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]]
extends SeqLike[A, Repr]
trait SeqLike[+A, +Repr]
extends IterableLike[A, Repr]
trait IterableLike[+A, +Repr]
extends Equals with TraversableLike[A, Repr]
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
Типом List ("London", "Paris")
является List [String]
, поэтому типы A
и Repr
, определенные в TraversableLike
, следующие: