8

只需使用基本的 JDBC 接口使用 Scala 读取一些数据。在 F#(使用 System.Data.SqlClient 命名空间)中,我们可以执行类似的操作以从数据库返回一个不可变列表。

    let rs = cmd.ExecuteReader()
    [while rs.Read() do yield rs.GetInt32(1)]

在 Scala 中,这被证明更加困难,据我所知,没有像 F# 这样的“while”理解。实际上,我想在 Scala 中做一些接近 F# 的事情,而不必使用可变变量——如果只是因为它们看起来很难看并添加到代码行中。

现在,在我的 Scala 代码中,这样的事情似乎很常见:

 var result = Seq.empty[Int]
 val rs = stmt.executeQuery()
 while (rs.next()) {
     result = result :+ rs.getInt(1) }
4

3 回答 3

9

我将创建一个Iterator包含查询结果的自定义子类。这真的很容易;塞尼亚展示了如何。

但你也可以

val rs = stmt.executeQuery
val it = Iterator.continually(if (rs.next()) Some(rs.getInt(1)) else None)
val result = it.takeWhile(_.isDefined).toList.flatten
于 2013-06-24T14:26:15.007 回答
7

您可以在 scala 中使用相同的方式,但我认为它很难看:

class Reader(var i: Int){
  def read = { i-=1; i > 0 }
  def getInt32 = i
}

val r = new Reader(10)
Stream.from(0).takeWhile{ _ => r.read}.map{ _ => r.getInt32}.toList
// List(9, 8, 7, 6, 5, 4, 3, 2, 1)

惯用的scala方式是将您转换ReaderIterator

implicit class ReaderIterator(r: Reader) extends Iterator[Int] {
  def hasNext = r.read
  def next = r.getInt32
}

scala> new Reader(10).toList
res0: List[Int] = List(9, 8, 7, 6, 5, 4, 3, 2, 1)

但是如果你真的错过了这个语法,你可以添加它:

import scala.collection.immutable.VectorBuilder
class FWhile(c: => Boolean){
  def apply[T](e: => T): Seq[T] = {
    val b = new VectorBuilder[T]
    while (c) b += e
    b.result
  }
}
object FWhile{
  def apply(c: => Boolean) = new FWhile(c)
}

scala> FWhile(r.read){r.getInt32}
res0: Seq[Int] = Vector(9, 8, 7, 6, 5, 4, 3, 2, 1)
于 2013-06-24T14:17:24.293 回答
5

您可以将隐式类与隐式CanBuildFrom. 这确实使用了可变构建器,但不在调用方:

object MyResultSetContainer {
  implicit class MyResultSet(rs: ResultSet) {
    def map[T, C <: Iterable[T]](f: (ResultSet) => T)
                               (implicit cbf: CanBuildFrom[Nothing, T, C]): C = {
      val builder = cbf()
      while (rs.next()) {
        builder += f(rs)
      }
      builder.result()
    }
  }
}

像这样使用:

import MyResultSetContainer._
val rs = stmnt.executeQuery("select * from pg_user")

val names = for (row <- rs) yield (row.getString(1))

println(names)
rs.close()

for 理解map在幕后使用,所以如果你更喜欢map直接:

val names = rs.map(row => row.getString(1))

产生一个序列。多亏了CanBuildFrom您,您也可以通过显式提供类型来生成其他集合:

val names: List[String] = rs.map(row => row.getString(1))

如何CanBuildFrom工作?Scala 编译器查看此表达式中涉及的类型:有结果类型,以及 map 调用的函数返回的类型。基于此信息,Scala 编译器隐式提供了一个工厂,可用于创建合适的构建器。因此,您只需要一种方法来生成不同类型的集合。

如果要返回多个值,只需返回一个元组:

val columns = rs.map(row => (row.getInt(2), row.getString(1)))

元组可用于Map直接创建:

val keyNamesMap: Map[Int, String] = 
  rs.map(row => (row.getInt(2), row.getString(1)))

这是基于结果集是行列表的想法,因此该map函数应该在它之上可用。隐式类用于将map方法隐式添加到底层结果集中。

于 2013-06-24T14:54:07.267 回答