Выход Scala 2.8

алгоритм сортировочной станции является правильным инструментом для этого. Википедия действительно сбивает с толку об этом, но в основном работах алгоритма как это:

Говорят, Вы хотите оценить 1 + 2 * 3 + 4. Интуитивно, Вы "знаете", что необходимо сделать 2 * 3 первых, но как Вы получаете этот результат? Ключ должен понять при сканировании строки слева направо оценку оператора, когда оператор, который следует , это имеет более низкое (или равный) приоритет. В контексте примера вот то, что Вы хотите сделать:

  1. Взгляд на: 1 + 2, ничего не делайте.
  2. Теперь взгляд 1 + 2 * 3, все еще ничего не делайте.
  3. Теперь взгляд 1 + 2 * 3 + 4, теперь Вы знаете, что 2 * 3 имеет быть оцененным, потому что следующий оператор имеет более низкий приоритет.

, Как Вы реализуете это?

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

Возвращение к примеру, это работает как это:

Н = [] операция в секунду = []

  • Read 1. N = [1], операция в секунду = []
  • Read +. N = [1], операция в секунду = [+]
  • Read 2. N = [1 2], операция в секунду = [+]
  • Read *. N = [1 2], операция в секунду = [+ *]
  • Read 3. N = [1 2 3], операция в секунду = [+ *]
  • Read +. N = [1 2 3], операция в секунду = [+ *]
    • Pop 3, 2 и выполняется 2 * 3 и продвигает результат на N. N = [1 6], операция в секунду = [+]
    • + левоассоциативна, таким образом, Вы хотите появиться 1, 6 прочь также и выполниться +. N = [7], операция в секунду = [].
    • Наконец продвигают [+] на стопку оператора. N = [7], операция в секунду = [+].
  • Read 4. N = [7 4]. Операция в секунду = [+].
  • Вы закончены от входа, таким образом, Вы хотите освободить стеки теперь. На который Вы получите результат 11.

Там, это не так трудно, не так ли? И это не делает вызовов ни к каким грамматикам или парсерам-генераторам.

223
задан DNA 8 May 2015 в 11:50
поделиться

1 ответ

Ответ находится в определении карты :

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 , следующие:

323
ответ дан 23 November 2019 в 03:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: