簡體   English   中英

Scala優雅列表理解如F#

[英]Scala elegant list comprehension as in F#

只需使用基本的JDBC接口即可使用Scala讀取一些數據。 在F#中(使用System.Data.SqlClient命名空間),我們可以執行類似這樣的操作,從數據庫返回不可變列表。

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

在Scala中,這被證明更加困難,據我所知,沒有像F#這樣的“同時”理解。 實際上,我想在Scala中做一些接近F#的事情,而不必使用可變變量 - 如果只是因為它們看起來很難看並添加到代碼行。

這樣的東西現在似乎在我的Scala代碼中很常見:

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

我將創建一個自定義的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

您可以在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方法是將您的Reader轉換為Iterator

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)

您可以將隱式類與隱式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 comprehension使用引擎蓋下的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方法添加到基礎結果集。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM