2013-05-29 4 views
1

는이 같은 방법을 썼다 :스칼라 : 선택적 형식 매개 변수?

def typeOnly[T, S](seq: Seq[S]): Seq[T] = { 
    seq.flatMap{ 
    case t: T => Some(t) 
    case _ => None 
    } 
} 

을 그리고 나는 이런 식으로 호출 할 수 있도록 노력하겠습니다 : 그것은 작동하지 않습니다

typeOnly[String](List(1, "2", 3, "4")) 

.

typeOnly[String, Any](List(1, "2", 3, "4")) 

그런데 왜 : 나는 두 번째 유형 매개 변수 S를 지정해야 할 것 같다? 컴파일러는 List(1, "2", 3, "4")Seq[Any]이라는 사실을 알고 있어야하지 않습니까?

+3

컴파일러 ** ** 사실을 추론 할 수있다 목록 (1, "2", 3 '이 " 4 ")'는'Seq [Any]'이지만, 당신이 실수로 두 번째 유형을 지정하는 것을 잊었다 고 생각할 때 살 수는 없습니다. Seq 유형은 런타임에 지워지기 때문에 일치가 쓸모 없다는 점에 유의하십시오. –

+0

글쎄, 네 말이 맞아. 클래스 유형으로이 메소드를 다시 작성할 수 있습니까? –

+0

타입 안전을 기꺼이 원한다면'Lists '대신에'HLists'를 사용하는 것을 고려해보십시오. [shapeless 라이브러리] (https://github.com/milessabin/shapeless/)에서 구현하면 원하는 것을 수행하는 메서드 ('filter')가 있습니다. [예] (https://github.com/milessabin/shapeless/blob/master/core/src/test/scala/shapeless/hlist.scala#L604-L632). – folone

답변

5

TL; DR 이것은 스칼라 2.9.x위한

사용 스칼라 2.10.x위한

def typeOnly[T](seq : Seq[Any])(implicit m : Manifest[T]) : Seq[T] = { 
    seq.collect { 
     case t if m.erasure.isInstance(t) => t.asInstanceOf[T] 
    } 
} 

scala> typeOnly[String](List(1,2,"3",4)) 
res1: Seq[String] = List(3)  

이 : Seq로서

def typeOnly[T](seq : Seq[Any])(implicit tag : scala.reflect.ClassTag[T]) = { 
    seq.collect { 
     case t if tag.runtimeClass.isInstance(t) => t.asInstanceOf[T] 
    } 
} 

trait Seq[+A]로 정의 (키는 +) Seq[S]도입니다..

반면에 이미 말했듯이 T는 함수가 컴파일 될 때 "잊혀진"것이므로 직접 사용할 수 없습니다. Class of T를 매개 변수로 전달해야합니다. 스칼라

def typeOnly[T](seq : Seq[Any], c : Class[T]) : Seq[T] = { 
    seq.flatMap { 
     case t if c.isInstance(t) => Some(t.asInstanceOf[T]) 
     case _ => None 
    } 
} 

, 아파트에서 Class[T], 결과적으로 더 관용적 약간 더 강력하고, 이는 또한 Manifest[T]있다. 특히 Class[T]을 반환하는 erasure 메서드가 있습니다. 그것을 사용하면 가서 같이 함수를 작성할 수

def typeOnly[T](seq : Seq[Any], m : Manifest[T]) : Seq[T] = { 
    seq.flatMap { 
     case t if m.erasure.isInstance(t) => Some(t.asInstanceOf[T]) 
     case _ => None 
    } 
} 

우리가 아무것도 얻은없는 것 같다. 그러나 컴파일러에게 (implicit을 사용하여) 멋지게 질문하면 함수를 호출 할 때 Manifest이 전달됩니다.

def typeOnly[T](seq : Seq[Any])(implicit m : Manifest[T]) : Seq[T] = { 
    seq.flatMap { 
     case t if m.erasure.isInstance(t) => Some(t.asInstanceOf[T]) 
     case _ => None 
    } 
} 

예 :

scala> typeOnly[java.lang.Integer](List(1,2,"3",4)) 
res2: Seq[java.lang.Integer] = List(1, 2, 4) 

scala> typeOnly[String](List(1,2,"3",4)) 
res3: Seq[String] = List(3) 

scala> typeOnly[java.lang.Double](List(1,2,"3",4)) 
res4: Seq[java.lang.Double] = List() 

은 그들 중 일부는 아직 더 관용적 더 많은 대안이있다. 당신은, 예를 들어, 는 부분적으로 정의 기능을 collect을 사용할 수

def typeOnly[T](seq : Seq[Any])(implicit m : Manifest[T]) : Seq[T] = { 
    seq.collect { 
     case t if m.erasure.isInstance(t) => t.asInstanceOf[T] 
    } 
} 

경고을 : 전 예는 다음과 에서 스칼라 2.9.3와 작동합니다. 스칼라 2.10.x 용으로 개발중인 경우 Manifest#erasure은 사용되지 않습니다. 대신 runtimeClass를 사용하여 매니페스트가 곧 (아래에 의견을 참조)도 더 이상 사용되지 않습니다

def typeOnly[T](seq : Seq[Any])(implicit m : Manifest[T]) = { 
    seq.collect { 
     case t if m.runtimeClass.isInstance(t) => t.asInstanceOf[T] 
    } 
} 

, 당신은 ClassTag을 사용하는 것이 좋습니다.

def typeOnly[T](seq : Seq[Any])(implicit tag : scala.reflect.ClassTag[T]) = { 
    seq.collect { 
     case t if tag.runtimeClass.isInstance(t) => t.asInstanceOf[T] 
    } 
} 
+0

스칼라 2.10에서는'Manifest '보다'TypeTag'를 선호한다는 것을 기억하십시오. 'Manifest'는 더 이상 사용되지 않을 예정입니다. http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html#typetags_and_manifests – gzm0

+0

잘 알고 있습니다. 고쳤다. – vlopez

2

귀하의 case t: T => Some(t) 유형 T하여 요소를 필터링하지 않습니다.

def typeOnly[T](seq: Seq[Any], clazz: Class[T] = classOf[Any]): Seq[T] = { 
    seq.flatMap{ 
     case t: T if clazz.isInstance(t) => Some(t) 
     case _ => None 
    } 
    } 

이 같은 호출 :

typeOnly(Seq(1, "2", 3)) 
typeOnly(Seq(1, "2", 3), classOf[String]) 
0

당신이 typeOnly 사용자 만 T 형식 매개 변수를 지정할 수 목표로하는 경우, 당신이이를 정의 할 수 있습니다 당신이 필요로하는 것은이 같은 것입니다 방법 : 당신은 것

def typeOnly[S](seq: Seq[S]) = new { 
    def apply[T]: Seq[T] = 
    seq.flatMap{ 
     case t: T => Some(t) 
     case _ => None 
    } 
} 

그것을 호출하는이 방법

val l = typeOnly(List(1, "2", 3, "4"))[String] 

(typeOnly 직후 대신 인수 뒤에 type 매개 변수가 있음).

구조 유형의 특정 용도로 인해 컴파일러 경고가 추가됩니다. 당신이 import language.reflectiveCalls 그 문제를 해결하기 위해 추가하지하려면, 당신은 중간 클래스에 의존 할 수

case class TypeOnlyFilter[S](seq: Seq[S]) { 
    def apply[T]: Seq[T] = 
    seq.flatMap{ 
     case t: T => Some(t) 
     case _ => None 
    } 
} 

def typeOnly[S](seq: Seq[S]) = 
    new TypeOnlyFilter(seq) 

참고이 구현은 아직 선택하지 않은 추상 형식 패턴 T에 대해 경고 컴파일러를 가지고 (원래의 구현처럼) . 당신은 최종 솔루션처럼 보이도록, 그 문제를 해결하는 유형 T에 대한 ClassTag을 요구해야한다 :

import scala.reflect.ClassTag 

case class TypeOnlyFilter[S](seq: Seq[S]) { 
    def apply[T: ClassTag]: Seq[T] = 
    seq.flatMap{ 
     case t: T => Some(t) 
     case _ => None 
    } 
} 

def typeOnly[S](seq: Seq[S]) = 
    new TypeOnlyFilter(seq)