검색하다보니 filter와 exists 컬렉션 API에 대한 성능 주의 사항이 좀 있다.

그선.. 스크랩을 해둔다. 




1. filter

https://www.sumologic.com/blog-technology/3-tips-for-writing-performant-scala/


Using lazy collections must be taken with a grain of salt — while lazy collections often can improve performance, they can also make it worse.  For example:

def nonview = (1 to 5000000).map(_ % 10).filter(_ > 5).reduce(_ + _)
def view = (1 to 5000000).view.map(_ % 10).filter(_ > 5).reduce(_ + _)
view rawgistfile1.scala hosted with ❤ by GitHub

For this microbenchmark, the lazy version ran 1.5x faster than the strict version.  However, for smaller values of n, the strict version will run faster. Lazy evaluation requires the creation of an additional closure. If creating the closures takes longer than creating intermediate collections, the lazy version will run slower. Profile and understand your bottlenecks before optimizing!


filter가 새로운 콜렉션을 내부적으로 생성하기 때문에

view.filter를 사용하면 lazy 코드로 1.5배 빠르다고 한다.


또한, filter는 컬렉션을 모두 순회하는 linear time의 오퍼레이션이다.





2. exists



http://stackoverflow.com/questions/16443177/scala-which-data-structures-are-optimal-in-which-siutations-when-using-contai


With exists, you really just care about how fast the collection is to traverse--you have to traverse everything anyway. There, List is usually the champ (unless you want to traverse an array by hand), but only Set and so on are usually particularly bad (e.g. exists on List is ~8x faster than on a Set when each have 1000 elements). The others are within about 2.5x of List(usually 1.5x, but Vector has an underlying tree structure which is not all that fast to traverse).



exist는 컬렉션을 모두 순회하는 선형 시간의 오퍼레이션이다. 그런데, List.exists가 Set.exists보다 8배 이상 빠르다고 한다. 다른 컬렉션보다 Lists.exists가 1.5배에서 2.5배 빠르다고 한다.




저작자 표시
신고
Posted by 김용환 '김용환'



scala의 컬렉션에서는 정렬을 메소드로 지원한다. 


println(List(2000, 1000, 500, 3000).sorted)

결과는 다음과 같다.

List(500, 1000, 2000, 3000)




sorted 메소드 원형은 바로 Ordering 이라는 트레이트를 사용한다. 


def sorted[B >: A](implicit ord: Ordering[B]): Repr = {

Ordering 트레이트는 java.util.Comparator를 상속한다. 


trait Ordering[T] extends Comparator[T] 
                      with PartialOrdering[T] with Serializable {

Ordering에 대한 자세한 내용은 추후 살펴보고 우선 컬렉션 정렬을 살펴보자. 



sortWith 메소드에 함수 매개변수를 사용하면 더 풍부해진다. 


println(List(2000, 1000, 500, 3000).sortWith((x, y) => x > y))


println(List(2000, 1000, 500, 3000).sortWith(_ > _))

결과는 다음과 같다.


List(3000, 2000, 1000, 500)



함수 원형은 다음과 같다. sorted 메소드에 함수를 전달한다. 

def sortWith(lt: (A, A) => Boolean): Repr = sorted(Ordering fromLessThan lt)



sortWith에 _ > _ 대신 하나의 함수를 추가할 수 있다. 동일한 기능을 하는 함수를 만들어 전달한다. 

def sortByDesc(x: Int, y: Int) : Boolean = {
if (Ordering.Int.compare(x, y) == 1) true else false
}

println(List(2000, 1000, 500, 3000).sortWith(sortByDesc))

결과는 이전 예시의 결과와 동일하다.


List(3000, 2000, 1000, 500)




이제는 클래스 정렬 부분에 대한 정렬이다. 


List(Price(1), Price(2)).sorted를 실행할 수 없다. Price에 대한 implict Ordering이 없기 때문이다. 


implict을 잘 모르면,  List(Price(1), Price(2)).sortWith(_.value < _.value) 이렇게 개발해야 한다. 



Price에 대한 implicit Ordering을 추가할 수 있는 여러 방법이 있는 듯 하다. 


Price 오브젝트에 Ordering 타입의 implicit val을 따로 선언하는 방식을 사용해봤다. 


예시는 다음과 같다. 


case class Price(value: Int) extends AnyVal
object Price {
implicit val ordering: Ordering[Price] = new Ordering[Price] {
def compare(x: Price, y: Price): Int =
Ordering.Int.compare(y.value, x.value)
}
}

object Main extends App {
val list = List(Price(2000), Price(1000), Price(500), Price(3000))
println(list.sorted)
}


결과는 다음과 같다.


List(Price(500), Price(1000), Price(2000), Price(3000))






sortBy 메소드도 존재한다. 

def sortBy[B](f: A => B)(implicit ord: Ordering[B]): Repr = sorted(ord on f)

println(List(2000, 1000, 500, 3000).sortBy((x: Int) => x))


TreeSet과..
object TreeSet extends ImmutableSortedSetFactory[TreeSet] {
implicit def implicitBuilder[A](implicit ordering: Ordering[A]): Builder[A, TreeSet[A]] = newBuilder[A](ordering)
override def newBuilder[A](implicit ordering: Ordering[A]): Builder[A, TreeSet[A]] =
new SetBuilder(empty[A](ordering))


TreeMap에서 implicit으로 Ordering을 쓰고 있다. 


object TreeMap extends ImmutableSortedMapFactory[TreeMap] {
def empty[A, B](implicit ord: Ordering[A]) = new TreeMap[A, B]()(ord)
/** $sortedMapCanBuildFromInfo */
implicit def canBuildFrom[A, B](implicit ord: Ordering[A]): CanBuildFrom[Coll, (A, B), TreeMap[A, B]] = new SortedMapCanBuildFrom[A, B]
}




간단한 예시를 보면, 다음과 같다. 

println(TreeSet(6,3,2,1))
println(TreeSet(3->'c', 1->'a', 2->'b'))

println(TreeMap(3->'c', 1->'a', 2->'b'))


결과는 다음과 같다.


TreeSet(1, 2, 3, 6)

TreeSet((1,a), (2,b), (3,c))

Map(1 -> a, 2 -> b, 3 -> c)







TreeMap을 좀 더 살펴보면..

Ordering 클래스는 A 타입의 엘리먼트를 자연적인 정렬에 대한 규칙을 정의한 타입 클래스이다.

따라서, 순서대로 엘리먼트를 순회할 때 뛰어난 성능을 제공한다. 


class TreeMap[A, +B] private (tree: RB.Tree[A, B])
                             (implicit val ordering: Ordering[A])










Ordering은 Compartator 계열이고, 콜렉션에서 사용된다. 



Ordered는 Comparable 계열이고, 여러 클래스에서 묵시적으로 사용하고 있다.






클래스간에 비교를 하려면, 에러가 발생한다. 

Price(10) < Price(20)



이를 가능하게 하는 것이 Ordered이다. Price라는 케이스 클래스에 Ordered 트레이트를 상속받고 compare(x)를 구현한다. 바로 이때 Ordered 트레이트를 사용한다. 


case class Price(value: Int) extends Ordered[Price] {
def compare(x: Price): Int =
Ordering.Int.compare(value, x.value)
}


object Main extends App {
println(Price(10) < Price(100)) }


결과는 true가 발생한다!!


코드가 정말 간결해질 것 같다. 


if (Price(x) < Price(y)) {

  // code

}



참고로 Ordered 트레이트의 원형은 다음과 같다. - 이런건 안된다. 



trait Ordered[A] extends Any with java.lang.Comparable[A] {

/** Result of comparing `this` with operand `that`.
*
* Implement this method to determine how instances of A will be sorted.
*
* Returns `x` where:
*
* - `x < 0` when `this < that`
*
* - `x == 0` when `this == that`
*
* - `x > 0` when `this > that`
*
*/
def compare(that: A): Int

/** Returns true if `this` is less than `that`
*/
def < (that: A): Boolean = (this compare that) < 0

/** Returns true if `this` is greater than `that`.
*/
def > (that: A): Boolean = (this compare that) > 0

/** Returns true if `this` is less than or equal to `that`.
*/
def <= (that: A): Boolean = (this compare that) <= 0

/** Returns true if `this` is greater than or equal to `that`.
*/
def >= (that: A): Boolean = (this compare that) >= 0

/** Result of comparing `this` with operand `that`.
*/
def compareTo(that: A): Int = compare(that)
}



저작자 표시
신고
Posted by 김용환 '김용환'

[scala] @ 연산자

scala 2016.09.28 19:24



간단한 패턴 매칭 예시이다. 

case class Member(name: String)

object Main extends App {
val samuel = Member("samuel")
samuel match {

case Member(name) => println(name)
case _ => println("x")
}
}


결과는 다음과 같다.

samuel




패턴 매칭할 때, 객체를 통째로 받고 싶다면 어떻게 해야 할까?

이 때 @ 연산자를 사용한다. 



case class Member(name: String)

object Main extends App {
val samuel = Member("samuel")
samuel match {
case p @ Member(_) => println(p)
case Member(name) => println(name)
case _ => println("x")
}
}


결과는 다음과 같다. 


Member(samuel)





패턴매칭과 @가 합쳐진 예시이다. 



object Main extends App {

(Some(1), Some(2)) match {
case d @ (c @ Some(a), Some(b)) => {
println(a)
println(b)
println(c)
println(d)
}
}
println

val d @ (c @ Some(a), Some(b)) = (Some(1), Some(2))
println(a)
println(b)
println(c)
println(d)
println

for (x @ Some(y) <- Seq(None, Some(1))) {
println(x)
println(y)
}
println

val List(x, xs @ _*) = List(1, 2, 3)
println(x)
println(xs)
}


결과는 다음과 같다.


1

2

Some(1)

(Some(1),Some(2))


1

2

Some(1)

(Some(1),Some(2))


Some(1)

1


1

List(2, 3)




저작자 표시
신고
Posted by 김용환 '김용환'


티스토리 툴바