Scala - функция карты - это сопоставление с образцом или целое - PullRequest
2 голосов
/ 23 октября 2019

Я потратил несколько недель, пытаясь понять идею, лежащую в основе «подъема» в scala.

Изначально это был пример, связанный с главой 4 книги «Функциональное программирование в Scala»

Затем я нашел ниже тему " Как карта работает на Опции в Scala? "

В выбранном ответе укажите, что:

def map[B](f: A => B): Option[B] = this match (Let's considered this as (*) )

Итак, из приведенного выше кода, яПредположим, что функция "map" получена из функции match. Следовательно, механизм, лежащий в основе «карты», является своего рода сопоставлением с шаблоном, обеспечивающим выбор регистра между Some и None

Затем я создал ниже примеры с использованием функции map для Seq, Option и Map (Давайте рассмотримПриведенные ниже примеры, такие как (**))

Example 1: map for Seq
  val xs = Seq(1, 2, 3)
  xs.map(println)

Example 2: map for Option
val a:Option[Int] = Some(5)
  a.map(println)
val b:Option[Int] = None
  b.map(println)

Example 3: map for Map
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo")
capitals.map(println)

Из (*) и (**), я не мог знать, является ли «карта» сопоставлением с образцом или итерацией, или и тем, и другим.

Спасибо, что помогли мне понять это.

Ответы [ 3 ]

2 голосов
/ 23 октября 2019

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

Я, конечно, ценю, что вы пытаетесь понять, как все работает в Scala, однако, если вы действительно хотите копнуть так глубоко, я боюсь, что вам нужно будет получить некоторые базовые знания теории категорий, поскольку за этим нет "идеи"поднятие в скале », но просто« идея поднятия »

Вот почему такие функции, как« карта », могут быть очень запутанными. По сути, программистов учат отображать и т. Д. Как операции над коллекциями, где они фактически являются операциями, которые идут с Функторами и Естественными Преобразованиями (это обычно упоминается как fmap в теории категорий, а также на Haskell).

Прежде чем я продолжу, краткий ответ - это сопоставление с образцом в приведенных вами примерах, а в некоторых из них это оба. Карта определена специально для случая, единственное условие - она ​​поддерживает functoriality

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

Давайте рассмотрим их как Функторы, определение будет примерно таким:

В (очень очень)Короче говоря, мы рассматриваем типы как объекты в категории нашего языка. Функции между этими типами (конструкторы типов) представляют собой морфизмы между типами в этой категории. Множество этих преобразований называются эндофункторами (заберите нас из категории Scala и верните в категорию Scala). Функторы должны иметь полиморфную (которая на самом деле имеет совершенно другое (дополнительное) определение в теории категорий) функцию карты, которая возьмет некоторый объект A, через конструктор типов превратит его в объект B.

implicit val option: Functor[Option] = new Functor[Option] {
    override def map[A,B](optA: Option[A])(f: (A) => B): Option[B] = optA match{
        case Some(a) => Some(f(a))
        case _ => None
    }
}


implicit val seq: Functor[Seq[_]] = new Functor[Seq[_]] {
  override def map[A,B](sA: Seq[A])(f: (A) => B): Seq[B] = sA match{
     case a :: tail => Seq(f(a), map(tail)(f))
     case Nil => Nil
  }
}

Как вы можете видеть во втором случае, есть и то, и другое (скорее рекурсия, чем итерация, но все же).

Теперь, прежде чем интернет взорвется, я скажу, что вы не можете сопоставить шаблон сСек в Скала. Это работает здесь, потому что по умолчанию Seq также является списком. Я просто привел этот пример, потому что он проще для понимания. Базовое определение что-то вроде этого.

Теперь подождите секунду. Если вы посмотрите на эти типы, вы увидите, что для них также определен flatMap. Это означает, что они являются чем-то более особенным, чем простые функторы. Это монады. Таким образом, помимо удовлетворения функториальности, они подчиняются монадическим законам.

Оказывается, у Monad есть другое значение в базовой scala, подробнее об этом здесь: Что именно делает Option монадой в Scala?

Но, опять же, очень оченьКороче говоря, это означает, что мы сейчас находимся в категории, где эндофункторы из нашей предыдущей категории являются объектами, а отображения между ними - морфизмы (естественные преобразования), это немного более точно, потому что, если вы подумаете об этом, когда вы берете тип итрансформируя его, вы берете (переносите) все его внутренние конструкторы типов (2-элементные или внутренние морфизмы) вместе с ним, вы не только берете эту единственную идею типа без его функций.

implicit val optionMonad: Monad[Option] = new Monad[Option] {
 override def flatMap[A, B](optA: Option[A])(f: (A) => Option[B]): Option[B] =  optA match{
   case Some(a) => f(a) 
   case _ => None 
 } 

 def pure[A](a: A): Option[A] = Some(a)

   //You can define map using pure and flatmap

}

implicit val seqMonad: Monad[Seq[_]] = new Monad[Seq[_]] {
   override def flatMap[A, B](sA: Seq[A])(f: (A) => Seq[B]): Seq[B] = sA match{
      case x :: xs => f(a).append(flatMap(tail)(f))
      case Nil => Nil
   } 
   override def pure[A](a: A): Seq[A] = Seq(a) 
   //Same warning as above, also you can implement map with the above 2 funcs 
 }  

Одна вещь, на которую вы всегда можете рассчитывать, это карта, имеющая совпадение с шаблоном (или некоторая инструкция if). Почему? Чтобы удовлетворить законы идентичности, нам нужно иметь своего рода «базовый случай», объект-единицу и во многих случаях (например, списки) эти типы будут тем, что мы называем продуктом или копроизведением.

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

2 голосов
/ 23 октября 2019

Со страницы ScalaDocs мы видим, что профиль типа для стандартной библиотеки map() немного отличается.

def map[B](f: (A) => B): Seq[B]

Таким образом, стандартная библиотека map()средство для перехода от коллекции элементов типа A к той же коллекции, но элементы типа B. (A и B могут относиться к одному и тому же типу. Они не обязательно должны быть разными.)

Итак, да, он выполняет итерацию по всей коллекции, применяя функцию f() к каждому элементу Aсоздать каждый новый элемент B. И функция f() может использовать сопоставление с образцом в своем коде, но это не обязательно.

Теперь рассмотрим a.map(println). Каждый элемент a отправляется на println, который возвращает Unit. Поэтому, если a равно List[Int], то результат a.map(println) равен List[Unit], что не очень полезно.

Когда все, что нам нужно, это побочный эффект отправки информации в StdOut, тогда мы используемforeach(), который не создает новую коллекцию: a.foreach(println)

1 голос
/ 23 октября 2019

Функция map для Option не касается сопоставления с образцом. match/case, использованное в указанной вами ссылке, является лишь одним из многих способов определения функции. Это можно было определить с помощью if/else. Фактически, именно так это определено в Scala 2.13 для источника class Option:

sealed abstract class Option[+A] extends IterableOnce[A] with Product with Serializable {
  self =>
    ...
    final def map[B](f: A => B): Option[B] =
      if (isEmpty) None else Some(f(this.get))
    ...
  }

Если вы просматриваете Option как «коллекцию» одного из элементов (Some(x)) или без элементов (None), может быть легче увидеть сходство того, как map преобразует Option по сравнению, скажем, с List:

val f: Int => Int = _ + 1

List(42).map(f)
// res1: List[Int] = List(43)

List.empty[Int].map(f)
// res2: List[Int] = List()

Some(42).map(f)
// res3: Option[Int] = Some(43)

None.map(f)
// res4: Option[Int] = None
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...