2015-08-14 2 views
2

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

val list = Seq()::Nil 
val listOfInts = List(1,2,3) 
listOfInts.foreach {case x=> 
list::List(x) 
} 

ожидал, что я бы в конечном итоге со списком списков: List (Список (1), список (2), Список (3))

Исходя из Явы я привык просто используя list.add (новый ArrayList (i)), чтобы получить тот же результат. Неужели я здесь?

+1

Может быть, вместо downvoting, SO должен иметь опцию для флага, "Не принимать Coursera." –

+0

Тип возврата 'foreach' -' Unit', поэтому вы ничего не получите. Вероятно, вам нужна карта? –

ответ

-2

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

list ::: listOfInts 

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

Если вам нужна изменчивость, возможно, для повышения производительности, используйте изменяемую коллекцию, такую ​​как ArrayBuffer.

1

Поскольку List является неизменны вы не можете изменить List на месте.

Чтобы создать список из 1 элемента списка из списка, вы можете map над списком. Разница между forEach и map заключается в том, что forEach ничего не возвращает, то есть Unit, а map возвращает список из возвратов некоторой функции.

scala> def makeSingleList(j:Int):List[Int] = List(j) 
makeSingleList: (j: Int)List[Int] 

scala> listOfInts.map(makeSingleList) 
res1: List[List[Int]] = List(List(1), List(2), List(3)) 
1

На странице документации есть учебники.

Существует рекламный ролик для ListBuffer, если вы качаете таким образом.

В противном случае,

scala> var xs = List.empty[List[Int]] 
xs: List[List[Int]] = List() 

scala> (1 to 10) foreach (i => xs = xs :+ List(i)) 

scala> xs 
res9: List[List[Int]] = List(List(1), List(2), List(3), List(4), List(5), List(6), List(7), List(8), List(9), List(10)) 

У вас есть выбор, используя изменяемый строитель как ListBuffer или местного вар и возвращения коллекции вы строите.

В функциональном мире, вы часто строят, предваряя, а затем обратное:

scala> var xs = List.empty[List[Int]] 
xs: List[List[Int]] = List() 

scala> (1 to 10) foreach (i => xs = List(i) :: xs) 

scala> xs.reverse 
res11: List[List[Int]] = List(List(1), List(2), List(3), List(4), List(5), List(6), List(7), List(8), List(9), List(10)) 
+0

Правильный ответ, потому что он работает, плохой ответ, потому что он показывает плохую практику и анти-шаблон – Dici

+1

Извините, @Dici, но каждая функция, которая хочет построить коллекцию, имеет выбор: использовать ли изменяемый построитель императивно или использовать неизменяемый сбор монадически. Это точка, которая снова и снова возвращается домой, поэтому я делаю это снова. Вопрос заключается в том, как их построить, а не как использовать карту и т. Д. –

+0

Так в чем же преимущество вашего фрагмента над идиоматическим 'xs ::: (от 1 до 10)'? – Dici

1

Ниже скопировать и вставить из Scala РЕПЛ с добавленным заявлением для печати, чтобы увидеть, что происходит:

scala>  val list = Seq()::Nil 
list: List[Seq[Nothing]] = List(List()) 

scala>  val listOfInts = List(1,2,3) 
listOfInts: List[Int] = List(1, 2, 3) 

scala>  listOfInts.foreach { case x=> 
|  println(list::List(x)) 
|  } 
List(List(List()), 1) 
List(List(List()), 2) 
List(List(List()), 3) 

Во время первой итерации цикла foreach вы фактически берете первый элемент listOfInts (который равен 1), помещая его в новый список (который является List (1)), а затем добавляете новый список элементов (который является List (List())) в начало списка (1). Вот почему он распечатывает List (List (List()), 1).

Поскольку ваш список и списокOfInts являются неизменными, вы не можете их изменить. Все, что вы можете сделать, это выполнить что-то на них, а затем вернуть новый список с изменением. В вашем списке случаев: List (x) внутри цикла фактически не делает ничего, что вы можете видеть, если не распечатываете его.

0

В Скале у вас есть два (три, а @ сома-snytt показал) вариант - выбрать изменяемую коллекцию (как буфер):

scala> val xs = collection.mutable.Buffer(1) 
// xs: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1) 

scala> xs += 2 
// res10: xs.type = ArrayBuffer(1, 2) 

scala> xs += 3 
// res11: xs.type = ArrayBuffer(1, 2, 3) 

Как вы можете видеть, это работает только например, вы будете работать со списками на Java. Другой вариант у вас есть, а на самом деле это настоятельно рекомендуется, чтобы выбрать в списке обработки функционально, это все, вы берете какую-то функцию, и применить его к каждому и каждый элемент коллекции:

scala> val ys = List(1,2,3,4).map(x => x + 1) 
// ys: List[Int] = List(2, 3, 4, 5) 

scala> def isEven(x: Int) = x % 2 == 0 
// isEven: (x: Int)Boolean 

scala> val zs = List(1,2,3,4).map(x => x * 10).filter(isEven) 
// zs: List[Int] = List(10, 20, 30, 40) 
1

Учитывая val listOfInts = List(1,2,3) , и вы хотите получить конечный результат как List(List(1),List(2),List(3)).

Другой хороший трюк я могу думать скольжения (элементы группы в блоках фиксированного размера пропусканием «скользящего окна» над ними)

scala> val listOfInts = List(1,2,3) 
listOfInts: List[Int] = List(1, 2, 3) 

scala> listOfInts.sliding(1) 
res6: Iterator[List[Int]] = non-empty iterator 

scala> listOfInts.sliding(1).toList 
res7: List[List[Int]] = List(List(1), List(2), List(3)) 

// If pass 2 in sliding, it will be like 
scala> listOfInts.sliding(2).toList 
res8: List[List[Int]] = List(List(1, 2), List(2, 3)) 

Более подробную информацию о скольжения, вы можете иметь чтения около скользящий в scala.collection.IterableLike.

1

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

scala> List(1,2,3).map(List(_)) 
res0: List[List[Int]] = List(List(1), List(2), List(3)) 

Или вы также можете использовать хвост рекурсии:

@annotation.tailrec 
def f(l:List[Int],res:List[List[Int]]=Nil) :List[List[Int]] = { 
    if(l.isEmpty) res else f(l.tail,res :+ List(l.head)) 
} 

scala> f(List(1,2,3)) 
res1: List[List[Int]] = List(List(1), List(2), List(3)) 
0
// input: List(1,2,3) 
// expected output: List(List(1), List(2), List(3)) 

val myList: List[Int] = List(1,2,3) 
val currentResult = List() 

def buildIteratively(input: List[Int], currentOutput: List[List[Int]]): List[List[Int]] = input match { 
    case Nil => currentOutput 
    case x::xs => buildIteratively(xs, List(x) :: currentOutput) 
} 

val result = buildIteratively(myList, currentResult).reverse 
Смежные вопросы