스칼라의 콜렉션 객체에서 foldLeft을 많이 사용한다. 왼쪽 항부터 마지막 항까지 계산되는 것을 의미한다. 

리스트의 값을 왼쪽에서 오른쪽으로 계산한다. 0(초기값) + 3 + 6 + 9  = 18이 예상된다.

val list : List[Int] = List(3, 6, 9)
println(list.foldLeft(0) ((x, y) => x + y))

결과는 다음과 같다.


18



이 코드는 조금 아래처럼 잼나게 _+_로 바꿔 쓸 수 있다.  결과는 동일하다. 


val list : List[Int] = List(3, 6, 9)
println(list.foldLeft(0) (_+_))

동작이 어떻게 되는 지 궁금하다면 다음처럼 디버깅하듯이 코딩할 수 있다.

val list : List[Int] = List(3, 6, 9)
println(list.foldLeft(0) { (x : Int, y : Int) => println("x:"+ x + ",y:"+ y); x + y })


결과는 다음과 같다.


x:0,y:3

x:3,y:6

x:9,y:9

18








이제, foldRight 메소드를 사용한다.


val list : List[Int] = List(3, 6, 9)

println(list.foldRight(0) ((x, y) => x - y))


 


결과를 0으로 생각할 수 있겠지만, 사실 결과는 6이 출력된다.




출력할 수 있도록 println을 사용해본다.

val list : List[Int] = List(3, 6, 9)
println(list.foldRight(0) { (x : Int, y : Int)
=> println("x:"+ x + ",y:"+ y); x - y })


결과는 다음과 같다. 3 - (-3) = 6 이 된다. 


x:9,y:0

x:6,y:9

x:3,y:-3

6






문자열도 foldLeft도 사용할 수 있다. 


val list : List[String] = List("aaa", "bbb", "ccc")
list.foldLeft("xxx") { (x : String, y : String)
=> println("x: " + x + " y: " + y) ; x + ":" + y}


출력은 다음과 같다.


x: xxx y: aaa

x: xxx:aaa y: bbb

x: xxx:aaa:bbb y: ccc

Posted by '김용환'
,


Stream 객체를 써서 List의 요소를 반복하고 싶다면, Stream.continually(리스트).take() 메소드를 사용한다.


val list : List[String] = List("aaa", "bbb", "ccc")
println(Stream.continually(list).take(10).flatten.toList)



결과


List(aaa, bbb, ccc, aaa, bbb, ccc, aaa, bbb, ccc, aaa, bbb, ccc, aaa, bbb, ccc, aaa, bbb, ccc, aaa, bbb, ccc, aaa, bbb, ccc, aaa, bbb, ccc, aaa, bbb, ccc)







주의할 점은 다음 코드는 Stream(aaa, ?) 타입을 리턴하게 되니. 값을 확인할 수 없다.


println(Stream.continually(list).take(10).flatten)



scala> Stream.continually(list.toStream).take(10).flatten

res5: scala.collection.immutable.Stream[String] = Stream(aaa, ?)




Posted by '김용환'
,



package object 에 대한 설명은 다음과 같다.

http://www.scala-lang.org/docu/files/packageobjects/packageobjects.html



com/google/utility 디렉토리에 package.scala를 생성하고 다음 코드를 저장한다.

scala에 패키지용 utility 처럼 쓸 수 있는 클래스와 같다. 


package com.google

import org.slf4s.Logging

package object utility extends Logging {
def print(message : String): Unit = {
println(message)
}
}


com/google 디렉토리에 test.scala를 생성한다.


package com
package google

object test {
def main(args: Array[String]): Unit = {
utility.print("test")
}
}


실행 결과는 다음과 같다.


test 




참고로 같은 패키지에 존재하기 때문에 test 클래스는 다음처럼 사용할 수 있다.


package com
package google

object test {
def main(args: Array[String]): Unit = {
print("test")
}
}

또는


object test {
def main(args: Array[String]): Unit = {
print("test")
}
}


Posted by '김용환'
,


for..yield는 for comprehension (sequence comprehension)이라 불린다. 

for.. yield 는 상황에 따라 foreach, flatmap, filter, map으로 translate 할 수 있는 문법 설탕(https://en.wikipedia.org/wiki/Syntactic_sugar)이다.  

개발자가 개발하기 쉽도록 (또는 보기 편하도록) 설계된 문법이다.  


좀 더 재미있는 내용을 살펴보고 싶다면, http://stackoverflow.com/questions/1052476/what-is-scalas-yield를 참조하면 즐거울 것 같다.




예시 시작





그냥 간단한 for문이다.

for(i <- 1 to 3) {
println(i)
}

결과

1

2

3



REPL에서 yield를 사용하면 결과가 Vector 타입의 인스턴스로 리턴한다.

scala> for(i <- 1 to 3) yield i

res2: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)



val a =  for(i <- 1 to 3) yield i;
println(a)


결과

Vector(1, 2, 3)





yield 값에 곱셈을 추가해본다.

val a =  for(i <- 1 to 3) yield i * 100;
println(a)


결과

Vector(100, 200, 300)





yield 뒤에 조건문을 두어 실행한다.

val a =  for(i <- 1 to 3) yield i > 2;
println(a)

결과

Vector(false, false, true)





yield 앞 for 조건문에 if 문을 둔다. 마치 filter의 효과를 둔다.

val a = for( i <- 1 to 3 ; if i == 1 ) yield i
println(a)

결과

Vector(1)



if 문을 여러 개 두어 필터링을 할 수 있다. 

val a = for( i <- 1 to 3 ; if i == 1 ; if i == 2 ) yield i
println(a)





List를 사용할 수 있고, { }를 이용하여 확장할 수 있다.

val list = List(1, 2, 3)
val a = for {
element <- list
} yield {
element
}
println(a)


결과 

List(1, 2, 3)






앞에서 테스트한 내용을 {}을 이용하여 확장하였다. 결과는 동일하다.

A 코드

val a =  for(i <- 1 to 3) yield i > 2;
println(a)

B 코드


val list = List(1, 2, 3)
val a = for {
i <- list
} yield {
i > 2
}
println(a)


결과

List(false, false, true)





for 문에 List 2개를 넣었다. loop 두 개의 효과가 발생한다.

val list = List(1, 2, 3)
val list2 = List(10, 20)
val a = for {
i <- list
j <- list2
}
yield {
i
}
println(a)


결과

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




val list = List(1, 2, 3)
val list2 = List(10, 20)
val a = for {
i <- list
j <- list2
} yield {
i * j
}
println(a)


결과

List(10, 20, 20, 40, 30, 60)





val list = List(1, 2, 3)
val list2 = List(10, 20)
val list3 = List(1, 2)
val a = for {
i <- list
j <- list2
z <- list3
} yield {
i + j + z
}
println(a)


결과

List(12, 13, 22, 23, 13, 14, 23, 24, 14, 15, 24, 25)







튜플을 만들 수도 있다.


val list = List(1, 2, 3)
val list2 = List("x", "y")
val a = for {
i <- list
j <- list2
} yield {
(i, j)
}
println(a)


결과

List((1,x), (1,y), (2,x), (2,y), (3,x), (3,y))








두 리스트의 엘리먼트를 하나로 합쳐주는 zip 함수를 사용하여 단일 리스트로 만든 후, for.. yield를 사용할 수 있다.

val names = List("Jax", "Peter", "Tony")
val score = List(100, 80, 90)
val zipped = names.zip(score);

val example = for ((name, score) <- zipped) yield name + "-" + score

println(example);


결과 

List(Jax-100, Peter-80, Tony-90)


zipped는 List((Jax,100), (Peter,80), (Tony,90)) 이다. 






두 리스트의 중복 요소를 찾는 경우에도 사용할 수 있다.

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

val result = for (e1 <- list1 ; e2 <- list2 if e1 == e2) yield e1
print(result)


결과

List(1)












Posted by '김용환'
,


scala 11.8에서 스코프를 사용하면 잘 동작한다.



~$ scala

Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_40).

Type in expressions for evaluation. Or try :help.


scala> val a = 1

a: Int = 1


scala> {

     | val a = 2

     | }


scala> println(a)

1



그러나, 이를 한 줄에 같이 쓰면 에러로 표현한다. 

scala> val a = 1 { val a = 2 }

<console>:11: error: Int(1) does not take parameters

       val a = 1 { val a = 2 }



또한, Intellij IDEA의 스칼라 컴파일러를 사용하면 에러가 발생한다


val a = 1
{
val a = 2
}



Int(1) does not take parameter 라는 에러가 발생한다. Integer 클래스에서 {로 시작하는 파라미터가 없다고 발생한다.

Error:(5, 5) Int(1) does not take parameters

    {

    ^




한 줄을 추가하면 정상적으로 동작한다. warning이 발생하지만, 잘 동작한다. Warning:(6, 5) a pure expression does nothing in statement position; you may be omitting necessary parentheses

    {

    ^

val a = 1

{
val a = 2
} println(a)


Posted by '김용환'
,



1.  javap

javap 라는 자바 유틸리티로 스칼라 클래스의 메소드만 살펴볼 수 있다.



$ javap YourInteger$.class

Compiled from "YourInteger.scala"

public final class YourInteger$ {

  public static final YourInteger$ MODULE$;

  public static {};

  public YourInteger intToYourInteger(int);

}



2. jad 

java deasemmbler로 유명한 jad를 다양한 운영체제에서 쓸 수 있다. 과거 jadclipse 또는 jad를 써본 사람은 익숙하다.

개인적으로도 좋아하는 툴이다.


http://varaneckas.com/jad/ 에서 운영체제에 맞게 다운로드한다.


다운로드하고 압축 파일을 풀면 jad 파일을 볼 수 있다. /usr/local/bin/으로 복사한다.

jad를 실행하고 디컴파일 된 파일을 보며 스칼라 컴파일러가 어떻게 스칼라 파일을 컴파일했는지 살펴볼 수 있다. 


$ jad YourInteger.class

Parsing YourInteger.class... Generating YourInteger.jad



$ cat YourInteger.jad

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.

// Jad home page: http://www.kpdus.com/jad.html

// Decompiler options: packimports(3)

// Source File Name:   YourInteger.scala


import scala.Predef$;

import scala.collection.mutable.StringBuilder;

import scala.runtime.BoxesRunTime;


public class YourInteger

{


    public static YourInteger intToYourInteger(int i)

    {

        return YourInteger$.MODULE$.intToYourInteger(i);

    }


    public int number()

    {

        return number;

    }


    public void method(int given)

    {

        Predef$.MODULE$.println((new StringBuilder()).append("Hi ").append(BoxesRunTime.boxToInteger(given)).toString());

    }


    public YourInteger plus(int given)

    {

        return new YourInteger(given + number());

    }


    public String toString()

    {

        return String.valueOf(BoxesRunTime.boxToInteger(number()));

    }


    public YourInteger(int i)

    {

        number = i;

    }


    private final int number;

}



3. scalac 의 -Xprint:parse 를 활용한다.


$ scalac  -Xprint:parse file.scala 처럼 사용한다.



예시)

object Hello {
def main(args: Array[String]): Unit = {
val res = for {
i <- 0 to 10
j <- i + 1 to 5
if i + j == 10
} yield s"$i $j"
println(res)
res.foreach(println)
}
}

스칼라 언어 관점으로 개발하면 for 문을, map, flatmap, filter, _에 익숙해지지만 유지보수성이 떨어질 수 있다. 

아직 완벽스럽지는 않지만, scala 컴파일러는 for문을 자연스럽게 flatmap, map, filter으로 변경한다. 



$ scalac  -Xprint:parse  Hello.scala

[[syntax trees at end of                    parser]] // Hello.scala

package <empty> {

  object Hello extends scala.AnyRef {

    def <init>() = {

      super.<init>();

      ()

    };

    def main(args: Array[String]): Unit = {

      val res = 0.to(10).flatMap(((i) => i.$plus(1).to(5).withFilter(((j) => i.$plus(j).$eq$eq(10))).map(((j) => StringContext("", " ", "").s(i, j)))));

      println(res);

      res.foreach(println)

    }

  }

}



Posted by '김용환'
,




YourInteger 라는 스칼라 클래스를 생성하고, + 연산을 할 수 있는 메소드를 추가한다.

class YourInteger(i : Int) {
val number = i

def method(given : Int) = {
println("Hi " + given);
}

def plus(given : Int):YourInteger = {
new YourInteger(given + number)
}

override def toString() = {
"" + number
}
}



YourInteger 객체를 생성하고 + 연산을 해본다.

val a = new YourInteger(5);
println(a)

결과는 다음과 같다.

5



YourInteger 클래스의 method를 호출하면 당연히 잘 되지만, 반대의 케이스는 에러가 발생한다.

val b = a.method(1) // b는 컴파일 성공
val c = 1.method(3) // c는 컴파일 에러

컴파일러는 overloaded method value + with alternatives를 출력하는 에러가 발생한다.



암묵적 타입 변환인 implicit def를 선언하면, int에 대해 YourInteger의 메소드를 실행할 수 있다.  ruby처럼 static duck typing 기법에 대해서 조금 알았다.


val a = new YourInteger(5);
println(a)
implicit def intToYourInteger(x: Int) = new YourInteger(x);

val b = a.method(1)
val c = 1.method(3)
val d = 1.plus(2)






실행 코드에 implict 구문이 있으면 지저분해서 조금 변경할 수 있다.



YourInteger 클래스 파일에 YourInteger 객체를 선언한 후, 묵시적 타입 변환을 추가한다.



class YourInteger(i : Int) {
val number = i

def method(given : Int) = {
println("Hi " + given);
}

def plus(given : Int):YourInteger = {
new YourInteger(given + number)
}

override def toString() = {
"" + number
}
}


object YourInteger {

implicit def intToYourInteger(x: Int) = new YourInteger(x);

}



실행 코드에서 import를 하고..


import YourInteger._


실행할 메소드에서 다음을 사용하면 실행된다. 

val a = new YourInteger(5);
println(a)

val b = a.method(1)
val c = 1.method(3) println(c)
val d = 1.plus(2)



javap로 확인하면 다음과 같다.


$ javap YourInteger.class

Compiled from "YourInteger.scala"

public class YourInteger {

  public static YourInteger intToYourInteger(int);

  public int number();

  public void method(int);

  public YourInteger plus(int);

  public java.lang.String toString();

  public YourInteger(int);

}


$ javap YourInteger$.class

Compiled from "YourInteger.scala"

public final class YourInteger$ {

  public static final YourInteger$ MODULE$;

  public static {};

  public YourInteger intToYourInteger(int);

}


좀 더 내부를 파고 싶다면, class파일을 디컴파일하는 jad(http://varaneckas.com/jad/)를 이용한다. 


YourInteger.scala를 컴파일한 scala파일의 java 구현체(YourInteger, YourInteger$)는 다음과 같다. Predef 외에는 특별한 내용이 없다. 


import scala.Predef$;

import scala.collection.mutable.StringBuilder;

import scala.runtime.BoxesRunTime;


public class YourInteger

{


    public static YourInteger intToYourInteger(int i)

    {

        return YourInteger$.MODULE$.intToYourInteger(i);

    }


    public int number()

    {

        return number;

    }


    public void method(int given)

    {

        Predef$.MODULE$.println((new StringBuilder()).append("Hi ").append(BoxesRunTime.boxToInteger(given)).toString());

    }


    public YourInteger plus(int given)

    {

        return new YourInteger(given + number());

    }


    public String toString()

    {

        return String.valueOf(BoxesRunTime.boxToInteger(number()));

    }


    public YourInteger(int i)

    {

        number = i;

    }


    private final int number;

}






public final class YourInteger$

{


    public YourInteger intToYourInteger(int x)

    {

        return new YourInteger(x);

    }


    private YourInteger$()

    {

    }


    public static final YourInteger$ MODULE$ = this;


    static

    {

        new YourInteger$();

    }

}





실행 코드의 디컴파일 결과이다. 묵시적 타입 변환은 BoxedUnit이라는 클래스를 활용했다.



        YourInteger a = new YourInteger(5);

        Predef$.MODULE$.println(a);

        a.method(1);

        BoxedUnit b = BoxedUnit.UNIT;

        YourInteger$.MODULE$.intToYourInteger(1).method(3);

        BoxedUnit c = BoxedUnit.UNIT;
       Predef$.MODULE$.println(c);

        YourInteger d = YourInteger$.MODULE$.intToYourInteger(1).plus(2);




아래 scala 코드에 대한 자바코드를 살펴본다.

val c = 1.method(3) println(c)

1.method는 아래와 같은 자바 코드로 변환한다. 



        YourInteger$.MODULE$.intToYourInteger(1).method(3);

        BoxedUnit c = BoxedUnit.UNIT;

        Predef$.MODULE$.println(c);




Posted by '김용환'
,


scala 2.11.8에서 테스트한 코드이다.



* Singleton Object (싱글톤 객체) : class 대신 object 키워드로 정의하고 static 멤버가 없는 객체. 

- static method를 가진다.

- new 로 인스턴스 초기화를 할 수 없다.

- 파라미터를 받을 수 없다.

- first class (1급 계층) 객체이다.

- 타입을 정의하지 않는다. 

- 슈퍼 클래스를 확장(extend)하고, trait를 믹스인(mix in)할 수 있다.




object
Calculator {

def plus(a : Int, b : Int): Long = {
a + b
}
}


- 실행 코드

object Hello {
def main(args: Array[String]): Unit = {
Console.out.println(Calculator.plus(1, 1));
}
}



Companion Class (동반 클래스) : (객체 관점) 클래스의 이름이 싱글톤 객체의 이름과 같다. 


* Companion Object (동반 객체) :  (클래스 관점) 싱글톤 객체의 이름이 클래스의 이름과 같다. 

  - 팩토리 패턴에 유용하다. 



- 예시

class Calculator2(version : String)

object Calculator2 {
def apply(version : String) = new Calculator2(version)
}




* Standalone Object (독립 객체) :  동반 클래스가 없는 싱글톤 클래스이다. 



---------------


컴파일된 scala 객체를 javap로 테스트한다.

Scala 컴파일러는 싱글톤 객체 scala에 대해서 이름 뒤에 $에 붙은 클래스를 생성한다. 총 2개의 .class가 생성된다.

그리고, 일반 클래스는 클래스만 생성한다.총 1개의 .class가 생성된다.



$ javap Calculator.class

Compiled from "Calculator.scala"

public final class Calculator {

  public static long plus(int, int);

}


$ javap Calculator$.class

Compiled from "Calculator.scala"

public final class Calculator$ {

  public static final Calculator$ MODULE$;

  public static {};

  public long plus(int, int);

}


자바에서 스칼라를 호출하는 구조는 Calculator$.MODULES$.plus(int, int)를 호출하는 형태가 될 것이다.


------

Calculator2 동반 객체를 javap로 테스트한다.


$ javap Calculator2.class

Compiled from "Calculator2.scala"

public class Calculator2 {

  public static Calculator2 apply(java.lang.String);

  public Calculator2(java.lang.String);

}


$ javap Calculator2$.class

Compiled from "Calculator2.scala"

public final class Calculator2$ {

  public static final Calculator2$ MODULE$;

  public static {};

  public Calculator2 apply(java.lang.String);

}




Posted by '김용환'
,

[scala] =과 함수 선언

scala 2016. 3. 23. 19:02

scala 2.11.8에서 테스트한 내용이다. 리턴 값이 이상하거나 명확한 리턴값이 없으면 warning이 발생한다.



아래 scala 코드는 Unit을 리턴하지만, 리턴값을 정한 케이스이다. 동작 안된다.

scala> def f():Unit = "this"

<console>:12: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses

       def f():Unit = "this"

                      ^

f: ()Unit


scala> f()

아무것도 출력 안됨




아래 scala 코드는 암묵적인 Unit을 사용했고 =를 안썼지만, 역시 동일하게 동작이 되지 않는다. 

scala> def h() { "this" }

<console>:12: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses

       def h() { "this" }

                 ^

h: ()Unit


scala> h()

아무것도 출력 안됨




아래 scala 코드는 동작 된다. 묵시적으로 함수가 String을 리턴한다. 


scala> def f() = "this"

f: ()String


scala> f()

res11: String = this



아래 scala 코드는 명시적으로 함수가 String을 리턴하는 코드라서 동작한다. 

scala> def h():String = { "this" }

h: ()String


scala> h()

res13: String = this




Posted by '김용환'
,



scala 2.8에서 List의 sort 메소드는 deprecated되었고, 뒤 버전부터 사라졌다. 


  1. defsort (lt: (A, A) ⇒ Boolean) List[A]

    Sort the list according to the comparison function lt(e1: a, e2: a) =&gt; Boolean, which should be true iff e1 precedes e2 in the desired ordering. !!! todo: move sorting to IterableLike

    lt

    the comparison function

    returns

    a list sorted according to the comparison function lt(e1: a, e2: a) =&gt; Boolean.

      deprecated: 
    1. use sortWith' instead

  2. defsortBy [B] (f: (A) ⇒ B)(implicit ord: Ordering[B]) List[A]

    Sorts this List according to the Ordering which results from transforming an implicitly given Ordering with a transformation function.

  3. defsortWith (lt: (A, A) ⇒ Boolean) List[A]

    Sorts this list according to a comparison function.

  4. defsorted [B >: A] (implicit ord: Ordering[B]) List[A]

    Sorts this list according to an Ordering.




http://www.scala-lang.org/api/current/#scala.collection.immutable.List에서도 확인할 수 있다. (현재 2.11.8)




scala> thrill.sort((s,t) => s.charAt(0).toLower < t.charAt(0).toLower)

<console>:14: error: value sort is not a member of List[String]

       thrill.sort((s,t) => s.charAt(0).toLower < t.charAt(0).toLower)

              ^


scala> thrill.sortWith((s,t) => s.charAt(0).toLower < t.charAt(0).toLower)

res11: List[String] = List(aa, bb)


scala> thrill.sortWith(_.charAt(0).toLower < _.charAt(0).toLower)

res12: List[String] = List(aa, bb)


scala> thrill.sortBy(_.charAt(0))

res14: List[String] = List(aa, bb)


scala> thrill.sorted

res15: List[String] = List(aa, bb)


Posted by '김용환'
,