2011-03-18 3 views
3

스칼라 입문하지만 C++에서 익숙하다. sqlite4java 라이브러리 위에 작은 라이브러리를 구현하여 쿼리 행 (쿼리 행에서 임의의 유형의 튜플을 자동 채우기)을 허용하려고한다. 열 유형은 각각 해당 튜플 요소 유형과 호환 가능).스칼라 튜플 질문

C++에서는 보통 boost :: tuples와 컴파일 타임 템플릿 재귀를 사용하여 구현합니다 (아래에 표시된 템플릿 전문화를 사용하여 종료되었습니다). Boost 튜플은 Haskell HList와 매우 유사하게 구현됩니다. (- 그것이 무슨 뜻인지 보여줍니다 내가 컴파일러를 통해 위의 실행되지했지만 바라고 사과가)

template<typename T1, typename T2> 
void populateTuple(boost::tuples::cons<T1, T2>& tupleRec, int index, const std::vector<std::string>& vals) 
{ 
    tupleRec.head = boost::lexical_cast<T1>(vals[index]); 
    populateTuple(tupleRec.tail, index+1, vals); 
} 

template<typename T> 
void populateTuple(boost::tuples::cons<T, boost::tuples::null_type>& tupleRec, int index, const std::vector<std::string>& vals) 
{ 
    tupleRec.head = boost::lexical_cast<T>(vals[index]); 
} 

: 패턴은 (쿼리 문자열의 벡터로 반환됩니다 단순화를 위해, 가정을) 것

저는 Scala와 비슷한 것을 할 수 있기를 바랍니다. Product trait을 통해 일반 Tuple 유형을 반복 할 수 있으며 패턴 일치 (지원하는 소수 열 유형의 경우)를 사용하여 런타임시 각 요소의 유형을 가져올 수 있습니다. 그러나 제품 특성을 통해 Tuple 요소를 할당하는 방법을 찾지 못했습니다. 그리고 솔직히 말해서, 나는 이것이 내가 어쨌든 필요로하는 것을하기에 특히 좋거나 관용적 인 방법이라고 확신하지 못한다.

하지만 뭔가 같은 : rowAsTuples 입력 한

val returnedFromQuery = List[String]("Hello", "4", "6.0") 
val rowAsTuples = interpretListAsTuple[(String, Int, Float)](returnedFromQuery) 

(문자열, 지능은, 플로트). 다시 말하지만, 구문 오류를 용서하십시오.

누구든지 어떤 생각이 있습니까? 또는 대안 제안? 사전 - 나는 더 높은 수준의 SQL 쿼리 라이브러리에 관심이 없다. 나는 sqlite4java에 만족하지만 단순한보다 추상적 인 인터페이스로 그것을 포장하고 싶다.

답변

5

해석 대신 패턴 일치를 사용해보아야한다고 생각합니다.첫째, 우리는 unapply 사용하여 문자열에서 우리의 유형을 끌어 뭔가가 필요 :

이제
object Stringy { 
    def unapply(s: String) = Some(s) 
} 
object Inty { 
    def unapply(s: String) = { 
    try { Some(s.toInt) } 
    catch { case nfe: NumberFormatException => None } 
    } 
} 
object Floaty { 
    def unapply(s: String) = { 
    try { Some(s.toFloat) } 
    catch { case nfe: NumberFormatException => None } 
    } 
} 

우리는 패턴에서 사용할 수있는 것은 일치 :

scala> List("Hello","4","6.0") match { 
    case Stringy(s) :: Inty(i) :: Floaty(f) :: Nil => Some((s,i,f)) 
    case _ => None 
} 
res3: Option[(String, Int, Float)] = Some((Hello,4,6.0)) 

주 당신이 이런 식으로 할 경우 (1) 원하지 않으면 터플을 반환 할 필요가 없습니다. (2) 파싱 된 모든 변수에 즉시 액세스 할 수 있습니다. (3) 자동 오류 검사 기능이 내장되어 있습니다 (옵션 포함).

+0

내 대답보다 더 정교하게, 엄지 손가락으로! –

+0

이것은 깔끔한 대답입니다. 아마도 내가 간절히 원했던 것처럼 간결하지는 않지만 아마도 전체 효과를 얻으려면 HList가 필요합니다. 둘 다 고려할 것입니다. 감사! –

1

MetaScala 라이브러리에서 HList를 사용해보십시오.

+0

감사합니다. 그리고 나는 결국 수도 있습니다. 하지만 가능한 경우 표준 튜플 유형을 사용할 수 있는지 알고 싶습니다. –

1

나는 동적 튜플 인수에 대응 문제를 가지고 생각, 그래서 당신은, 그런 일 각 튜플 인수에 대응하는 방법을 구현해야한다 : 때문에 모호성,

def interpretListAsTuple2[A,B](s: List[String])(implicit s2a: String => A, s2b: String => B) = { 
    s.grouped(2).map { case x :: y => (x: A, y.head: B) } 
} 

def interpretListAsTuple3[A,B,C](s: List[String])(implicit s2a: String => A, s2b: String => B, s2c: String => C) = { 
    s.grouped(3).map { case x :: y :: z => (x: A, y: B, z.head: C) } 
} 

implicit def string2String(s: String) = s 
implicit def string2Int (s: String) = s.toInt 
implicit def string2Float (s: String) = s.toFloat 

val returnedFromQuery = List("Hello", "4", "6.0") 

interpretListAsTuple3[String,Int,Float](returnedFromQuery) 

죄송합니다,이 코드 나던 일을 각각 ~ float에 대한 암시 적 변환은 PredefLowPriorityImplicits입니다. 누군가가 도움을 줄 수 있습니다. 그러나 주요 아이디어는 분명해야합니다. 데이터 유형에 대해 암시 적 변환을 한 번 정의하면 모든 튜플 구문과 함께 작동합니다.

편집 :

당신은 여러 튜플의 문자열 목록을지도 위의 버전을 사용할 수 있습니다. List("Hello", "4", "6.0","Hey","1", "2.3") 당신이 하나의 튜플을 처리 할 경우 이것을 사용 : 물론

def interpretListAsTuple3[A,B,C](s: List[String])(implicit s2a: String => A, s2b: String => B, s2c: String => C): (A,B,C) = { 
    s.grouped(3).map { case x :: y :: z => (x: A, y: B, z.head: C) }.next 
} 

을 인수 잘 형성되어야한다.

0

AFAIK, 매개 변수 유형을 사용할 수 없습니다. 그 증거를 들어, 기능과 튜플 (22)

의 임의의 인수에 대응까지 각 인수에 대응하기위한 하나의 정의를 가지고 있다는 사실을 고려하면 HList 기존의 구현은 당신에게 다음, 무엇을 당신 둥지 타입 선언, 뭔가를 할 수 있습니다.

+0

MetaScala에는 안정성에 대한 건강 경고가 있습니다. HList는 아는 한 합리적으로 성숙합니까? 아니면 내 자신을 굴려야합니까? –

+0

@Alex 나는 결코 metascala를 사용하지 않았다. 일부 블로그에서는 구현 가능한 HList를 알고 있습니다. –

0

그래서 sqlite4java sqlite 인터페이스 주위에 간단한 래퍼를 작성하고 싶었 기 때문에이 질문을했습니다. 쿼리에서 행 유형이 준비된 문에 지정 될 수있는 다음과 같은 형식의 코드를 사용하려면 (I 비슷한 방법에 따라 전달 된 매개 변수에 유형 검사를 추가 할 예정) :

test("SQLite wrapper test") 
{ 
    val db = new SQLiteWrapper() 
    db.exec("BEGIN") 
    db.exec("CREATE TABLE test(number INTEGER, value FLOAT, name TEXT)") 

    val insStatement = db.prepare("INSERT INTO test VALUES(?, ?, ?)", HNil) 
    insStatement.exec(1, 5.0, "Hello1") 
    insStatement.exec(2, 6.0, "Hello2") 
    insStatement.exec(3, 7.0, "Hello3") 
    insStatement.exec(4, 8.0, "Hello4") 

    val getStatement = db.prepare("SELECT * from test", Col[Int]::Col[Double]::Col[String]::HNil) 
    assert(getStatement.step() === true) 
    assert(_1(getStatement.row) === Some(1)) 
    assert(_2(getStatement.row) === Some(5.0)) 
    assert(_3(getStatement.row) === Some("Hello1")) 

    getStatement.reset() 

    db.exec("ROLLBACK") 
} 

그리고이 가능하도록 , 도움이되는 다양한 제안을 사용하여 나는 아래의 코드를 생각 해냈다. 이것은 스칼라의 제네릭 프로그래밍의 모든 형태에 대한 첫 번째 시도입니다. 나는 단지 1, 2 주 동안 언어를 가지고 놀았습니다. 그래서 이것은 경험있는 스칼라 커뮤니티에 의해 코드가 좋고/좋다고 생각되지 않을 것입니다. 모든 제안/의견 환영 ... ....

import java.io.File 
import com.almworks.sqlite4java._ 

object SqliteWrapper 
{ 
    trait TypedCol[T] 
    { 
     var v : Option[T] = None 
     def assign(res : SQLiteStatement, index : Int) 
    } 

    sealed trait HList 
    { 
     def assign(res : SQLiteStatement, index : Int) 
    } 

    final case class HCons[H <: TypedCol[_], T <: HList](var head : H, tail : T) extends HList 
    { 
     def ::[T <: TypedCol[_]](v : T) = HCons(v, this) 
     def assign(res : SQLiteStatement, index : Int) 
     { 
      head.assign(res, index) 
      tail.assign(res, index+1) 
     } 
    } 

    final class HNil extends HList 
    { 
     def ::[T <: TypedCol[_]](v : T) = HCons(v, this) 
     def assign(res : SQLiteStatement, index : Int) 
     { 
     } 
    } 

    type ::[H <: TypedCol[_], T <: HList] = HCons[H, T] 

    val HNil = new HNil() 



    final class IntCol extends TypedCol[Int] 
    { 
     def assign(res : SQLiteStatement, index : Int) { v = Some(res.columnInt(index)) } 
    } 

    final class DoubleCol extends TypedCol[Double] 
    { 
     def assign(res : SQLiteStatement, index : Int) { v = Some(res.columnDouble(index)) } 
    } 

    final class StringCol extends TypedCol[String] 
    { 
     def assign(res : SQLiteStatement, index : Int) { v = Some(res.columnString(index)) } 
    } 

    trait TypedColMaker[T] 
    { 
     def build() : TypedCol[T] 
    } 

    object TypedColMaker 
    { 
     implicit object IntColMaker extends TypedColMaker[Int] 
     { 
      def build() : TypedCol[Int] = new IntCol() 
     } 
     implicit object DoubleColMaker extends TypedColMaker[Double] 
     { 
      def build() : TypedCol[Double] = new DoubleCol() 
     } 
     implicit object StringColMaker extends TypedColMaker[String] 
     { 
      def build() : TypedCol[String] = new StringCol() 
     } 
    } 

    def Col[T : TypedColMaker]() = implicitly[TypedColMaker[T]].build() 

    // Hideousness. Improve as Scala metaprogramming ability improves 
    def _1[H <: TypedCol[_], T <: HList](t : HCons[H, T]) = t.head.v 
    def _2[H1 <: TypedCol[_], H2 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, T]]) = t.tail.head.v 
    def _3[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, HCons[H3, T]]]) = t.tail.tail.head.v 
    def _4[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, T]]]]) = t.tail.tail.tail.head.v 
    def _5[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], H5 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, HCons[H5, T]]]]]) = t.tail.tail.tail.tail.head.v 
    def _6[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], H5 <: TypedCol[_], H6 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, HCons[H5, HCons[H6, T]]]]]]) = t.tail.tail.tail.tail.tail.head.v 
    def _7[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], H5 <: TypedCol[_], H6 <: TypedCol[_], H7 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, HCons[H5, HCons[H6, HCons[H7, T]]]]]]]) = t.tail.tail.tail.tail.tail.tail.head.v 
    def _8[H1 <: TypedCol[_], H2 <: TypedCol[_], H3 <: TypedCol[_], H4 <: TypedCol[_], H5 <: TypedCol[_], H6 <: TypedCol[_], H7 <: TypedCol[_], H8 <: TypedCol[_], T <: HList](t : HCons[H1, HCons[H2, HCons[H3, HCons[H4, HCons[H5, HCons[H6, HCons[H7, HCons[H8, T]]]]]]]]) = t.tail.tail.tail.tail.tail.tail.tail.head.v 

    final class DataWrapper[T <: HList](var row : T) 
    { 
     def assign(res : SQLiteStatement) { row.assign(res, 0) } 
    } 

    final class SQLiteWrapper(dbFile : File) 
    { 
     val conn = new SQLiteConnection(dbFile) 
     conn.open() 

     def exec(statement : String) 
     { 
      conn.exec(statement) 
     } 

     def prepare[T <: HList](query : String, row : T) = 
     { 
      new PreparedStatement(query, row) 
     } 

     // TODO: Parameterise with tuple type 
     // make applicable to for comprehensions (implement filter, map, flatMap) 
     final class PreparedStatement[T <: HList](query : String, var row : T) 
     { 
      val statement = conn.prepare(query) 

      private def bindRec(index : Int, params : List[Any]) 
      { 
       println("Value " + params.head) 
       // TODO: Does this need a pattern match? 
       params.head match 
       { 
        case v : Int => statement.bind(index, v) 
        case v : String => statement.bind(index, v) 
        case v : Double => statement.bind(index, v) 
        case _ => throw new ClassCastException("Unsupported type in bind.") 
       } 

       if (params.tail != Nil) 
       { 
        bindRec(index+1, params.tail) 
       } 
      } 

      def bind(args : Any*) 
      { 
       bindRec(1, args.toList) 
      } 

      def exec(args : Any*) 
      { 
       bindRec(1, args.toList) 
       step() 
       reset() 
      } 

      def reset() 
      { 
       statement.reset() 
      } 

      def step() : Boolean = 
      { 
       val success = statement.step() 
       row.assign(statement, 0) 
       return success 
      } 
     } 
    } 
}