2016-08-12 8 views
0

Почему приведенный ниже оператор действует для .map(), но не для .flatMap()?Scala: flatMap с кортежами

val tupled = input.map(x => (x*2, x*3)) 

//Compilation error: cannot resolve reference flatMap with such signature 
val tupled = input.flatMap(x => (x*2, x*3)) 

Это утверждение не имеет никаких проблем, хотя:

val tupled = input.flatMap(x => List(x*2, x*3)) 
+0

Что такое тип ввода? Проверьте подписи 'map' и' flatMap'. У него разные параметры, поэтому ваш код имеет ошибку компиляции. –

+0

Потому что 'flatMap' используется для выравнивания структуры. Когда вы получите 'M [M [A]]' с 'map', вы получите' M [A] 'с' flatMap'. –

+0

Вложенные 'M' иногда могут быть разными,' N' .. Например, 'List [Option [String]]' => 'List [String]' –

ответ

3

Предполагая input если типа List[Int], map принимает функцию от Int к A, в то время как flatMap принимает функцию от Int к List[A].

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

Например, если вы просто преобразуя элементы List обычно требуется использовать map:

List(1, 2, 3).map(x => x * 2) // List(2, 4, 6) 

но вы хотите изменить структуру List и - например - «взрываются» каждый элемент в другой список, то уплощение их, flatMap ваш друг:

List(1, 2, 3).flatMap(x => List.fill(x)(x)) // List(1, 2, 2, 3, 3, 3) 

Использование map вы имели бы List(List(1), List(2, 2), List(3, 3, 3)) вместо этого.

+1

@NathanielFord да, вы правы. Обновлено, спасибо –

1

Чтобы понять, как это работает, может быть полезно явно распаковать функции, которые вы отправляете, на map и flatMap и изучить их подписи. Я переписал их здесь, так что вы можете видеть, что f - это отображение функции от Int до кортежа (Int, Int), а g - это функция, которая отображает от Int до List[Int].

val f: (Int) => (Int, Int) = x => (x*2, x*3) 
val g: (Int) => List[Int] = x => List(x*2, x*3) 

List(1,2,3).map(f) 
//res0: List[(Int, Int)] = List((2,3), (4,6), (6,9)) 
List(1,2,3).map(g) 
//res1: List[List[Int]] = List(List(2, 3), List(4, 6), List(6, 9)) 
//List(1,2,3).flatMap(f) // This won't compile 
List(1,2,3).flatMap(g) 
//res2: List[Int] = List(2, 3, 4, 6, 6, 9) 

Так почему же не будет flatMap(f)? Давайте посмотрим на подпись для flatMap, в этом случае вытащил из List реализации:

final override def flatMap[B, That](f : scala.Function1[A, scala.collection.GenTraversableOnce[B]])(...) 

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

Это объяснение, связанное с сорняками, почему печатание неверно и стоит объяснить, потому что любая ошибка, которая говорит «не может разрешить ссылку с такой сигнатурой», означает, что она не может найти функцию, которая принимает явные типа, который вы предлагаете. Типы очень часто выводятся в Scala, поэтому вам хорошо известно, что тип, который вы даете, - это тип, ожидаемый методом, который вы вызываете.

Обратите внимание, что flatMap имеет стандартное значение в функциональном программировании, которое является, грубо говоря, любой функцией отображения, которая потребляет один элемент и производит n элементов, но конечным результатом является объединение всех этих списков.Поэтому функция, которую вы переходите на flatMap, всегда будет ожидать для создания списка, и функция flatMap не будет знать, как действовать на отдельные элементы.