2016-09-20 4 views
7

zurück. Ich habe ein HList, in dem jede Spalte eine Spalte einer Tabelle darstellt. Jede Liste im HList ist gleich lang.Wählen Sie das N-te Element eines HLists von Listen aus, und geben Sie diesen Wert als HList der Werte

Ich möchte in der Lage sein, eine Funktion zu schreiben, die einzelne Zeilen dieser Tabelle als ein Tupel oder ein HList der Werte auswählt. Letztendlich werde ich dies in etwas Sinnvolleres umwandeln (z.B. eine Fallklasse).

import shapeless.PolyDefns.~> 
import shapeless.{HList, HNil} 
val a = List(1,2,3) :: List("a", "b", "c") :: List(true, false, true) :: HNil 

object broken extends (HList ~> HList) { 
    def apply[T](n:Int, l:HList): HList = { 
    // I want to pick out the nth element of each HList 
    // so in the above example, if n==1 
    // I want to return 
    // 2 :: "b" :: false :: HNil 
    ??? 
    } 
} 

broken(1,a) 

Kann ich diese Funktion reparieren, so dass sie entsprechend den in den Kommentaren beschriebenen Funktionen funktioniert?

Bonuspunkte: Kann ich dies als Iterator schreiben, der meine HList "a" oben in eine Sequenz von (Int, String, Boolean) oder einem äquivalenten HList transformiert?

Antwort

7

Es gibt eine Reihe von Möglichkeiten, wie Sie dies tun könnte, aber ich mit einem benutzerdefinierten Typ Klasse gehen würde:

import shapeless._ 

trait RowSelect[L <: HList] extends DepFn2[L, Int] { 
    type Row <: HList 
    type Out = Option[Row] 
} 

object RowSelect { 
    def select[L <: HList](l: L, i: Int)(implicit rs: RowSelect[L]): rs.Out = rs(l, i) 

    type Aux[L <: HList, Row0 <: HList] = RowSelect[L] { type Row = Row0 } 

    implicit val hnilRowSelect: Aux[HNil, HNil] = new RowSelect[HNil] { 
    type Row = HNil 
    def apply(l: HNil, i: Int): Option[HNil] = Some(HNil) 
    } 

    implicit def hconsRowSelect[A, T <: HList](implicit 
    trs: RowSelect[T] 
): Aux[List[A] :: T, A :: trs.Row] = new RowSelect[List[A] :: T] { 
    type Row = A :: trs.Row 
    def apply(l: List[A] :: T, i: Int): Option[A :: trs.Row] = for { 
     h <- l.head.lift(i) 
     t <- trs(l.tail, i) 
    } yield h :: t 
    } 
} 

die wie folgt funktioniert:

scala> println(RowSelect.select(a, 0)) 
Some(1 :: a :: true :: HNil) 

scala> println(RowSelect.select(a, 1)) 
Some(2 :: b :: false :: HNil) 

scala> println(RowSelect.select(a, 2)) 
Some(3 :: c :: true :: HNil) 

scala> println(RowSelect.select(a, 3)) 
None 

A RowSelect Instanz für L Zeugen, dass L ist eine Hlist mit allen List Elemente, und bietet eine Operation, die optional das Element an einem angegebenen Index von jedem List auswählt.

Sie sollten die gleiche Sache mit NatTRel oder einer Kombination aus ConstMapper und ZipWith und einem Poly2, sondern eine individuelle Typklasse bündelt alles gut zusammen und erlaubt in vielen Fällen bequemer Kompositionalität bewerkstelligen können.

Zum Beispiel, in diesem Fall die Lösung Ihrer Bonus-Frage ziemlich werden kann in Bezug auf RowSelect ohne weiteres geschrieben:

def allRows[L <: HList](l: L)(implicit rs: RowSelect[L]): List[rs.Row] = 
    Stream.from(0).map(rs(l, _).toList).takeWhile(_.nonEmpty).flatten.toList 

Und dann:

scala> allRows(a).foreach(println) 
1 :: a :: true :: HNil 
2 :: b :: false :: HNil 
3 :: c :: true :: HNil 

Und ähnlich, wenn Sie konvertieren möchten diese hlists zu Tupeln:

def allRows[L <: HList, R <: HList](l: L)(implicit 
    rs: RowSelect.Aux[L, R], 
    tp: ops.hlist.Tupler[R] 
): List[tp.Out] = 
    Stream.from(0).map(rs(l, _).map(tp(_)).toList).takeWhile(_.nonEmpty).flatten.toList 

Welche gibt yo u:

scala> allRows(a) 
res7: List[(Int, String, Boolean)] = List((1,a,true), (2,b,false), (3,c,true)) 

Und so weiter.

+0

Einige verwandte Fragen zu polymorphen Funktionen: http://stackoverflow.com/questions/39628068/passing-an-extra-argument-into-a-polymorphe-function http://stackoverflow.com/questions/39628022/using -a-polymorphe Funktion zum Extrahieren eines Objekts aus Optionen –

Verwandte Themen