2013-06-18 4 views
2

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

val s = if (<some condition>) Array(...) else List(...) 
print(s.length) 

Дело в том, что с будет выведено в качестве Object, так как нет общего суперкласса для Array и List. Таким образом, вторая строка дает ошибку, так как Object не имеет метода length.

Заранее благодарен!

Update

Ба, это позор, что я не могу принять несколько ответов! Спасибо за вашу помощь, ребята! Я играл с Scala довольно долгое время, но я, кажется, все еще есть много, чтобы узнать :-)

+0

Я знаю, он не отвечает на вопрос, но зачем вам делать такую ​​дифференциацию? Возможно, если вы дадите больше контекста, мы могли бы обходиться таким образом, чтобы определение не требовалось. (кстати, есть методы * .to [...] * *) –

+0

Да, я знаю, что это довольно странно, и я бы не встретил это нормально :-) Я разбиваю чей-то контрольный код и хотел бы включить два дополнительных случаев, один для Array и один для списков. Я знаю, что это может быть решение, но я также хотел бы, чтобы это было просто, чтобы убедить его, что Скала крута и лаконична :-) – rlegendi

+1

Вы можете использовать оболочку для 'Array' и использовать' s' как 'Seq [T]': 'if (true) Array (1): Seq [Int] else List (1)'. Вы получите 'res0: Seq [Int] = WrappedArray (1)'. – senia

ответ

1

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

val s: collection.SeqLike[Int,_] = 
    if (condition) Array(1, 2, 3) 
    else List(1, 2, 3, 4); 
print(s.size) 

Обновление: Для многомерных массивов, вам нужно вызвать неявные преобразования внутри, а также, потому что Array[ArrayOps[X]] не присваиваемые Array[Array[X]] и наоборот:

type SL[+A] = SeqLike[A,_] 
val s1: SL[SL[Int]] = 
    if (x) Array(Array(1, 2, 3): SL[Int]) else List(List(1, 2, 3, 4)); 
print(s1.size) 

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

Если вы сами создаете массивы из фиксированного набора элементов, вы можете создавать вспомогательные функции, которые возвращают одну из двух возможных оберток (ArrayOps и WrappedArray). Тогда вам не потребуется какая-либо явная типизация:

import scala.collection.mutable._ 
import scala.reflect.ClassManifest 

// Using ArrayOps 
def arrayO[A: ClassManifest](xs: A*): ArrayOps[A] = Array(xs : _*); 
val s2 = 
    if (x) arrayO(arrayO(1, 2), arrayO(3)) else List(List(1, 2), List(3, 4)); 
println(s2.size) 

// UsingWrappedArray 
def arrayW[A: ClassManifest](xs: A*): WrappedArray[A] = Array(xs : _*); 
val s3 = 
    if (x) arrayW(arrayW(1, 2), arrayW(3)) else List(List(1, 2), List(3, 4)); 
println(s3.size) 

Если у вас есть существующие массивы, которые вы хотите, чтобы обернуть, вам нужно обернуть каждый уровень, как в

val a4 = Array(Array(1, 2, 3), Array(5, 6)); 
val s4: SL[SL[Int]] = a.map(x => x: SL[Int]) 
+0

Кажется здорово! К сожалению, когда я пытаюсь использовать его в 2D seqs, например 'val s: SeqLike [SeqLike [Int, _] _] = if (...) Array (Array (1)) else List (List (1))' I все еще возникают ошибки типа. Не могли бы вы рассказать, как я могу успокоить компилятор? Благодаря! – rlegendi

+0

@rlegendi Я продлил ответ. –

+0

Прохладный, спасибо, Петр! Я точно искал то, что указано в вашем втором блоке кода. Спасибо за ваше время снова. – rlegendi

2

Я был под впечатлением, что черта Scala Iterable охватывает этот случай использования: http://www.scala-lang.org/api/current/index.html#scala.collection.Iterable я тоже noobish о Scala.

+0

Обратите внимание, что под обложкой подразумевается неявный массив в [WrappedArray] (http://www.scala-lang.org/api/current/index.html#scala.collection.mutable.WrappedArray) обертывание –

4

Вы можете использовать обертку для Array и использовать s в Seq[T]:

scala> val s = if (true) Array(1): Seq[Int] else List(1) 
s: Seq[Int] = WrappedArray(1) 

Для многомерных массивов можно создавать свои собственные конвертеры, как это:

implicit class ArrArrWrap[T](a: Array[Array[T]]) extends IndexedSeq[WrappedArray[T]] { 
    def length = a.length 
    def apply(idx: Int) = a(idx): WrappedArray[T] 
} 

Использование:

scala> val s = if (true) Array(Array(1)): Seq[Seq[Int]] else List(List(1)) 
s: Seq[Seq[Int]] = (WrappedArray(1)) 

Идея использования мультидименси массив в виде многомерных последовательностей имеет большой недостаток:

Он включает преобразование при доступе ко всем измерениям, кроме последнего.

s(0) // <- conversion here 

Это может привести к проблемам с производительностью. Это может быть лучше, чтобы преобразовать Array в Seq:

scala> val s = if (true) Array(Array(1)).map{ i => i: Seq[Int] }(breakOut) else List(List(1)) 
s: scala.collection.immutable.Seq[Seq[Int]] = Vector(WrappedArray(1)) 
+0

Thx senia ! К сожалению, когда я пытаюсь использовать его для многомерных последовательностей типа 'val s = if (true) Array (Array (1)): Seq [Seq [Int]] else List (List (1))' Я снова получаю несоответствие типов , Не могли бы вы рассказать мне, как я могу обобщить это? Благодаря! – rlegendi

+0

@rlegendi: Я обновил свой ответ. Я предполагаю, что это может быть обобщено на любое количество массивов в [бесформенном] (https://github.com/milessabin/shapeless) стиле, но это не стоит пытаться из-за проблем с производительностью. – senia

+0

Отлично, спасибо за отличный ответ, @senia! Кроме того, спасибо за указание на неявное преобразование при индексации! Думаю, я не узнал бы это сам. К сожалению, я не могу принять 2 ответа, и Петр Пудлак придумал немного более легкое решение в отношении моих конкретных потребностей. И спасибо за помощь снова! – rlegendi

Смежные вопросы