3

いくつかのscalaタイプをHTML表現に変換するための変換ライブラリを作成しようとしています。List(1,2).toHtmlたとえば、を実行して取得<ul><li>1</li><li>2</li></ul>したいと思いStringます。
これまで、結果タイプの検出と正しいの適用にうまく機能する一連の暗黙的な変換を作成しましたtoHtml

サンプルを見せてみましょう:

object Conversions {

  implicit def fromIterable[A](l : Iterable[A]) = new Object {
       def toHtml = <ul> { l.map{ e => <li>{ e }</li> } } </ul> toString
  }
}
import Conversions._

このコードを使用すると、コンパイラーに問い合わせるたびList(1,2).toHtmlに、正しい変換が得られます。他のIterablevalの場合と同じように。

ここでの私の問題と質問は、このtoHtml変換を再帰的に使用するにはどうすればよいですか?を入力した場合List( List(1,2), List(3,4) ).toHtml、取得したいので<ul> <li><ul><li>1</li><li>2</li></ul></li> <li><ul><li>3</li><li>4</li></ul></li> </ul>toHtml変換は入力のすべての要素に再帰的に適用されますIterable

コンパイラが完全に理にかなっていると教えてくれたので、うまくいかなかったtoHtml定義を変更しようとしました。 私の問題はおそらく暗黙の定義から戻ってきていることにあることを知っています。それはおそらく特性か何かを持つクラスを返すはずです。 暗黙について多くのことを読んでみましたが、fromIterableシグネチャのパラメータを解除したり、そのようないくつかの特定のケースを定義したりせずに、この変換を再帰的に適用できることをまだ理解していません... def toHtml = <ul> { l.map{ e => <li>{ e.toHtml }</li> } } </ul> toStringvalue toHtml is not a member of type parameter A
new Object { ... }fromIterable[A]
toHtmlfromIterable(l : List[List[Any]])

それを達成する方法と私が間違っていることについていくつかアドバイスをください。

ありがとう!

4

2 に答える 2

7

この種の問題は、型クラスを使用してエレガントで型安全な方法で解決できます。

// Evidence that we can turn an A into some XML:
trait Markup[-A] { def toHtml(a: A): xml.Node }

def baseMarkup[A] = new Markup[A] { def toHtml(a: A) = xml.Text(a.toString) }

implicit def markup[A](implicit m: Markup[A] = baseMarkup[A]) =
  new Markup[Iterable[A]] {
    def toHtml(c: Iterable[A]) = <ul>
      { c.map(child => <li>{ m.toHtml(child) }</li>) }
    </ul>
  }

implicit def fromMarkup[A](a: A)(implicit m: Markup[A]) = new {
  def toHtml = m toHtml a
}

これは、任意の深さのネストされたリストに対して機能します。

val messy = List(List(List("a")), List(List("b", "c"), List("c")))

val printer = new xml.PrettyPrinter(80, 2)

その後:

scala> printer format messy.toHtml
res1: String = 
<ul>
  <li>
    <ul>
      <li>
        <ul>
          <li>a</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>
    <ul>
      <li>
        <ul>
          <li>b</li>
          <li>c</li>
        </ul>
      </li>
      <li>
        <ul>
          <li>c</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

上記の方法のより詳細な説明については、ここで私の答えを参照してください。

于 2012-12-02T19:58:15.753 に答える
4

まず、暗黙的/オブジェクトと変換のすべてを使わずにそれを行う方法を見てみましょう。私も削除しましたtoStringが、お気軽に追加してください。

def toHtml[A](l:Iterable[A]):scala.xml.Elem  = 
  <ul> {l.map( _ match{ 
    case e:Iterable[_] => <li>{toHtml(e)}</li> 
    case e => <li>{e}</li>
  })}</ul>

// Exiting paste mode, now interpreting.

toHtml: [A](l: Iterable[A])scala.xml.Elem
scala> toHtml(List(List(1,2), List(3,4)))
res17: scala.xml.Elem = <ul> <li><ul> <li>1</li><li>2</li></ul></li><li><ul> <li>3</li><li>4</li></ul></li></ul> 

あなたの質問に答えるために、今度はすべてを再ラップしましょう:

object Conversions {
  implicit def fromIterable[A](l : Iterable[A]):Object{def toHtml:xml.Elem} = new Object {
    def toHtml:xml.Elem  = 
      <ul>{l.map( _ match{ 
        case e:Iterable[_] => <li>{e.toHtml}</li> 
        case e => <li>{e}</li>
      })}</ul>
  }
}
import Conversions._

これにより、望ましい結果が得られます。

scala> List(List(1,2), List(3,4)).toHtml
res3: scala.xml.Elem = <ul><li><ul><li>1</li><li>2</li></ul></li><li><ul><li>3</li><li>4</li></ul></li></ul>

いくつかの意見:

  • 次に、「fromIterable」メソッドの戻りタイプを指定する必要がありました。より良い解決策があると確信していますが、暗黙の変換についてはそれほど経験がありません。
  • 最後に、誰かが提案を持っているなら、私は私の答えを編集してうれしいです。(または自由に編集してください)
于 2012-12-02T18:58:15.403 に答える