[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 '김용환'
,