1

メッセージに変数のマージを実装するクラスを Ruby から Scala 2.11 に移植しています。オブジェクトの配列を merge メソッドに渡し、メッセージのテキストで参照されているキーを各オブジェクトで検索させたいと思います。

このコアは、検索する単一のキーと、キーを検索する単一のオブジェクトを受け取る lookUp(key: String, obj: AnyRef) と呼ばれるメソッドです。オブジェクトが Map で、Map のキーが Symbol または String の場合、要求されたキーを Map で探します。それ以外の場合は、オブジェクトにキーと同じ名前のメソッドがあるかどうかを確認し、ある場合はそのメソッドを呼び出します。

既存の Ruby コードでは、これを行うのは簡単です。

def look_up(key, obj)
  if obj.respond_to?(:has_key?) && obj.has_key?(key)
    obj[key]
  elsif obj.respond_to?(key)
    obj.send(key)
  elsif obj.instance_variable_defined?(ivar = "@#{key}")
    obj.instance_variable_get(ivar)
  end
end

簡単に実行できるので、Ruby コードは同じ名前のインスタンス変数も探します。私の Scala バージョンでは必ずしもその機能が必要なわけではありません。

私が抱えている問題の 1 つは、私が見つけた例では、呼び出すことができるようにオブジェクトのクラスを事前に知っている必要があることですru.typeOf[MyClass].declaration(ru.TermName("key"))(where ruis scala.reflect.runtime.universe)。

もう 1 つの問題は、このメッセージのマージが 1 分間に何百回も発生する可能性があり、リフレクションが遅く複雑なプロセスのように見えることです。これがすべて計画どおりに機能する場合、オブジェクト タイプごとにリフレクションの結果をキャッシュする可能性があります。

更新: このようなことを考えていましたが、これはやり過ぎですか? または、マップ内のタイプを適切にキャプチャする必要がありますか? また、これはコンパイルされません。MapSymbolおよびStringそれらのコンテキストに適したタイプではありません。

def lookUp[T](obj: T, key: String)(implicit tag: ru.TypeTag[T]): Option[String] = tag.tpe match {
  case ru.TypeRef(a, Map, List(Symbol, _)) => if (obj.contains(Symbol(key))) Some(obj(Symbol(key)).toString) else None
  case ru.TypeRef(a, Map, List(String, _)) => if (obj.contains(key)) Some(obj(key).toString) else None
  case _ =>
    if (/* obj.key() exists */)
      // Some(obj.key().toString)
    else
      None
}

asInstanceOf更新 2:のようなもので使用できるとは思いもしませんでしたMap[String, _]。@johny の 2 番目のコード例を使用して、ソリューションを考え出しました。メソッド名をクラスごとに にキャッシュしmutable.HashMap[Class[_], Set[String]]ます。

def lookUp(obj: AnyRef, key: String): Option[String] = obj match {
  case m: Map[_, _] =>
    if (m.asInstanceOf[Map[String, _]].contains(key))
      extractValue(m.asInstanceOf[Map[String, _]](key))
    else if (m.asInstanceOf[Map[Symbol, _]].contains(Symbol(key)))
      extractValue(m.asInstanceOf[Map[Symbol, _]](Symbol(key)))
    else
      None
  case _ =>
    val klass = obj.getClass
    if (!methodsCache.contains(klass))
      methodsCache(klass) = klass.getMethods.toList.filter(_.getParameterCount == 0).map(_.getName).toSet
    val methodNames = methodsCache(klass)
    if (methodsCache(klass).contains(key))
      extractValue(klass.getDeclaredMethod(key).invoke(obj))
    else
      None
}

def extractValue(obj: Any): Option[String] = obj match {
  case null | None => None
  case Some(x) => Some(x.toString)
  case x => Some(x.toString)
}
4

1 に答える 1

0
 def lookUp(key: String, obj: AnyRef) {
    obj match {
      case x: Map[String, _] => x(key)
      case _                 => obj.getClass.getDeclaredMethod(key).invoke(obj)
    }
  }

編集: Map のキーが String または scala.Symbol であることを確認するには

def lookUp(key: String, obj: AnyRef)= {
    obj match {
      case x: Map[_, _]  => 
        if(x.asInstanceOf[Map[String,_]].contains(key))
          x.asInstanceOf[Map[String,_]].get(key)
        else if(x.asInstanceOf[Map[scala.Symbol,_]].contains(scala.Symbol(key)))
          x.asInstanceOf[Map[scala.Symbol,_]].get(scala.Symbol(key))
        else
          None

      case _ => Some(obj.getClass.getDeclaredMethod(key).invoke(obj))
    }
  }

上記も、返された出力が意図したものであることを確認しませんMap

編集:@JimCainのリードに従う

def lookUp[T:ru.TypeTag](obj: T, key: String): Option[Any] = ru.typeTag[T].tpe match {
    case ru.TypeRef(a, m, l) if(m.name.toString=="Map"&&l.head =:= ru.typeOf[java.lang.String])=> obj.asInstanceOf[Map[String,_]].get(key)
    case ru.TypeRef(a, m,l) if(m.name.toString=="Map"&&l.head =:= ru.typeOf[Symbol])=> obj.asInstanceOf[Map[Symbol,_]].get(scala.Symbol(key))
    case _ => Try(obj.getClass.getDeclaredMethod(key).invoke(obj)) match {
      case Success(x) => Some(x)
      case Failure(_) => None
    }
  }
于 2015-11-28T18:56:14.743 に答える