parameter sweep, sweep은 하드웨어 용어에서 나온 듯 하다. 

구글에서 검색하면 논리/물리 회로를 테스트할 때가 자주 나온다.


parameter sweep은 일종의 자동화 튜닝 기능으로 특정 범위 내에서 나름 정해진 방법(random, step)으로 변수 값을 변화시켜 데이터 결과를 얻는다. 즉, 다양한 내용을 넣고 어떤 결과가 나오는지 명확하게 확인할 수 있기 때문이다. 



즉, 애플리케이션이나 테스트할 플랫폼을 블랙 박스로 여기고 다양한 정보를 정해진 방법으로 입력할 수 있는 형태를 parameter sweep이라 한다.


성능 분야 뿐 아니라 최근에 머신 러닝이 뜨면서 이런 단어를 쓰고 있다. 


https://msdn.microsoft.com/en-us//library/azure/dn905810.aspx


Tune Model Hyperparameters


 

Updated: April 29, 2016

Performs a parameter sweep on a model to determine the optimum parameter settings



이를 한국어로 표시하면 

https://msdn.microsoft.com/ko-kr/library/azure/dn905810.aspx


모델 하이퍼 조정


 

최적의 매개 변수 설정 결정 하는 모델에 매개 변수 비우기를 수행합니다

범주: 기계 학습 / 학습




정확하게 한국말로 표현하기가 어색한데,  '매개변수 비우기'라는 이름으로 번역되는 경우가 있다. 

내가 생각할 때는 이 말도 맞지만, 때로는  '매개변수로 전달'이라는 뜻으로 번역해도 괜찮을 것 같다. 



Posted by '김용환'
,


스칼라 클래스를 공부하고 있다.

자바 클래스와 비슷한 Member 클래스를 스칼라로 만들었다. 


class Member(_id: Int, _name: String) {
private var id = _id
private var name = _name

def get(_id: Int): String = {
name
}

def set(_id: Int, _name: String) = {
id = _id
name = _name
}

override def toString(): String = "(" + id + ", " + name + ")";
}

클래스 필드의 modifier는 private로 선언해야 외부에서 사용할 수 없다.(modifier를 쓰지 않으면 public이다, 참고로 protected는 자바처럼 그대로 존재한다.)

상위 클래스에서 이미 선언된 toString()을 정의할 때는 override를 써야 컴파일 에러가 발생하지 않는다.




사용하는 방법은 다음과 같다. 인스턴스를 생성하고 Member를 얻는다.


val member : Member = new Member(1, "samuel")
println(member.get(1))
println(member)

결과는 다음과 같다. 


samuel

(1, samuel)



public 필드를 가진 Dummy 클래스를 사용해보자.

class Dummy() {
var x = ""
}


Dummy 클래스를 다음처럼 사용할 수 있다.

val dummy = new Dummy()
dummy.x = "1"
println(dummy.x)





싱글톤 객체(Singleton object)를 생성해본다. object Member를 하나 추가해본다.


class Member(_id: Int, _name: String) {
private var id = _id
private var name = _name

def get(_id: Int): String = {
name
}

def set(_id: Int, _name: String) = {
id = _id
name = _name
}

override def toString(): String = "(" + id + ", " + name + ")";
}

object Member {
def print(_member : Member) : Unit = {
println("id : " + _member.id + ", name : " + _member.name)
}
}



사용할 때는 static method처럼 사용할 수 있다. 

object Member 를 class Member의 동반 객체(companion object)라 부른다. 반대로 class Member를 object Member의 동반 클래스(companion class)라 부른다.

val member : Member = new Member(1, "samuel")
Member.print(member)



결과는 다음과 같다.


id : 1, name : samuel



위의 코드는 일반 생성 코드와 싱글톤 객체의 메소드가 있으니 좀 보기 이상할 수 있다.

아예 싱글톤 객체로만 사용해본다.

object Member에 apply 메소드를 추가하여 new 없이 생성자 역할을 하도록 한다. 



class Member(_id: Int, _name: String) {
private var id = _id
private var name = _name

def get(_id: Int): String = {
name
}

def set(_id: Int, _name: String) = {
id = _id
name = _name
}

override def toString(): String = "(" + id + ", " + name + ")";
}

sealed trait Print {
def print(_member : Member) : Unit
}

object Member extends Print {
def print(_member : Member) : Unit = {
println("id : " + _member.id + ", name : " + _member.name)
}

def apply(_id: Int, _name: String) : Member = {
new Member(_id, _name)
}
}



new 생성자 없이 Member 객체를 생성했다. 

Member.print(Member(1, "samuel"))
Member.print(Member(2, "jackson"))

결과는 다음과 같다.


id : 1, name : samuel

id : 2, name : jackson





자세한 내용은 아래 링크를 참조한다.

http://knight76.tistory.com/entry/scala-%EC%8B%B1%EA%B8%80%ED%86%A4-%EA%B0%9D%EC%B2%B4-%EB%8F%85%EB%A6%BD-%EA%B0%9D%EC%B2%B4-%EB%8F%99%EB%B0%98-%ED%81%B4%EB%9E%98%EC%8A%A4




이번에는 unapply 메소드를 소개한다. unapply 메소드는 apply 메소드의 반대되는 개념이다. 

스칼라에서는 apply 메소드를 injection이라 부르고, unapply를 extraction이라 부른다. unapply를 통해 객체를 얻었다면, extractor object라 부른다.



다음 예제를 살펴본다.  unapply를 추가했고, 리턴 값을 튜플로 했다. 


 

class Member(_id: Int, _name: String) {
private var id = _id
private var name = _name

def get(_id: Int): String = {
name
}

def set(_id: Int, _name: String) = {
id = _id
name = _name
}

override def toString(): String = "(" + id + ", " + name + ")";
}

sealed trait Print {
def print(_member : Member) : Unit
}

object Member extends Print {
def print(_member : Member) : Unit = {
println("id : " + _member.id + ", name : " + _member.name)
}

def apply(_id: Int, _name: String) : Member = {
new Member(_id, _name)
}

def unapply(member: Member) : Option[(Int, String)] = {
if (member.id > 0 && !member.name.isEmpty)
Some(member.id, member.name)
else
None
}
}

object Main extends App {
println(Member.unapply(new Member(1, "samuel")))
}


결과는 다음과 같다.


Some((1,samuel))









에는 case class를 생성한다. case class는 생성자 매개변수를 노출하고 패턴 매칭을 가능케 한다. equals와 toString 메소드를 지원한다.


예시로 Member 추상 클래스와 하위 case class를 생성한다.

abstract class Member

case class CompanyMember(name: String) extends Member {}
case class SchoolMember(name: String) extends Member {}
case class CommunityMember(name: String) extends Member {}


case class를 테스트해본다.

val member = CompanyMember("samuel")
println(member) // toString
println(member == CompanyMember("samuel")) //equals

결과는 다음과 같다.


CompanyMember(samuel)

true




이제 패턴 매칭을 해본다. printMember 메소드 하나를 만들고, case-match 문을 간단히 추가하다.


def printMember(member: Member) {
member match {
case CompanyMember(n) => println("Company Member")
case SchoolMember(n) => println("School Member")
case CommunityMember(n) => println("Community Member")
case _ => println("unknown")
}
}

실행 코드는 다음과 같다.


val list = List(CompanyMember("samuel"), CommunityMember("matt"), 
                SchoolMember("jeff"))
list.foreach { printMember }

결과는 다음과 같다.


Company Member

Community Member

School Member



case class가 abstract class Member를 상속받았지만, 이를 trait로 바꿔도 동작한다.


sealed trait Member

case class CompanyMember(name: String) extends Member {}
case class SchoolMember(name: String) extends Member {}
case class CommunityMember(name: String) extends Member {}








지금까지, 실행 코드는 지금까지 object Main에 def main(args: Array[String]): Unit을 사용했다.


object Main {
def printMember(member: Member) {
member match {
case CompanyMember(n) => println("Company Member")
case SchoolMember(n) => println("School Member")
case CommunityMember(n) => println("Community Member")
case _ => println("unknown")
}
}

def main(args: Array[String]): Unit = {
val list = List(CompanyMember("samuel"), CommunityMember("matt"), SchoolMember("jeff"))
list.foreach { printMember }
}
}


object Main이 App 트레이트를 상속하면, 조금 단순해 질 수 있다.



object Main extends App {
def printMember(member: Member) {
member match {
case CompanyMember(n) => println("Company Member")
case SchoolMember(n) => println("School Member")
case CommunityMember(n) => println("Community Member")
case _ => println("unknown")
}
}

val list = List(CompanyMember("samuel"), CommunityMember("matt"), SchoolMember("jeff"))
list.foreach { printMember }
}


내부 구현은 아래 링크를 참조한다.

https://charsyam.wordpress.com/2015/02/27/%EC%9E%85-%EA%B0%9C%EB%B0%9C-scala-%EC%9D%98-app-trait%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94%EA%B0%80/



나는 여기서 디컴파일된 부분을 살펴본다.

object Main extends App {
println("App")
}


Main$.class 파일에 해당 내용이 담겨져 있다. 


public final class Main$

    implements App

{


    public long executionStart()

    {

        return executionStart;

    }


    public String[] scala$App$$_args()

    {

        return scala$App$$_args;

    }


    public void scala$App$$_args_$eq(String x$1[])

    {

        scala$App$$_args = x$1;

    }


    public ListBuffer scala$App$$initCode()

    {

        return scala$App$$initCode;

    }


    public void scala$App$_setter_$executionStart_$eq(long x$1)

    {

        executionStart = x$1;

    }


    public void scala$App$_setter_$scala$App$$initCode_$eq(ListBuffer x$1)

    {

        scala$App$$initCode = x$1;

    }


    public String[] args()

    {

        return scala.App.class.args(this);

    }


    /**

     * @deprecated Method delayedInit is deprecated

     */


    public void delayedInit(Function0 body)

    {

        scala.App.class.delayedInit(this, body);

    }


    public void main(String args[])

    {

        scala.App.class.main(this, args);

    }


    public final void delayedEndpoint$com$google$Main$1()

    {

        Predef$.MODULE$.println("App");

    }


    private Main$()

    {

        scala.App.class.$init$(this);

        delayedInit(new Main.delayedInit.body(this));

    }


    public static final Main$ MODULE$ = this;

    private final long executionStart;

    private String scala$App$$_args[];

    private final ListBuffer scala$App$$initCode;


    static

    {

        new Main$();

    }

}




이제, 간단한 보조 생성자를 살펴본다. def this를 사용할 수 있다.


class Test(n: Int, m: Int) {
def this(n: Int) = this(n, 0)
}

object Main extends App {
new Test(1, 0)
new Test(1)
}


전제 조건을 확인할 수 있는 require가 있다.



class Test(n: Int, m: Int) {
require(n > 0)
def this(n: Int) = this(n, 0)
}

object Main extends App {
new Test(1, 0)
new Test(1)
new Test(-1)
}


다음처럼 IllegalArgumentException이 발생한다. 


Caused by: java.lang.IllegalArgumentException: requirement failed

at scala.Predef$.require(Predef.scala:212)



참고로 동일한 기능으로 assert도 있다.


assert(n > 0)

다음처럼 AssertionError가 발생한다.


Exception in thread "main" java.lang.AssertionError: assertion failed



require는 생성자 매개변수의 검증을 위해, assert는 내부 로직의 검증을 위해 사용하는 것이 좋은 것 같다.






다음은 합성 타입(compound type)이다. 두 trait를 합쳐진 합성된(compound, 교집합) 채로 사용한다. 이를 통해 강력한 타입 시스템을 가지면서 유연성을 가질 수 있다. 


아래 예시는 joinAndFork 메소드에 적용할 수 있는 메소드는 Joinable과 Forkable을 상속한 클래스라고 명시적으로 규정할 수 있다. 


trait Joinable {
def join: String = { "join" }
}

trait Forkable {
def fork: String = { "fork" }
}

class JoFo extends Joinable with Forkable {
override def join: String = super.join
override def fork: String = super.fork
}

object Main extends App {

def joinAndFork(obj: Joinable with Forkable): Joinable = {
println(obj.join)
println(obj.fork)
obj
}
joinAndFork(new JoFo)
}

결과는 다음과 같다.


join

fork






apply 메소드와 unapply 메소드에서 사용했던 Member 클래스를 제네릭 클래스로 만들어본다.


class Member[T](_id: Int, _name: T) {
private var id = _id
private var name : T = _name

def get(_id: Int): T = {
name
}

def set(_id: Int, _name: T) = {
id = _id
name = _name
}

override def toString(): String = "(" + id + ", " + name + ")";
}

실행을 간단히 해본다.

val member = new Member(1, "aa")
println(member.get(1))

결과는 aa가 출력된다. 





다음은 묵시적 변환, 묵시적 클래스, 묵시적 매개변수 공부한 내용이다. 


http://knight76.tistory.com/entry/scala-class-%EC%98%88%EC%8B%9C-2-%EC%95%94%EC%8B%9C%EC%A0%81-%EB%B3%80%ED%99%98implicit-conversion


Posted by '김용환'
,

[scala] List 예시 - 2

scala 2016. 9. 19. 18:34


List의 메소드에 대한 설명이다. 

head 메소드는 리스트의 첫 번째 원소를 리턴하지만, tail 메소드는 head 메소드에서 리턴하는 값을 제외한 나머지를 리턴한다(나는 언제나 이 부분이 어색하다). isEmpty 메소드는 List가 비어 있는지 확인한다. 


val list = List(1,2,3)
println(list.head)
println(list.tail)
println(list.isEmpty)


결과는 다음과 같다.


1

List(2, 3)

false








리스트 소스를 보면, 리스트의 멤버 변수는 다음과 같다. 


def isEmpty: Boolean
def head: A
def tail: List[A]





빈 리스트에 isEmpty 메소드를 호출하고 head 메소드를 호출해본다. 

val list = List()
println(list.isEmpty)
println(list.head)

결과는 다음과 같다. head에 기본적으로 Nil이 저장되기때문에 관련 에러가 발생한다.


true

Exception in thread "main" java.util.NoSuchElementException: head of empty list

at scala.collection.immutable.Nil$.head(List.scala:420)

at scala.collection.immutable.Nil$.head(List.scala:417)



Nil을 할당하면, head메소드를 호출할 수 있다. 

val list2 = Nil
println(list2.head)


결과는 다음과 같다. 

Exception in thread "main" java.util.NoSuchElementException: head of empty list

at scala.collection.immutable.Nil$.head(List.scala:420)




Nil은 왜 List일까 생각할 수 있겠지만, Nil은 List[Nothing]을 상속받은 객체이다. 즉, 리스트  케이스 타입 중 하나이다. 


case object Nil extends List[Nothing] {


리스트 객체에 tail을 계속 붙일 수 있다.
val list = List(1,2,3)
println(list.tail.tail)
println(list.tail.tail.tail)

결과는 다음과 같다.

List(3)
List()



tail을 엘리먼트 개수 대비해서 많이 호출하면 에러가 발생할 수 있다. 


println(list.tail.tail.tail.tail)

결과는 다음과 같은 Exception이 발생된다. 



Exception in thread "main" java.lang.UnsupportedOperationException: tail of empty list

at scala.collection.immutable.Nil$.tail(List.scala:422)

at scala.collection.immutable.Nil$.tail(List.scala:417)




head, tail과 비슷한 last와 init이 있다. 


val list = List(1,2,3)
println(list.init)
println(list.last)


결과는 다음과 같다.


List(1, 2)

3




리스트의 length 메소드는 리스트의 길이를 리턴한다.

val list = List(1,2,3)
println(list.length)


결과는 다음과 같다.


3



리스트의 reverse 메소드이다. 

val list = List(1,2,3)
println(list.reverse)

결과는 다음과 같다.


List(3, 2, 1)




리스트의 drop 메소드이다. drop(1)은 처음부터 첫 번째 엘리먼트까지 버리고 나머지만 얻고, drop(2)는 처음부터 두 번째 엘리먼트까지 버리고 나머지를 버린다는 의미이다.

val list = List(1,2,3)
println(list.drop(1))
println(list.drop(2))

결과는 다음과 같다. 


List(2, 3)

List(3)



리스트의 splitAt 메소드는 다음과 같다. 


val list = List(1,2,3)
println(list.splitAt(1))
println(list.splitAt(2))

결과는 다음과 같다.


(List(1),List(2, 3))

(List(1, 2),List(3))



스칼라는 각 엘리먼트 요소에 index로 접근할 수 있다. 

val list = List(1,2,3)
println(list(0))
println(list(1))
println(list(2))
println()
println(list.apply(0))
println(list.apply(1))
println(list.apply(2))

결과는 다음과 같다.


1

2

3


1

2

3



만약 index를 잘못 넣어 가르키는 index에 엘리먼트가 없다면 IndexOutOfBoundsException이 발생한다. 


Exception in thread "main" java.lang.IndexOutOfBoundsException: 3
at scala.collection.LinearSeqOptimized$class.apply(LinearSeqOptimized.scala:65)
at scala.collection.immutable.List.apply(List.scala:84)


List에 index를 두든, apply(index)를 두든 아래 소스를 찾도록 되어 있다. 소스를 찾아보면 다음 구현으로 되어 있다.
먼저 drop으로 list를 얻은 후, 비어있지 않는다면 가장 맨 앞의 엘리먼트를 리턴하도록 되어 있다. 
def apply(n: Int): A = {
val rest = drop(n)
if (n < 0 || rest.isEmpty) throw new IndexOutOfBoundsException("" + n)
rest.head
}

flatten과 zip은 다음에서 설명했다.
http://knight76.tistory.com/entry/scala-map-flatten-flatmap-%EC%98%88%EC%8B%9C

zip와 unzip은 다음에서 설명했다.
http://knight76.tistory.com/entry/scala-zip-unzip-%EC%98%88%EC%8B%9C



Array의 mkString 메소드와 달리 List의 mkString은 비슷하다.
(http://knight76.tistory.com/entry/scala-Array-%EC%98%88%EC%8B%9C)

val list = List("s", "a", "m", "u", "e", "l")
println(list.mkString(":"))

결과는 다음과 같다.

s:a:m:u:e:l


리스트의 toArray는 배열로 변경한다. 
val list = List("s", "a", "m", "u", "e", "l")
println(list.toArray mkString " ")

결과는 다음과 같다.

s a m u e l




리스트의 copyToArray는 기존의 배열에 복사한다. 
val list = List("s", "a", "m", "u", "e", "l")
val emptyList = new Array[String](7)
list.copyToArray(emptyList, 0)
println(emptyList mkString " ")
결과는 다음과 같다. 초기화된 배열은 모두 null이기 때문에 맨 마지막 요소는 null로 표시되었다.

s a m u e l null



리스트의 map 예시이다. 
val list = List("s", "a", "m", "u", "e", "l")
println(list.map(_ + "X"))
println(list.flatMap(_ + "X"))

결과는 다음과 같다. 

List(sX, aX, mX, uX, eX, lX)
List(s, X, a, X, m, X, u, X, e, X, l, X)




List의 filter와 find 메소드의 예시이다. 
val list = List("s", "a", "m", "u", "e", "l")
println(list.filter(_ == "s"))
println(list.find(_ == "s"))

결과는 다음과 같다. filter의 결과 타입은 List이지만, find의 결과 타입은 Some이다. 

List(s)
Some(s)



만약 없는 요소를 filter, find하면 확연히 결과를 자세히 알 수 있다.

println(list.filter(_ == "z"))
println(list.find(_ == "z"))

결과는 다음과 같다.

List()
None



리스트의 partition은 리스트를 두 개로 나눈다.
val list = List(0,1,2,3,4,0)
println(list.partition(_ > 3))


결과는 다음과 같다.
(List(4),List(0, 1, 2, 3, 0))

partition메소드의 원형을 보면, predicate가 참이면, 두 개의 리스트를 리턴한다. 
def partition(p: A => Boolean): (Repr, Repr) = {
val l, r = newBuilder
for (x <- this) (if (p(x)) l else r) += x
(l.result, r.result)
}




리스트의 takeWhile은 좀 특이한 메소드이다. 함수를 받고, 조건식이 맞으면 ListBuffer에 넣었다가 조건식이 맞지 않을 때의 값을 리턴한다.  여기서 완전 주의할 것이 처음 엘리먼트부터 참이어야 한다는 점이다.!!!

@inline final override def takeWhile(p: A => Boolean): List[A] = {
val b = new ListBuffer[A]
var these = this
while (!these.isEmpty && p(these.head)) {
b += these.head
these = these.tail
}
b.toList
}

예시를 살펴본다. 
첫 번째, 4보다 작은 predicate을 주었다. 리스트의 처음 엘리먼트부터 참으로 가다가 엘리먼트의 값이 4에서 false가 되었다. (결과는 List(0,1,2,3) 일 것이다.)
두 번째, -1보다 큰 predicate을 주었다. 리스트의 처음 엘리먼트부터 참이다가, 엘리먼트가 -1인 부분에서 false가 되었다. (결국 결과는 List(0,1,2,3,4,5)가 될 것이다)
세 번째, 리스트의 처음 엘리먼트의 값은 3보다 크지 않다. 따라서 어떠한 List 엘리먼트도 추가되지 않는다. 결과는 List()가 된다.

val list = List(0,1,2,3,4,5,-1,0)
println(list.takeWhile(_ < 4))
println(list.takeWhile(_ > -1))
println(list.takeWhile(_ > 3))



예상대로 되었는지 결과를 살펴본다.


List(0, 1, 2, 3)

List(0, 1, 2, 3, 4, 5)

List()



다음은 takeWhile의 반대 메소드인 dropWhile이다. 결과를 살펴보면, 상반된 결과이지만, 구현이 조금 다르다.

dropWhile의 메소드 원형은 다음과 같다. 내부 메소드와 재귀 메소드를 적절히 사용했다.


@inline final override def dropWhile(p: A => Boolean): List[A] = {
@tailrec
def loop(xs: List[A]): List[A] =
if (xs.isEmpty || !p(xs.head)) xs
else loop(xs.tail)

loop(this)
}


dropWhile 예시는 다음과 같다. 

val list = List(0,1,2,3,4,5,-1,0)
println(list.dropWhile(_ < 4))
println(list.dropWhile(_ > -1))
println(list.dropWhile(_ > 3))

결과는 다음과 같다. 


List(4, 5, -1, 0)

List(-1, 0)

List(0, 1, 2, 3, 4, 5, -1, 0)




span 메소드는 takeWhile과 dropWhile 두 메소드를 하나도 합쳐 튜플로 리턴한다.


val list = List(0,1,2,3,4,5,-1,0)
println(list.span(_ < 4))

결과는 다음과 같다.


(List(0, 1, 2, 3),List(4, 5, -1, 0))



구현은 다음과 같다.

@inline final override def span(p: A => Boolean): (List[A], List[A]) = {
val b = new ListBuffer[A]
var these = this
while (!these.isEmpty && p(these.head)) {
b += these.head
these = these.tail
}
(b.toList, these)
}


리스트의 forall 메소드와 exists 메소드의 예시이다.

forall 메소드는 리스트의 모든 엘리먼트가 참일 때만 true를 리턴하고, exists 메소드는 하나라도 참이면 true를 리턴한다. 

val list = List(0,1,2,3,4,5,-1,0)
println(list.forall(_ > -100))
println(list.exists(_ == 0))


결과는 다음과 같다.


true

true




foldLeft와 foldRight는 우선 순위 연산을 지원하는 메소드이다.

val list = List(1,2,3)
println(list.foldLeft(1)(_ * _))
// ((1 * 1) * 2) * 3 = 6
println(list.foldLeft(2)(_ * _))
// ((2 * 1) * 2) * 3 = 12

println(list.foldRight(1)(_ * _))
// 1 * (2 * (3 * 1)) = 6
println(list.foldRight(2)(_ * _))
// 1 * (2 * (3 * 2)) = 12

결과는 다음과 같다.


6

12

6

12



리스트의 sortWith 메소드는 소팅을 지원한다. 다음 예시는 오른차순, 내림차순을 출력한다.

val list = List(1,2,3,-1,0)
println(list.sortWith(_ < _))
println(list.sortWith(_ > _))

결과는 다음과 같다.


List(-1, 0, 1, 2, 3)

List(3, 2, 1, 0, -1)




리스트의 팩토리 메소드이다.

http://www.scala-lang.org/docu/files/collections-api/collections_45.html




List.empty 예시이다.

println(List.empty)

결과는 List()이다.




List.range 예시이다.

println(List.range(1,5))
println(List.range(1,5,1))
println(List.range(1,6,2))


결과는 다음과 같다. 매개변수는 (start, end, step)을 의미한다. 

List(1, 2, 3, 4)

List(1, 2, 3, 4)

List(1, 3, 5)




List.concat 예시이다.

println(List.concat(List('s'), List('a'), List('m')))

결과는 다음과 같다.




List.fill 예시이다.

println(List.fill(2)("samuel"))

예시는 다음과 같다.



List.tabulate 예시이다.



println(List.tabulate(2)(a => a + 1))


결과는 다음과 같다. 


List(1, 2)




List.tabulate 메소드로 List(samuel, samuel)를 출력할 수도 있다. 


println(List.tabulate(2)(_ => "samuel"))






Posted by '김용환'
,




scala의 List 연산자 중, 

:::는 triple colon이라고 하고, 

::는 cons(콘즈)라고 한다.



Scala의 List는 단방향 연결 리스트이고 sealed abstract class이다. Nil과 ::라는 구현이 있다.


sealed trait List[+A] 

case object Nil extends List[Nothing] 

case class ::[A](head: A, tail: List[A]) extends List[A] 




List 트레이트 정의에 플러스(+) 기호가 있는 것을 주목한다. 플러스 기호는 List가 타입 매개변수 A에 공변성(covariant)이 있다는 것을 가르킨다. 공변성은 일반적인 타입에 다형성 제약을 표현할 수 있다. 예를 들면 다음 클래스를 살펴본다.


sealed trait Base

case class BaseImpl(value: Int) extends Base


여기에 Base와 Impl 간에 관계를 설명한다. Impl 클래스는 Base의 서브 타입이다. List가 함께 쓰인다면, 공변성은 List[Impl]은 List[Base]의 서브 타입이라는 뜻을 의미한다. 예시로 표현한 것은 공변성이 다음 코드를 컴파일할 수 있음을 의미한다. 


val bases : List[Base] = List[BaseImpl](BaseImpl(1))
println(bases)

결과는 다음과 같다.

List(BaseImpl(1))





이제 간단한 List 예시를 살펴본다. 적당하게 List를 선언한다. 

val list1 = List(1,2)
val list2 = List(3)


:: (콘즈)를 사용한다. 

val list3 = list1 :: list2
println(list3)



결과는 다음과 같다.리스트의 엘리먼트가 리스트가 되었다.


List(List(1, 2), 3)




이번에는 triple 리스트를 보자. 

val list4 = list1 ::: list2
println(list4)
val list4_1 = list2.:::(list1)
println(list4_1)


결과는 다음과 같다.

List(1, 2, 3)




::(콘즈)는 ::를 사용할 때와 .::()을 사용할 때가 정 반대임을 잘 기억하면 좋다.  ::(콘즈)는 두번째 리스트 끝에 첫번째 리스트를 붙이라는 의미를 가진다. 

val list5 = ::(1, ::(2, ::(3, Nil)))
println(list5)

val list5_1 = (1 :: (2 :: (3 :: Nil)))
println(list5_1)

val list5_2 = Nil.::(3).::(2).::(1)
println(list5_2)

val list5_3 = 1 :: 2 :: 3 :: Nil
println(list5_3)


결과는 모두 갖으며, 다음과 같다.

List(1, 2, 3)

List(1, 2, 3)

List(1, 2, 3)

List(1, 2, 3)





List는 다른 콜렉션과 다르게 패턴 매치 기능을 가지고 있다. ::(콘즈)와 함께 사용하면 재미있는 것을 만들어 볼 수 있다. List의 엘리먼트를 순차적으로 패턴매치를 하는 예시이다. 


0 엘리먼트부터 시작하는 경우에 대해서 다음 엘리먼트를 x에 저장하고 나머지를 rest로 저장하고 위해, 위에서 사용했던 ::을 사용해봤다. (0 :: x :: rest)

List(0, 1, 2, 3, 4, 5) match {
case 0 :: x :: rest => println(s"2번째: $x, 나머지: $rest")
case _ => println("others")
}

결과는 다음과 같다.


2번째: 1, 나머지: List(2, 3, 4, 5)


만약 아래 내용을 추가하지 않았다면, warning이 발생한다.


List(0, 1, 2, 3, 4, 5) match {
case 0 :: x :: rest => println(s"2번째: $x, 나머지: $rest")
}


scala에서는 List의 엘리먼트가 원치 않은 값에 대해서 처리할 수 없음을 warning으로 알려준다. 이런 이유로 case _ 구문을 추가했다.


Warning:(40, 9) match may not be exhaustive.

It would fail on the following inputs: List((x: Int forSome x not in 0)), List((x: Int forSome x not in 0), _), List(0), Nil

    List(0, 1, 2, 3, 4, 5) match {



정상적으로 출력되는지 확인하기 위해 다르게 테스트해본다.


List(6, 1, 2, 3, 4, 5, null) match {
case 0 :: x :: rest => println(s"2번째: $x, 나머지: $rest")
case _ => println("others")
}


결과는 다음과 같다. 


others




Scala의 List에 ::과 :::가 지원하는 것은 List에 prepend 연산이 최적화되어 있기 때문일지 모르겠다. 앞에 붙이는 연산은 싸고, 뒤로 붙이는 연산은 비싸다. 뒤에 붙이면 deep copy하고 새로운 리스트를 생성한다.


scala 문서(http://docs.scala-lang.org/overviews/collections/performance-characteristics.html)에 나온 이유가 이런 이유이다. append 연산은 constant time이지만, insert나 prepend는 linear하다. 




head
tailapplyupdateprependappendinsert
immutable       
ListCCLLCL-




CThe operation takes (fast) constant time.
LThe operation is linear, that is it takes time proportional to the collection size.







Posted by '김용환'
,


docker pull 하다가 ctrl+c를 눌러, 중지시켰다.

그리고, docker pull을 실행해도 다음 메시지만 출력하고 더 이상 pull을 하지 못할 수 있다.


.. already being pulled by another client. Waiting.



docker의 불안전한성(?) 때문인 것으로 생각되며, docker daemon을 재시작한다.


$ sudo service docker restart



docker daemon을 재시작한 후, docker pull하면 정상적으로 동작된다. 

Posted by '김용환'
,

[scala] Array 예시

scala 2016. 9. 12. 18:53


scala의 Array 예제이다. 


val array1 = Array(1,2,3)
val array2 = Array(3,4,5)


+: 연산자를 사용하면 재미있는 결과를 볼 수 있다. 앞과 뒤에 쉽게 붙일 수 있다. c++ 연산자 오버로딩과 비슷하게 사용할 수 있다. 

println(-1 +: Array(1,2,3) :+ 4 mkString " ")

결과는 다음과 같다. 


-1 1 2 3 4



Array 객체의 내용을 출력하려면, mkString 메소드를 사용해야 엘리먼트를 볼 수 있다. 그냥 출력하면 내부에서 관리하는 객체 이름으로 보인다. 

++ 연산자는 Array 객체를 서로 합친다. 


//println(array1 + array2) //compile error
println(array1 ++ array2) //내부 객체 이름 출력
println(array1 ++ array2 mkString " ")


결과는 다음과 같다.


[I@3567135c

1 2 3 3 4 5



intersect와 union 예제이다. union 메소드 결과와 Array.concat 메소드 결과는 동일하다.


println(array1 intersect array2 mkString " ")
println(array1 union array2 mkString " ")
println(Array.concat(array1, array2) mkString " ")

결과는 다음과 같다.


3

1 2 3 3 4 5

1 2 3 3 4 5



두 배열의 엘리먼트를 합치면서 유일한 값만 뽑으려면 distinct 메소드를 사용한다.

A-B 집합연산에 해당하는 diff메소드를 사용할 수도 있다.

println((array1 union array2 distinct) mkString " ")
println(array1 diff array2 mkString " ")


결과는 다음과 같다.


1 2 3 4 5
1 2



배열의 엘리먼트의 순서를 거꾸로 하려면 reverse 메소드를 사용한다.


println((array1.reverse).mkString(" "))

결과는 다음과 같다.


3 2 1




range를 array로 변환할 수도 있다.

println((0 to 3).toArray.mkString(" "))

결과는 다음과 같다.

0 1 2 3





Array.fill 메소드는 c의 memset과 같은 느낌으로 초기화할 수 있는 메소드이다. 

Byte 타입으로 3개의 엘리먼트를 가진 Array를 0으로,

Byte 타입으로 5개의 엘리먼트를 가진 Array를 1로 초기화하려면 다음과 같이 호출할 수 있다.

println(Array.fill[Byte](3)(0) mkString " ")
println(Array.fill[Byte](5)(1) mkString " ")

결과는 다음과 같다.

0 0 0

1 1 1 1 1




만약 5개의 엘리먼트를 Int 타입으로 랜덤하게 생성하려면 다음을 호출한다.



println(Array.fill[Int](5){scala.util.Random.nextInt(5)} mkString " ")

랜덤 결과 값은 다음과 같다.


4 0 2 0 2






Array.fill 메소드로 2*2 배열을 초기화할 수 있다. 결과값을 보려면, mkString에 .deep를 추가한다. 

deep.mkString을 사용하니 내용을 출력할 수 있었다.

val array = Array.fill[Int](2,2){scala.util.Random.nextInt(5)}
println(array mkString " ")
println(array.deep.mkString(" "))


결과는 다음과 같다.


[I@73f792cf [I@2ed94a8b

Array(2, 2) Array(2, 3)





Array에 foreach를 사용할 수 있다. 

val array = Array.fill[Int](5){scala.util.Random.nextInt(5)}
var sum = 0
array.foreach(sum += _)
println(sum)

array foreach(i => println(i))

array foreach{
case i => println(i)
}


Posted by '김용환'
,

[scala] zip, unzip 예시

scala 2016. 9. 12. 11:57


zip 메소드는 리스트를 순환하면서 리스트 튜플을 만든다. (내부적으로 iterator 객체를 포함할 때만 동작한다)


zipWithIndex는 따로 List를 주지 않아도 자동으로 0부터 시작하는 list와 합치도록 해준다. 


Stream from 0 은 0부터 시작하는 리스트를 리턴한다. 


unzip은 만어진 리스트 튜플을 다시 리스트 객체로 쪼개주는 역할을 한다. unzip._1은 다시 원래의 list로 리턴하는 메소드이다. 



val list = List(1,2,3,4,5)
println(list.zipWithIndex)
println(list.zip(Stream from 0))
println(list.zip(Stream from 0).unzip._1)


결과는 다음과 같다.


List((1,0), (2,1), (3,2), (4,3), (5,4))

List((1,0), (2,1), (3,2), (4,3), (5,4))

List(1, 2, 3, 4, 5)



문자열도 동일하게 동작한다. 

scala에서는 메소드르 사용할 때 꼭 내부 함수를 의미하는 .나 괄호를 쓰지 않아도 되는 경우가 있다. 마치 수학 연산 같은 느낌을 주기 위해 다음과 같이 List zip List 형태로 쓸 수 있다.


zip, unzip, toMap 메소드 예시를 실행해 본다.

val zipped = List("A", "B") zip List("C", "D")
println(zipped)
println(zipped.unzip)
println(zipped.toMap)


결과는 다음과 같다.


List((A,C), (B,D))

(List(A, B),List(C, D))

Map(A -> C, B -> D)




만약 엘리먼트가 하나가 더 큰 상황에서 zip메소드를 호출하면 긴 엘리먼트 쪽 데이터는 zip 메소드에서 사용하지 않는다.


val zips = List("A", "B") zip List("C")
println(zips)


결과는 다음과 같다.


List((A,C))




두 개 뿐 아니라 세 개의 튜플을 가지는 리스트 객체를 생성할 수도 있다. 


println((List(1,2,3,4,5), List(0,1,2,3,4)).zipped.toList)
println((List(1,2,3,4,5), List(0,1,2,3,4), List(10,11,12,13,14)).zipped.toList)


결과는 다음과 같다. 

List((1,0), (2,1), (3,2), (4,3), (5,4))

List((1,0,10), (2,1,11), (3,2,12), (4,3,13), (5,4,14))


Posted by '김용환'
,




Map을 다루는 예시이다.

val m = Map(1 -> "a", 2 -> "b", 3 -> "c")


transform메소드를 호출한다. transfrom의 정의문은 다음과 같다. 매개변수는 (A,B)  => C이다.


def transform[C, That](f: (A, B) => C)


다음을 실행한다.

println(m.transform((k,v) => v.toUpperCase))


결과는 다음과 같다. value가 모두 대문자가 되었다


Map(1 -> A, 2 -> B, 3 -> C)






이제, Map의 map 메소드를 호출한다. map 메소드의 원형은 다음과 같다. 매개변수는 A=>B 이다. 



def map[B, That](f: A => B)



함수의 원형에 따라 m.map을 호출한다. (Map.map 호출이라 굉장히 어색하긴 하지만..)

println(m.map(x => x + "y"))


결과를 보면, 다음처럼 y가 튜플 뒤에 붙었다.


List((1,a)y, (2,b)y, (3,c)y)




이제 괄호문과 case를 사용해본다. 그러면 (k,v)로 확인해볼 수 있다. value를 대문자로 바꿔 리스트로 리턴하라는 명령이다. 


println(m.map {case (k,v) => v.toUpperCase})


결과는 다음과 같다.


List(A, B, C)




이제는 키의 값만 바꿔 1을 더해, 키 리스트를 얻는다.



println
(m.map {case (k,v) => k + 1})

결과는 다음과 같다.

List(2, 3, 4)





key 값 연산과 value 값 연산을 합쳐, 이제는 Map의 key의 값에 숫자를 더하고, value를 대문자로 변환하는 map을 만든다.


println(m.map {case (k,v) => (k + 5, v.toUpperCase)})

결과는 다음과 같다.



Map(6 -> A, 7 -> B, 8 -> C)






이제 mapValues를 사용한다. mapValues의 메소드 원형은 다음과 같다. 매개변 수가 B => C 이다. 



override def mapValues[C](f: B => C): Map[A, C]


간단하게 테스트해본다. map의 value에 연산을 하고 Map을 리턴한다.

println(m.mapValues(_ capitalize))

println(m.mapValues(_ + "xxx"))


결과는 다음과 같다. 


Map(1 -> A, 2 -> B, 3 -> C)

Map(1 -> axxx, 2 -> bxxx, 3 -> cxxx)












* 중요한 포인트는 이제 시작이다. 



Map.transform과 Map.mapValues에는 비슷해 보이지만, 차이점이 있다. 예시를 테스트하면서 메소드 원형을 살펴본 이유가 그런 배경이다. Map[A, C]를 리턴한다는 점에서 동일하다.  요약하면 다음과 같을 것 같다.



def map[B](f: ((A, B)) ⇒ B): Map[B]

def map[B](f: (A) ⇒ B): Set[B]

def map[B](f: (A) ⇒ B): Seq[B]

def transform[C](f: (A, B) => C) : Map[A, C]

def mapValues[C](f: (B) ? C): Map[A, C]






Map.transform 구현 코드는 다음과 같다. 결과를 리턴한다



def transform[C, That](f: (A, B) => C)
        (implicit bf: CanBuildFrom[This, (A, C), That]): That = {
val b = bf(repr)
for ((key, value) <- this) b += ((key, f(key, value)))
b.result()
}




Map.mapValues의 원형은 다음과 같다.



override def mapValues[C](f: B => C): Map[A, C] = 
                    new MappedValues(f) with DefaultMap[A, C]


MapValues 클래스의 원형은 다음처럼 되어 있다.  self를 호출하고 map 메소드에 f를 적용한다. 또 한 번의 연산이 발생한다. 


protected class MappedValues[C](f: B => C) extends AbstractMap[A, C] with DefaultMap[A, C] {
override def foreach[U](g: ((A, C)) => U): Unit = for ((k, v) <- self) g((k, f(v)))
def iterator = for ((k, v) <- self.iterator) yield (k, f(v))
override def size = self.size
override def contains(key: A) = self.contains(key)
def get(key: A) = self.get(key).map(f)
}



좀 더 자세한 테스트를 위해서 mutable Map을 통해 다음 코드를 실행한다. 


val newMap = collection.mutable.Map(1 -> 1, 2 -> 2)
println(newMap)
println(newMap eq newMap.mapValues(_+1))
println(newMap)
println()

println(newMap)
println(newMap eq newMap.transform((k, v) => v + 1))
println(newMap)
println()


결과는 다음과 같다. 원래 Map 값이 바뀌었다.


Map(2 -> 2, 1 -> 1)

false

Map(2 -> 2, 1 -> 1)


Map(2 -> 2, 1 -> 1)

true

Map(2 -> 3, 1 -> 2)



(참고로 Immutable Map에서 mapValues나 transform 메소드를 호출할 때 원본 Map은 바뀌지 않았다!!)



api 철학이 여기에서 보이는데, mapValues를 실행하고 난 뒤, 원래 Map은 바뀌지 않고 코드에 있는 대로 뷰만 리턴한다. 하지만, tranform을 실행하면 원래 Map을 바꾼다. 이상하다고 볼 수 있지만, java 언어에서도 가끔씩 볼 수 있는 api라서, 철학 차이 정도로 생각하고 있다. 


상황에 따라서는 분명 데이터와 속도 이슈가 좀 차이가 나기 때문에 이슈라 생각할 수도 있을 것 같다. 



이런 이슈로, mapValues 메소드를 호출하면 MapView라는 클래스를 만들자는 의견이 나왔고 scala 2.13.0 RC1부터 반영될 것 같다.


https://issues.scala-lang.org/browse/SI-4776





[참고]

http://stackoverflow.com/questions/25635803/difference-between-mapvalues-and-transform-in-map

http://blog.bruchez.name/2013/02/mapmap-vs-mapmapvalues.html





'scala' 카테고리의 다른 글

[scala] Array 예시  (0) 2016.09.12
[scala] zip, unzip 예시  (0) 2016.09.12
[scala] groupBy, distinct, sortWith, sortBy 예시  (0) 2016.09.09
[scala] grouped와 sliding  (0) 2016.09.09
[scala] main 메소드 argument 처리  (0) 2016.09.08
Posted by '김용환'
,



groupBy 메소드는 Traversable 트레이트의 자식 클래스로, 그루핑할 때 사용한다. 



예시로 빈도 별로 뽑고 싶다면 다음처럼 코드를 실행한다.


val names = List("Jackson", "Peter", "Jax", "Karl", "Jackson",
                 "Jackson", "Jax")
println(names.groupBy(x => x.toString))



람다식 대신 _를 활용할 수 있다.

val names = List("Jackson", "Peter", "Jax", "Karl",
"Jackson", "Jackson", "Jax")
println(names.groupBy(_.toString))



결과는 다음과 같다.


Map(Karl -> List(Karl), Jax -> List(Jax, Jax), Jackson -> List(Jackson, Jackson, Jackson), Peter -> List(Peter))



엘리먼트의 length 별로 그룹핑할 수 있다. groupBy의 결과 타입은 Map이다. 


val names = List("Jackson", "Peter", "Jax", "Karl",
"Jackson", "Jackson", "Jax")
println(names.groupBy(_.length))

결과는 다음과 같다.


Map(5 -> List(Peter), 4 -> List(Karl), 7 -> List(Jackson, Jackson, Jackson), 3 -> List(Jax, Jax))






같은 단어라면 보이지 않도록 distinct 메소드를 사용한다.

val names = List("Jackson", "Peter", "Jax", "Karl",
"Jackson", "Jackson", "Jax")

val unsorted = names.distinct.groupBy(_.length)
println(unsorted)


결과는 다음처럼 깔끔해졌다.


Map(5 -> List(Peter), 4 -> List(Karl), 7 -> List(Jackson), 3 -> List(Jax))





하지만 length 별로 sorting이 되어 있지 않다. 이를 sort해보자.

이를 위해서는 바로 변환이 안된다. 먼저 Seq(ArrayBuffer)로 변환한 후, 다시 map으로 만든다. 



Map을 Seq로 변환한후 sort한다. sort할 때 sortBy를 써서 1 번째 값(length)로 sort가 되게 한다.

sortBy 말고도 sortWith도 사용할 수 있다. sortWith는 자바와 compare 메소드와 비슷하다. 


그리고, _*를 사용해서 Seq를 ListMap으로 변환한다. 

val names = List("Jackson", "Peter", "Jax", "Karl",
"Jackson", "Jackson", "Jax")
val unsorted = names.distinct.groupBy(_.length)
println(unsorted)

//val sorted = unsorted.toSeq.sortBy(_._1)
val sorted = unsorted.toSeq.sortWith(_._1 < _._1)
println(sorted)

val map = ListMap(sorted:_*)
println(map)



결과는 다음과 같다.


Map(5 -> List(Peter), 4 -> List(Karl), 7 -> List(Jackson), 3 -> List(Jax))

ArrayBuffer((3,List(Jax)), (4,List(Karl)), (5,List(Peter)), (7,List(Jackson)))

Map(3 -> List(Jax), 4 -> List(Karl), 5 -> List(Peter), 7 -> List(Jackson))












마법의 키 sorted:_*가 궁금할 것이다.

sortBy, sortWith의 리턴 타입은 튜플을 엘리먼트로 갖는 Seq[(String, Int)]이다.


실행해보면, 아래와 같은 type mismatch 에러가 발생한다.


val
map = ListMap(sorted)

Error:(26, 23) type mismatch;

 found   : Seq[(Int, List[String])]

 required: (?, ?)

    val map = ListMap(sorted)





따라서, ListMap의 생성자인 apply 메소드를 쫓아가면, GenMapFactory의 apply 메소드를 만나는데, 튜플의 가변 인자:(A, B)*를 받도록 되어 있다. 그래서 _*로 만들어 낸 것이다.


def apply[A, B](elems: (A, B)*): CC[A, B] = (newBuilder[A, B] ++= elems).result()


특이하긴 하지만, scala 공식 문서에도 나와 있는 내용이기도 하다. 

http://docs.scala-lang.org/tutorials/FAQ/finding-symbols.html

f(xs: _*) // Sequence xs is passed as multiple parameters to





다시 groupBy로 넘어와서 case를 쓰는 예시를 사용한다.


val list = List(1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9)

val g = list.distinct.groupBy {
case num if (num % 2 == 0) => "짝수"
case _ => "홀수"
}


println(g)


결과는 다음과 같다. 


Map(짝수 -> List(2, 4, 6, 8), 홀수 -> List(1, 3, 5, 7, 9))



이번에는 숫자 리스트를 groupBy해서 숫자 엘리먼트별로  몇 개가 존재하는지 살펴본다. 모든 map 값에 _.size를 적용해서 그 키에 대한 새로운 맵을 구성한다. 
val list = List(1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9)
val g = list.groupBy(x => x).mapValues(_.size)

println(g)
결과는 다음과 같다.

Map(5 -> 1, 1 -> 2, 6 -> 1, 9 -> 3, 2 -> 1, 7 -> 1, 3 -> 1, 8 -> 1, 4 -> 1)



sort를 해보고 싶다면, toSeq.sortBy(_._1):_*를 붙이면 된다.
val list = List(1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9)
val g = ListMap(list.groupBy(x => x).mapValues(_.size).toSeq.sortBy(_._1):_*)
println(g)

결과는 다음과 같다.



Map(1 -> 2, 2 -> 1, 3 -> 1, 4 -> 1, 5 -> 1, 6 -> 1, 7 -> 1, 8 -> 1, 9 -> 3)



Posted by '김용환'
,

[scala] grouped와 sliding

scala 2016. 9. 9. 11:39





grouped 메소드는 콜렉션 엘리먼트를 일정의 크기로 나누고,

sliding 메소드는 고정 블럭 블록(fixed size block) 나눠, sliding window가 된다. 

둘다 리턴 클래스는  scala.collection.Iterator이다. 



아래 예시에서 grouped는 예상하는 것처럼 3개식 나누면, List(1,2,3), List(4,5) 이렇게 나눠진다.

그러나 sliding은 List(1,2,3)과 List(2,3,4)로 나눈다.

val list = List(1,2,3,4,5)
val g = list.grouped(3)
println(g.next())
println(g.next())

val s = list.sliding(3)
println(s.next)
println(s.next)


결과는 다음과 같다.


List(1, 2, 3)

List(4, 5)


List(1, 2, 3)

List(2, 3, 4)




다른 예시를 봐야 더 이해가 된다.

grouped는 3 개씩 칼처럼 나누지만, sliding은 순서대로 3개씩 묶어준다. 


val list = List(1,2,3,4,5,6,7)
val g = list.grouped(3)
println(g.next())
println(g.next())
println(g.next())
println

val s = list.sliding(3)
println(s.next)
println(s.next)
println(s.next)
println(s.next)
println(s.next)

결과는 다음과 같다.


List(1, 2, 3)

List(4, 5, 6)

List(7)


List(1, 2, 3)

List(2, 3, 4)

List(3, 4, 5)

List(4, 5, 6)

List(5, 6, 7)






Posted by '김용환'
,