Сделайте код более функциональным

1

Вот код Java-Scala:

class MyDbManager extends SQLiteOpenHelper ....
val cursor = new MyDbManager().getReadableDatabase.query(....)
val result = new ArrayList[MyItem] //???

//?????????
if (cursor.moveToFirst()) {
  do {
    result + parse(cursor)
  } while (cursor.moveToNext())
}
//?????????

cursor.close()
result

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

val result = if (cursor.moveToFirst()) {
  do { parse(cursor) } while (cursor.moveToNext())
}

У вас возникла идея: не использовать избыточные переменные, особенно изменчивые. Конечно, код выше не будет компилироваться.

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

обновление: "для" было предложено. поскольку "for" переводит на "карту" и "фильтр" в моем случае, интересно, почему это не работает:

if.(cursor.moveToFirst()) cursor.filter(_.moveToNext()).map { x => parse(x) }
  • 1
    Это будет сложно: курсор БД по своей сути является императивной конструкцией. Вы можете использовать fold проходящий вдоль курсора, если вы хотите избежать императивных циклов.
  • 0
    Или, если вы хотите использовать filter , вы можете использовать предикат, всегда разрешающий значение true чтобы получить все строки запроса.
Показать ещё 2 комментария
Теги:
functional-programming

1 ответ

1

Этот код основан на java.sql.ResultSet, но шаблон должен быть применим и к вашему делу.

Вариант 1. Предоставьте метод foreach

Вы можете определить класс следующим образом:

class Cursor[T](rs: ResultSet)(f: Row => T) {
  def foreach(g: T => Unit) {
    val row = new Row(rs)
    while (rs.next()) {
      g(f(row))
    }
  }
}

Он получает два аргумента:

  • Основной набор результатов
  • Функция f которая создает экземпляр для каждой строки. Поэтому во время итерации создаются экземпляры, основанные на текущей строке.

Класс Cursor предоставляет метод foreach. Поэтому его можно использовать в "для понимания":

val cursor = Cursor(rs) {
  row =>
    Person(row.getString(1), row.getString(2))
}

for {
  person <- cursor
} {
  println(person)
}

Чтобы иметь возможность использовать синтаксис {... } для функции f, необходим сопутствующий объект:

object Cursor {
  def apply[T](rs: ResultSet)(f: Row => T) = {
    new Cursor(rs)(f)
  }
}

Используется класс-оболочка Row представляющий строку результирующего набора, потому что некоторые методы базового набора результатов не должны быть видны функции (например, next()).

class Row(rs: ResultSet) {
  def getString(n: Int) = rs.getString(n)
  def getInt(n: Int) = rs.getInt(n)
  // ... more getters for other types
}

Вариант 2: удлинить итератор

class CursorWithIterator[T](rs: ResultSet)(f: Row => T) extends Iterator[T] {
  private val row = new Row(rs)

  override def hasNext = rs.next()

  override def next() = f(row)
}

Вы также можете использовать Iterator для "понимания". И это дает вам доступ к API полной коллекции, например, к map и т.д.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню