Лучший способ представления цикла readline в Scala?

Исходя из фона C/С++, я не очень хорошо знаком с функциональным стилем программирования, поэтому весь мой код имеет очень важное значение, так как в большинстве случаев я просто не вижу лучшего способа сделать это.

Мне просто интересно, есть ли способ сделать этот блок кода Scala более функциональным?

var line:String = "";
var lines:String = "";

do {
    line = reader.readLine();
    lines += line;
} while (line != null)

Ответ 1

Как насчет этого?

val lines = Iterator.continually(reader.readLine()).takeWhile(_ != null).mkString

Ответ 2

Ну, в Scala вы можете сказать:

val lines = scala.io.Source.fromFile("file.txt").mkString

Но это всего лишь библиотечный сахар. См. Прочитать весь файл в Scala? для других возможностей. На самом деле вы спрашиваете, как применить функциональную парадигму к этой проблеме. Вот подсказка:

Source.fromFile("file.txt").getLines().foreach {println}

У вас есть идея? foreach в функции execute println. BTW не волнуется, getLines() возвращает итератор, а не весь файл. Теперь что-то более серьезное:

lines filter {_.startsWith("ab")} map {_.toUpperCase} foreach {println}

Посмотрите на идею? Возьмите lines (это может быть массив, список, набор, итератор, все, что можно отфильтровать, и которое содержит элементы, имеющие метод startsWith) и filter, беря только элементы, начинающиеся с "ab". Теперь возьмите каждый элемент и map с помощью метода toUpperCase. Наконец foreach результирующий элемент print it.

Последняя мысль: вы не ограничены одним типом. Например, скажем, у вас есть файл с целым числом, по одному на строку. Если вы хотите прочитать этот файл, проанализируйте его и суммируйте, просто скажите:

lines.map(_.toInt).sum

Ответ 3

Чтобы добавить, как этого можно добиться, используя ранее новые файлы nio, за которые я голосую, потому что у этого есть несколько преимуществ:

val path: Path = Paths.get("foo.txt")
val lines = Source.fromInputStream(Files.newInputStream(path)).getLines()

// Now we can iterate the file or do anything we want,
// e.g. using functional operations such as map. Or simply concatenate.
val result = lines.mkString

Не забудьте потом закрыть ручей.

Ответ 4

Я нахожу, что Stream - довольно приятный подход: он создает повторную (если необходимо) последовательность:

def loadLines(in: java.io.BufferedReader): Stream[String] = {
  val line = in.readLine
  if (line == null) Stream.Empty
  else Stream.cons(line, loadLines(in))
}

Каждый элемент Stream имеет в этом случае значение (a String, line) и вызывает функцию (loadLines(in) в этом примере), которая даст следующий элемент, лениво, по требованию, Это обеспечивает хороший профиль использования памяти, особенно с большими наборами данных. Линии не читаются до тех пор, пока они не понадобятся, и не сохраняются, если только что-то на самом деле не удерживает их. Тем не менее вы также можете вернуться к предыдущему элементу Stream и снова пройти вперед, давая тот же результат.