Чтение большого файла в функциональном scala - PullRequest
4 голосов
/ 15 октября 2011

Я пытаюсь обработать большой двоичный файл с помощью scala.Если возможно, я бы хотел использовать функциональный подход.Мой основной метод для этого выглядит прямо сейчас:

def getFromBis( buffer:List[Byte], bis:BufferedInputStream ):(Byte,List[Byte],Boolean) = {
    buffer match {
        case Nil =>
            val buffer2 = new Array[Byte](100000)
            bis.read(buffer2) match {
                case -1 => (-1,Nil,false)
                case _  => 
                    val buffer3 = buffer2.toList
                    (buffer3.head,buffer3.tail,true)
            }
        case b::tail => return (b,tail,true)
    }
}

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

Как видите, это не оченьфункциональны.Я пытаюсь сделать это так, чтобы было как можно меньше базовых вызовов, поэтому я делаю это частями.Проблема здесь в новом массиве.Каждый раз, когда я запускаю функцию, она создает новый массив, и, судя по постоянно увеличивающемуся использованию памяти при запуске программы, я не думаю, что они уничтожаются.

Мой вопрос таков: есть лилучший способ чтения большого файла с использованием scala?Я хотел бы сохранить полностью функциональный подход, но по крайней мере мне нужна функция, которая могла бы действовать как черный ящик для остальной части моей функциональной программы.

1 Ответ

7 голосов
/ 15 октября 2011

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

Самый простой способ сделать это - создать итератор, который хранит внутреннее состояние:

class BisReader(bis: BufferedInputStream) {
  val buffer = new Array[Byte](100000)
  var n = 0
  var i = 0
  def hasNext: Boolean = (i < n) || (n >= 0 && {
    n = bis.read(buffer)
    i = 0
    hasNext
  })
  def next: Byte = {
    if (i < n) {
      val b = buffer(i)
      i += 1
      b
    }
    else if (hasNext) next
    else throw new IOException("Input stream empty")
  }
}
implicit def reader_as_iterator(br: BisReader) = new Iterator[Byte] {
  def hasNext = br.hasNext
  def next = br.next
}

Можно использовать расширение BisReader для Iterator [Byte], но, поскольку Iterator не специализирован, это потребует создания бокса для необработанного доступа next / hasNext. Таким образом, вы можете получить низкоуровневый (next / hasNext) доступ на полной скорости, когда вам это нужно, и в противном случае использовать удобные методы итератора.

Теперь вы изолировали свои уродливые нефункциональные компоненты ввода-вывода Java в одном классе с чистым интерфейсом и можете вернуться к функциональности.


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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...