스칼라 문서에 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 '김용환'
,

[scala] @ 연산자

scala 2016. 9. 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 '김용환'
,



scala에서 vararg에 대한 처리 내용을 설명한다.


object Objective {
def printAll(values: String *): Unit = {
values.foreach(print)
}
}

object Main extends App {
Objective.printAll("aaaa", "bbbbbb")
println()
val vals = List("aaaa", "bbbbbb")
Objective.printAll(vals : _*)
}

결과는 다음과 같다.

aaaabbbbbb

aaaabbbbbb




String* 과 같은 표현으로 String 타입의 vararg를 정의하고,

호출할 쪽에서는 List 컬렉션을 : _*으로 바꿔 전달했다. 


자바 개발자에게는 황당할 수 있지만, Scala Language Specification의 4.6.2 Repeated Parameters를 살펴보면, 관련 내용이 나오고 있다. _*를 말할 때 (_* 타입 어노테이션)라고 한다. 

Smooch operator 또는 unpacker라고 하기도 한다.






조금 더 수정해서 Any, Any*를 매개변수를 받는다고 가정했을 때, List :: 오퍼레이터를 사용한 사례이다. 

object Objective {
def printAll(head: Any, values: Any *): Unit = {
(head :: values.toList).toArray.foreach(print)
}
}

object Main extends App {
Objective.printAll("xxx", "aaaa", "bbbbbb")
println()
val vals = List("aaaa", "bbbbbb")
Objective.printAll("xxx", vals : _*)
}


Posted by '김용환'
,

[scala] lazy, view

scala 2016. 9. 28. 17:36


아래 값을 계산하면 당연 6이 출력된다.

var a = 3
val b = a + 3
a += 1
println(b)



스칼라에서 늦은 초기화(laziness)를 지원하며, lazy 지시자를 사용하면 호출되는 순간 값이 초기화된다.

var a = 3
lazy val b = a + 3
a += 1
println(b)


결과는 7이다.





상속 구조의 클래스에서도 사용할 수 있다. 


abstract class Parent {
val value: String
println ("value : " + value.length)
}

object Child extends Parent { lazy val value = "Hello" }



lazy를 사용하지 많으면 NPE가 발생하지만, lazy를 통해서 5라는 결과를 얻을 수 있다. 






스칼라의 lazy 언어 기능을 사용한 것이 바로 컬렉션의 view이다. 


println((1 to 3).view)
println((1 to 3).view.force)


결과는 다음과 같다.


SeqView(...)

Vector(1, 2, 3)



view메소드만 호출하면 값이 결정되지 않은 SeqView만 리턴한다.

view의 정의를 보면, 다음과 같다. lazy로 인스턴스를 그제서야 실행한다. 


override def view = new SeqView[A, Repr] {
protected lazy val underlying = self.repr
override def iterator = self.iterator
override def length = self.length
override def apply(idx: Int) = self.apply(idx)
}

컬렉션 메소드는 strict한 transformer가 있고, lazy한 transformer로 구현된 메소드로 나뉜다.  view는 이 lazy를 이용한다. '



또한, strict한 메소드를 사용하면 일련의 객체를 메모리에 계속 쌓지만, view는 함수적으로 transformer를 잘 사용한다. view를 이용하면, 메모리잘 잘 아낄 수 있다. 



scala> (1 to 1000000000).filter(_ % 2 == 0).take(10).toList

java.lang.OutOfMemoryError: GC overhead limit exceeded

  at java.lang.Integer.valueOf(Integer.java:832)

  at scala.runtime.BoxesRunTime.boxToInteger(BoxesRunTime.java:65)

  at scala.collection.immutable.Range.foreach(Range.scala:160)

  at scala.collection.TraversableLike$class.filterImpl(TraversableLike.scala:247)

  at scala.collection.TraversableLike$class.filter(TraversableLike.scala:259)

  at scala.collection.AbstractTraversable.filter(Traversable.scala:104)

  ... 26 elided


scala> (1 to 1000000000).view.filter(_ % 2 == 0).take(10).toList

res1: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)





view는 모듈이나 라이브러리, DAO 레이어 객체에서 많이 사용될 수 있다.






Posted by '김용환'
,


Unit은 아무 것도 리턴하지 않음을 의미하는 메소드 리턴 타입이다. 


def run(): Unit = {
}


Unit은 AnyVal을 상속했다.

final abstract class Unit private extends AnyVal 




null은 자바의 null과 동일하지만, 

Null은 null이라는 값을 가진 trait이며, 타입 매개변수를 제약하는 데 쓰인다. 


implicit def nullMember[B >: Null] = new Member[B] { def apply = null }




Nothing은 trait이며, Option의 타입 매개변수로 쓰인다. 그리고 인스턴스가 없다. 

case object None extends Option[Nothing] {



Nothing은 모든 클래스를 상속받을 수 있다. 즉 바닥 타입(bottom type)이라 한다.

http://www.scala-lang.org/api/current/index.html#scala.Null


None은 NPE를 해결하기 위핸 Option 래퍼이다.  

val opt: Option[String] = None

참고로 Option은 Some과 None이라는 하위 클래스가 있다. 


final case class Some[+A](x: A) extends Option[A]




Nill은 엘리먼트가 하나도 없는 리스트를 의미한다.

println (Nil == List())




null은 좀 특이하다. 스칼라 컴파일러가 마음대로 적용하는 것 같다. 


println(null.asInstanceOf[Double])

NPE가 발생하지 않고 null이 출력된다. 


스칼라 컴파일러가 만든 JVM 바이트 코드는 다음코드로 번역된다.


scala.this.Predef.println(null);



만약 toString을 호출하면, NPE가 발생한다.


println(null.toString)


스칼라 컴파일러가 만든 JVM 바이트 코드는 다음코드로 번역된다.


      scala.this.Predef.println(null.toString());



아래 코드를 보면 더욱 재미있다. 결국 문맥에 따라 스칼라 컴파일러가 다르게 변경될 수 있다. 


scala> null.asInstanceOf[Int]

res2: Int = 0


scala> println(null.asInstanceOf[Int])

null






Posted by '김용환'
,


개발, 테스트와 관련된 용어이다. 



경계 조건은 영어로 edge case로서, 하나의 매개변수 값이 극단적인 최대값 또는 최소값이어서 로직에 문제가 발생할 수 있는 경우를 말한다. 

더 상세히 말하면..알고리즘에 처리하는 데 있어서 입력 매개변수의 유효 범위는 정해진다. 입력 범위가 다양해 질 수 있다. 따라서 테스트를 사용해서 입력 범위를 확인(validate)할 수 있다. 


예측이 될 수도 있고, 예측이 안될 수도 있지만, 문제를 어떻게든 해결할 수 있다.




복합 경계 조건은 영어로 corner case로서, 변수와 환경적인 요소로 인해서 로직에 문제가 발생할 있는 경우를 의미한다. 코드가 변경되지 않았는데, 테스트가 잘 성공하다가 실패할 때, 어떤 요인 때문에 발생할 수 있다.  랜선이 문제가 많거나 습도가 높은 컴퓨팅 환경 때문에 영향을 받을 수도 있다. 


재현, 테스트가 무척 어렵다.









Posted by '김용환'
,



실행 중인 메소드에서 호출 객체 인스턴스 참조를 자기 참조(self reference)라 한다. 

자바의 this와 동일하며, 스칼라에서도 this를 그대로 쓸 수 있다. 


case class Member(val name: String) {
def compare(that: Member): Boolean = {
if (this.name == that.name) true
else false
}
}


테스트 코드는 다음과 같다.

println(Member("samuel").compare(Member("samuel")))
println(Member("samuel").compare(Member("excel")))


결과는 다음과 같다.


true

false









this에 대한 추가 타입으로 self type annotation이고, this에 대한 앨리어스로 쓰는 self를 보자.

느낌은 바로 아래와 같다. 

trait Trait {
private[this] val self = this
}


이번에는 self 지시자를 테스트한다. 


trait Self {
self =>
val name = "outer"
val innerObject = new AnyRef {
val name = "inner"
println(name)
println(this.name)
println(self.name)
}
}

object Main extends App {
new AnyRef with Self
}

결과는 다음과 같다.


inner

inner

outer


트레이트에 self => 를 추가하면 멤버 필드는 트레이트가 소유한 것을 의미한다. 내부 클래스를 선언할 때, 내부 클래스의 동일한 변수를 참조할 때 self를 사용했다. 




물론, abstract class를 써도 동일한 결과는 발생한다. 



abstract class Self {
self =>
val name = "outer"
val innerObject = new AnyRef {
val name = "inner"
println(name)
println(this.name)
println(self.name)
}
}
class ChildSelf extends Self {
}

object Main extends App {
new ChildSelf
}


self를 name conflicting(이름 충돌)을 피하기 위해서 사용했다. 이 방법 외에, Injection하는 방식을 설명한다.



예는 마치 서비스 코드와 비슷하게 진행했다. 모르는 것이 훨씬 많아서 대충 코드를 썼다.


Controller - UserService (UserServiceImpl) 구조이다. 스칼라 Main 메소드에  withController with UserServiceImpl을 추가해서 믹스인(mixin)을 진행했다. 



trait UserService {
def printUser : Unit
}

trait UserServiceImpl extends UserService {
def printUser : Unit = println("samuel")
}

trait Controller {
self: UserService =>
}

object Main extends App with Controller with UserServiceImpl {
printUser
}


결과는 samuel으로 출력된다.



Controller 트레이트에서는 self:UserService 만 가르켰고 UserServiceImpl에 대한 정체는 알지 못한다.


Controller를 믹스인한 Main 객체에서 UserSerivceImpl 트레이트를 믹스인하면서, UserService를 통해 UserServiceImpl을 구했다. 


즉, Dependency Injection을 구현했다. 


스칼라에서는 이를 Cake Pattern 이라고 한다. 



재미있을 만한 링크는 다음에 있다. 


http://www.andrewrollins.com/2014/08/07/scala-cake-pattern-self-type-annotations-vs-inheritance/



https://coderwall.com/p/t_rapw/cake-pattern-in-scala-self-type-annotations-explicitly-typed-self-references-explained


http://marcus-christie.blogspot.kr/2014/03/scala-understanding-self-type.html



http://www.cakesolutions.net/teamblogs/2011/12/19/cake-pattern-in-depth




Posted by '김용환'
,

[scala] sugar 의미

scala 2016. 9. 27. 11:42


scala 문서나 책을 보면 sugar, desugar 라는 단어가 종종 나온다.


scala compiler가 jvm으로 translate할 때, 다른 언어에서 볼 수 없는 scala의 함수형 프로그래밍을 재밌게 진행할 수 있는 문맥(예, under score, case class 등)에서 많이 쓰이는 것 같다.


* scala sugar : 스칼라의 문법


* syntactic sugar : 편리한 문법, 간편 표기법


* desugar : 번역한다.




Posted by '김용환'
,