Scala优雅列表理解如F#

Ric*_*odd 8 scala list-comprehension jdbc

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

    let rs = cmd.ExecuteReader()
    [while rs.Read() do yield rs.GetInt32(1)]
Run Code Online (Sandbox Code Playgroud)

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

这样的东西现在似乎在我的Scala代码中很常见:

 var result = Seq.empty[Int]
 val rs = stmt.executeQuery()
 while (rs.next()) {
     result = result :+ rs.getInt(1) }
Run Code Online (Sandbox Code Playgroud)

Rex*_*err 9

我会创建一个Iterator包装查询结果的自定义子类.这真的很容易; senia展示了如何.

但你也可以

val rs = stmt.executeQuery
val it = Iterator.continually(if (rs.next()) Some(rs.getInt(1)) else None)
val result = it.takeWhile(_.isDefined).toList.flatten
Run Code Online (Sandbox Code Playgroud)


sen*_*nia 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)
Run Code Online (Sandbox Code Playgroud)

惯用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)
Run Code Online (Sandbox Code Playgroud)

但如果您真的错过了这种语法,可以添加它:

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)
Run Code Online (Sandbox Code Playgroud)


Ber*_*ium 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()
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用:

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

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

println(names)
rs.close()
Run Code Online (Sandbox Code Playgroud)

for comprehension使用map引擎盖,所以如果你喜欢map直接:

val names = rs.map(row => row.getString(1))
Run Code Online (Sandbox Code Playgroud)

产生一个序列.由于CanBuildFrom您可以通过明确提供类型来生成其他集合:

val names: List[String] = rs.map(row => row.getString(1))
Run Code Online (Sandbox Code Playgroud)

CanBuildFrom工作怎么样?Scala编译器会查看此表达式中涉及的类型:结果类型和map调用的函数返回的类型.根据此信息,Scala编译器隐式提供工厂,可用于创建合适的构建器.因此,您只需要一种方法来生成不同类型的集合.

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

val columns = rs.map(row => (row.getInt(2), row.getString(1)))
Run Code Online (Sandbox Code Playgroud)

并且元组可以用于Map直接创建:

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

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