альтернатива breakOut, которая является менее подробной

#scala

#scala

Вопрос:

breakOut хорош, но слишком многословен:

 List(1, 2, 3).map{i => (i * 2, i / 2.0, i.toString)}(breakOut) : Array[(Int, Double, String)]
  

Я не хочу указывать тип элемента. Я хочу что-то вроде:

  List(1, 2, 3).map{i => (i * 2, i / 2.0, i.toString)}(build[Array])
  

Я могу buildArray легко написать, но тогда мне понадобятся buildSet, buildList и т.д. Итак, я хочу что-то общее.

Дополнительные баллы ( ;-)), если вы можете заставить это работать для Map (используя то же имя build , а не build2 или buildMap)

Комментарии:

1. Это не то, что вы хотите, но я думаю, это лучшее, что вы можете получить. Это не будет работать для String или Map .

2. Да, это то, чего я хотел. Я сделал то же самое, но перепутал общие параметры (использовал T в def и A в коде) и не смог разобраться с ошибкой компилятора.

3. @senia Может быть, сделать это ответом?

4. Кстати, было бы неплохо удалить пустые скобки.

Ответ №1:

Это не будет работать для String или Map . Также этот код требует scala.language.higherKinds :

 import collection.generic.CanBuildFrom
import collection.breakOut

class Build[To[_]]

def build[To[_]] = new Build[To]

implicit def buildToCbf[From, T, To[_]](b: Build[To])
                                       (implicit cbf: CanBuildFrom[Nothing,T,To[T]]): CanBuildFrom[From,T,To[T]] =
  collection.breakOut

List(1, 2, 3).map{i => (i * 2, i / 2.0, i.toString)}(build[Array])
//res0: Array[(Int, Double, String)] = Array((2,0.5,1), (4,1.0,2), (6,1.5,3))
  

Ответ №2:

Решение Senia отличное. К сожалению, как он упомянул, это не будет работать для Map nor String . Вот другая альтернатива (основанная на его решении), которая делает:

 import collection.generic.CanBuildFrom
import collection.breakOut

class Build[To]

def build[TargetSuperType] = new Build[TargetSuperType]

implicit def buildToCbf[From, T, TargetSuperType, To<:TargetSuperType](b: Build[TargetSuperType])
                                       (implicit cbf: CanBuildFrom[Nothing,T,To]): CanBuildFrom[From,T,To] =
  collection.breakOut

List(1, 2, 3).map{i => (i * 2, i / 2.0, i.toString)}(build[Array[_]])
//res0: Array[(Int, Double, String)] = Array((2,0.5,1), (4,1.0,2), (6,1.5,3))

List(1, 2, 3).map{i => (i * 2, i.toString)}(build[Map[_,_]])
//res1: scala.collection.immutable.Map[Int,String] = Map(2 -> 1, 4 -> 2, 6 -> 3)

List('a', 'b', 'c').map(_.toUpper)(build[String])
//res2: String = ABC
  

Это немного более подробно, потому что теперь вы не просто делаете build[Array] , но build[Array[_]] . В обмен вы получаете возможность указать любую целевую коллекцию, которую вы хотите, независимо от количества аргументов типа (таких как Map и String ).

Кроме того, вы все равно можете быть полностью явными (аналогично использованию breakOut ), если вы решите:

 scala> List(1, 2, 3).map{i => (i * 2, i / 2.0, i.toString)}(build[Array[(Int, Double, String)]])
res3: Array[(Int, Double, String)] = Array((2,0.5,1), (4,1.0,2), (6,1.5,3))
  

Все это с тем же синтаксисом (другими словами, с использованием того же имени build , как вы просили)