[英]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.