스칼라 문서에 by name parameter라는 게 있다. 문법적인 의미라서 크게 와닿지는 않는다. 문법 정도만.. 



4.6.1 By-Name Parameters 


Syntax: ParamType ::= ‘=>’ Type The type of a value parameter may be prefixed by =>, e.g. x: => T . 


The type of such a parameter is then the parameterless method type => T . This indicates that However, at present singleton types of method parameters may only appear in the method body; so dependent method types are not supported. Basic Declarations and Definitions the corresponding argument is not evaluated at the point of function application, but instead is evaluated at each use within the function. 


That is, the argument is evaluated using call-by-name. The by-name modifier is disallowed for parameters of classes that carry a val or var prefix, including parameters of case classes for which a val prefix is implicitly generated.


The by-name modifier is also disallowed for implicit parameters (§7.2). Example 4.6.2 The declaration def whileLoop (cond: => Boolean) (stat: => Unit): Unit indicates that both parameters of whileLoop are evaluated using call-by-name.






by-name parameter는 함수를 매개변수로 보내고 해당 함수를 lazy evaluation을 할 수 있는 기법이다. 


더 쉽게 말하면 매개변수의 계산을 지연하고 싶어할 때 사용할 수 있다 .





아래 웹 페이지가 이해하는 데 많은 도움이 되었다.


https://groups.google.com/forum/#!topic/scala-korea/qyjq7IEdwRc

http://daily-scala.blogspot.kr/2009/12/by-name-parameter-to-function.html



def takesFunction(functionByName: () => Int) = println(functionByName())
takesFunction(() => 10)

by-name 매개변수는 :() => 형태를 가지고 있고, 결과는 10이다. 그리고 :() => 매개변수를 :=>으로 변경할 수 있고, 사용할 때는 깔끔하게 리턴 값만 넣을 수 있다.



def takesFunction(functionByName: => Int) = println(functionByName)
takesFunction(10)



래핑한 send 함수를 이용한다.


def takesFunction(functionByName: () => Int) = println(functionByName())
def send(x: => Int) = takesFunction(() => x)
send{10}


래핑한 send 함수를 사용해서 결과를 만들 수 있다. 

def takesAnotherFunction1(functionByName: => Int) = println(functionByName)
def sendAnother1(x: => Int) = takesAnotherFunction1(x)
sendAnother1{10}


다른 형태로도 사용할 수 있다. 


def takesAnotherFunction1(functionByName: => Int) 
                               = println(functionByName)
def sendAnother1(x: => Int) = takesAnotherFunction1(x)
sendAnother1{10}

def takesAnotherFunction2(functionByName: => Int) = println(functionByName)
def sendAnother2(x: => Int) = takesAnotherFunction2(x)
sendAnother2{10}



Posted by '김용환'
,



검색하다보니 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)
}



'scala' 카테고리의 다른 글

[scala] 이름에 의한 호출 매개변수 (by-name parameter)  (0) 2016.09.29
[scala] filter, exists 성능 주의  (0) 2016.09.29
[scala] @ 연산자  (0) 2016.09.28
[scala] vararg, _* 타입 어노테이션  (0) 2016.09.28
[scala] lazy, view  (0) 2016.09.28
Posted by '김용환'
,