Ответ 1
Как насчет этого?
val lines = Iterator.continually(reader.readLine()).takeWhile(_ != null).mkString
Исходя из фона C/С++, я не очень хорошо знаком с функциональным стилем программирования, поэтому весь мой код имеет очень важное значение, так как в большинстве случаев я просто не вижу лучшего способа сделать это.
Мне просто интересно, есть ли способ сделать этот блок кода Scala более функциональным?
var line:String = "";
var lines:String = "";
do {
line = reader.readLine();
lines += line;
} while (line != null)
Как насчет этого?
val lines = Iterator.continually(reader.readLine()).takeWhile(_ != null).mkString
Ну, в 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
Чтобы добавить, как этого можно добиться, используя ранее новые файлы 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
Не забудьте потом закрыть ручей.
Я нахожу, что 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
и снова пройти вперед, давая тот же результат.