레이블이 Scala인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Scala인 게시물을 표시합니다. 모든 게시물 표시

2014년 3월 27일 목요일

[일기] Scala 04 조사 보고

진보된 For 루프와 For 강화문.

 잘 알고 있는 C++/Java의 for 기본 구문 형태는 동일하다.

 그러나, 우리는 다수의 변수 생성문을 지정할 수 있다. '<-'를 이용하여 수행되며 이들은 각각 세미콜론으로 구분된다.


[그림 1]

또한 조건 구문을 생성할 수 있다.


[그림 2]

또, 내부에 변수를 추가 생성하여 이용할 수 있다.


[그림 3]

 기막히군. 그러나 '산출'(yield) 키워드를 이용하면 '컬렉션'을 리턴한다.


[그림 4]

 게다가 첫번째 생성문(?; generator)하고도 같이 호환된다.


[그림 5]

 for 내의 변수들인 c나 i는 외부에서 별도로 미리 선언할 필요는 없다.

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


 함수.

 함수뿐만 아니라 메서드를 갖고 있다. 누가? 스칼라가. (자바는 함수와 유사한 기능을 정적 메서드로 제공했다.)

 선언은 다음고 같다.

 def abs(x: Double) = if ( x >= 0 ) x else -x

 척 보면 척 알수 있다.

 def는 함수 선언의 시발점이며 뒤의 abs는 함수명, 뒤는 인자와 타입(':'(콜론)으로 구분)을 적는다.

 그리고! '='(대입 기호)를 입력한다. 이것을 중요히 생각해야 한단다.

 뒤는 내용이며 즉, 몸체다.

 반환형은 문장의 맨 마지막 값이며 이는 블럭과 같다. 고로 return 키워드는 별도로 입력되지 않는다. 그럴 필요도 없다.
 (사실 return도 메서드로 되어 있다.)

여기서 반환형의 타입을 지정하는 부분이 없었다. 이는 사실 평상시에는 필요없다. 그래서 필요할 때만 지정하곤 하는데 그 때는 재귀호출을 할 때이다.

 def fac(n: Int): Int = if (n <= 0) 1 else n * fac(n - 1)

 이와 같이 팩토리얼을 구하는 메서드를 구현할 경우 재귀로 하면 편하다. 이 때 만일 반환 타입 명시를 하지 않으면 실행할 때 반환한 타입이 무엇인지 모르기 때문에 에러가 난다.

 ':'(콜론)과 타입이 추가되어 입력되면 그것은 반환타입을 명시하는 부분이 된다.

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

 디폴트 매개변수.

 이런 이런, 자바에 없던 존재가 등장한다.

 def decorate(str: String, left: String = "[", right: String = "]") = left + str + right

이를 기준으로 유추가 가능하다. C++이 생각나거나 C#이 생각난다. (사실 알아보는 중에 C#의 문법과 더욱 비슷하다고 느꼈다.)


[그림 6]


 이에 별말없이 [그림 6]으로만 대체한다.

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

 가변인수

 def sum(args: Int*) = {
      var result = 0
      for (arg <- args) result += arg
      result
 }

유일하게 보이는 차이점은 파라미터 타입 옆에 바로 존재하는 애스터리스크 문자. 포인터인줄 알았으나 아니다.

val s = sum(1, 4, 9, 16, 25)

이와 같이 가변 길이의 인자를 받는다.


[그림 7]

 그런데 리스트 자체를 넘길수는 없다.


[그림 8]

 이는 큰 불편이 아닐 수 없지 않을 수 없다. 
 그래서 이를 해결하기 위해 ':'(콜론)을 통해 부가 설명을 추가한다. '_*'(뭐라 읽어야 해? 한쪽눈만 뜬 인면이모티콘) 키워드를 추가하면 범위값들을 개별 값들로 나누어 함수에 각각 입력해 준다.


[그림 9]

  재귀에서도 확장할 수 있다고 하던데,


[그림 A]


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

프로시저

 함수에 리턴값이 없는 것을 특별히 부르는 말이다.
 
def procedures(parameter : String) { // '='가 없어.
 ...
}

이는 

def procedures(parameter : String) : Unit = {
 ...
}

과 같다.

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

게으른 변수(?)

 변수의 초기화를 변수 접근이 이루어질 때까지 미루는 경우 이 변수를 지칭하는 이름.

 맨 앞에 lazy만 붙인다.

 lazy val variable = 123;

 만일 변수 variable에 한번도 접근 하지 않는다면 저 변수는 값 할당 전혀 안 받는다. 여기는 예가 좋지 못하지만 인터넷에는 이러한 문장이 있다.

 lazy val words = io.Source.fromFile("/usr/share/dict/words").mkString

 파일의 모든 단어를 대입하는 구문이라고 했던가.. 저 문장은 실행되기 비싸(?)기 때문에 lazy를 도입했단다.

c"=*B,2*)(&2E*">*7,p$*:,7=90*,0*(,7>A,$*C9)A992 val ,2; def.*!"1#,%9
val words = io.Source.fromFile("/usr/share/dict/words").mkString
// W:,7=,)9;*,0*0""2*,0 words &0*;9>&29;
lazy val words = io.Source.fromFile("/usr/share/dict/words").mkString
// W:,7=,)9;*)(9*>&%0)*)&19 words &0*=09;
def words = io.Source.fromFile("/usr/share/dict/words").mkString
// W:,7=,)9;*9:9%$*)&19 words &0*=09;

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

예외처리.

 throw new IllegalArgumentException(" I am super exception. puhahahaha! ");

 이 친구는 Nothing을 리턴한다.

if (x >= 0) sqrt(x)
else throw new IllegalArgumentException("x should not be negative")

 에서 x가 음수면 위 문장은 Nothing이 리턴된다.

다른 건 다 필요없다.

try {
process(new FileReader(filename))
} catch {
case _: FileNotFoundException => println(filename + " not found")
case ex: IOException => ex.printStackTrace()
}

이것만 잘 보면 된다.
그렇게 없던 case가 왜 여기서 보이는지... 예외 선택구문이라나... '패턴 매칭'이라던데 지금은 모르겠으나 case 뒤가 패턴 이름인 듯하다. 이름에 선입견은 갖지 말라고 했었다.

try { ... } catch { ... } finally { ... } 도 가능하다.

너무 길었군. 나누기 귀찮았다.

당시 알아냈던 내용 보고를 여기에서 종료한다.

[일기] Scala 03 조사 보고

그때 '심심한' 때가 찾아왔다. 그후 찾아본 내용을 정리하여야 했다. 쉽지 않았다. 문제는 내 글쓰기 실력이었다. 그러나 드디어 막바지에 도달했다. 이 글이 당시 찾아본 내용의 거의 마지막이다.

제어문.

 IF/ELSE
  약방의 감초와 같이 바늘의 실과 같이 군대의 이등병같이 항시 존재하는 구문이다.

  if ( x > 0 ) 1 else -1

 눈을 크게 뜨고 보지 않아도 이는 명백히 조건 분기문으로 보여진다만, 특이한 것은 분기 내용이다. 덩그러니 1하고 -1만 쓰여 있지 않은가.


[그림 1]

 무슨 일이 일어났는가. 저 IF/ELSE 구문이 값을 반환했다는 것이다. 보통 눈치가 없는 사람이라도 이것이 삼항 연산자(?:)와 유사하다는 사실을 간파할 수 있다. 그렇다. 스칼라에는 삼항 연산자가 별도로 존재하지 않는다.

 또한 반환 타입이 확정돼 있는 것이 아니므로 무엇을 리턴하든 상관이 없다.

  if ( x > 0 ) "hello I'm string" else 1021

 으로 문자열이나 숫자를 리턴할 수도 있다. 딱히 세미콜론도 없다.

  else는 당연히 생략 가능하다. 단, 이 때는 'else ()'가 자동으로 생성된다고 보면 된다. ()는 Unit 타입니다.

 이런, '스위치'가 없다.

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

 문장 종결.

 세미콜론을 이용한다. 한 줄에서 여러 문장을 작성할 때는 반드시 구분한다. 맨 뒷 문장은 세미콜론이 필요가 없다.
 그룹화된 복문은 '중괄호'로 묶는다.

 스칼라에서 명령을 입력할 때 주의하여야 한다. 가령,

 val test = x0 + (y1 - y0) * x1 + 4.4 * ( z0 + z1 ) * ( z1 - z0 )

 의 수식을 작성할 때, 너무 길어서 두 줄로 나누어 쓰고 싶다고 한다면 개행 직전에 입력된 문자열은 일반적으로 절대 문장의 끝에 올 수 없는 것들로 이루어져야 한다. 이를 보통 행말 금칙어라 호칭한다. 즉, 이 행말 금칙어와 허용 가능한 문자를 제외한 모든 문자를 개행 전에 입력해야 한다.

val test = x0 + (y1 - y0) * x1
 + 4.4 * ( z0 + z1 ) * ( z1 - z0 )    안 좋은 예(X)
val test = x0 + (y1 - y0) * x1 +
 4.4 * ( z0 + z1 ) * ( z1 - z0 )      좋은 예 (O)

이상.
-------------------------------------------------------------------------------

블록 할당.


[그림 2]

 In this case, I'll leave only this picture 2 just here. Just check what functions the block assignment has.

오류는 필요한 패키지를 임포트 안해서 발생했다. 

 '블럭의 값은 블럭 내의 마지막 문장의 값이다'

이것을 알면 된다.

 참고로 스칼라에서의 할당문(대입문)은 리턴값이 Unit이라는 사실에 유의하여야 한다. 이 말인 즉슨,

 { "hello", x = 32 }

이 블럭의 값은 결국 마지막 문장의 값, Unit이라는 것이다. 절대 32가 아니다. 다음 두 개 그림을 잘 비교하면 이해가 된다.


[그림 3]

 할당에러가 Unit를 Int에 못 대입한다는 내용으로 이루어져 있다.

[그림 4]

 블럭의 값이 첫째(윗부분)에선 마지막 문장이었던 y의 값으로 리턴되었으나, 둘째(아랫부분)에선 리턴값이 없음을 확인할 수 있다.

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

2014년 3월 26일 수요일

[일기] Scala 두번째 자료 조사.

스칼라에 주로 쓰이는 자료형이 있었다.

 Byte Char Short Int Long Float Double이 그 7개이며, 이 외 Boolean이 존재한다.

 그러나 나를 놀라게 한 것은 이들은 원시 자료형이 아니라 사실 스칼라의 클래스 타입이라는 것이다.

이는 다음을 가능하게 한다.


[그림 1]

 흥미롭게도, 이것도 가능하다.


[그림 2]

 메서드의 목록을 조회했다는 것, to(10) 메서드를 호출하여 1 ~ 10의 범위값을 얻어냈다는 것. 거참.

 자료 조사 결과, 스칼라에는 Wrapper 형이 필요가 없다. 컴파일러가 원시타입과 Wrapper를 알아서 전환시키기 때문이다. 이에 Int형의 배열을 만들게 되면 가상 머신을 통해 int[]형을 얻을 수 있다고 한다. 시험은 아직 못했다.

 이전에 확인한 것과 같이 문자열은 스칼라에서 기반 클래스인 String(java.lang.String)에 의존한다. 그러나 이는 연산에서StringOps라는 수백개 이상의 연산을 가진 클래스로 확장될 수 있다. 가령,



[그림 3]

[그림 3]에서 보여지는 것과 같이 intersect라는 메서드를 호출하였다. 이는 기존 자바 개발자라고 한다면 java.lang.String의 메서드는 아님을 알고 있다. 이 말인 즉슨, 문자열 "SuperMarketPower"는 암묵적으로 StringOps로 변환되며 그리고 이 클래스의 intersect 메서드가 수행된다는 뜻이다. intersect는 동일한 문자를 가져오고 이를 이어서 문자열로 반환한다.

 이와 같이 스칼라에는 RichIntRichDoubleRichChar 등 수많은 클래스가 존재하며 이들은 원시 친척(?)뻘인 Int, Double, Char에 대해 기능을 더 편리하게 제공하기 위해 제공된다. [그림 2]의 '1.to(10)'의 to 메서드는 사실 RichInt의 메서드라고 한다.

 즉, Int 형이었던 숫자 1이 RichInt로 변환된 후 to 메서드가 그 값에 대해 호출된 것이다.

 BigDecimal도 있다는데....

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

스칼라의서의 산술연산은 우리가 C++이나 Java에서 수행한 것과 같이 기대하던 결과를 내놓는다.

가령 val answer= 8 * 5 +2

를 한 경우 우리는 42값을 얻었다.

즉, + - * /와 같은 연산자는 그들의 범상적인 정의 그대로 역할을 수행해낸다. 이는 또한 비트 연산자인 & | ^ >> <<도 마찬가지이다. 그러나 한 가지 주목해야하는 측면이 있다면 이들은 실제로 메서드이다.

 a + b 는 사실 a.+(b)의 축약형이다.

여기서 +는 메서드의 이름이다. 놀랐다. 스칼라에서 메서드의 이름은 반드시 알파벳과 숫자로만 구성되어야 한다는 선입견을 가지면 안된다. 어떠한 심볼을 통해 메서드 이름을 만들 수 있었다. 예를 들어 BigInt 클래스에는 /%메서드가 존재하며 이는 나누기 연산의 몫과 나머지의 쌍을 반환한다.

 일반적으로 우리는 

 a method b

의 형태를 a.method(b)의 축약형으로써 쓸 수 있다.

여기서 method는 두 개의 파라미터를 가진다(하나는 묵시적으로 하나는 명시적으로 지정된다). 이전의 

1.to(10)

의 경우

 1 to 10

으로 쓸 수 있다는 것!!

 그런데 스칼라에서는 보통 위치에 따라 후위/전위를 구분하여 개념을 익혔던 증감 연산자(++, --)는 존재하지 않았다. 이 때신 그저 +=나 -=를 이용해야 한다.

손쉽게 BigInt와 BigDecimal 객체에 관한 연산도 수행할 수 있다.


[그림 4]

Java에선 저런거 연산자로 처리 못하고 메서드를 이용했어야 했는데 참 간단해졌다.

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

 함수와 메서드의 호출이 구분되어 있다. 객체 지향에서 '함수'란 존재하지 않는 줄 알았는데 존재한다.
이는 정적 메서드와 유사하나 다른 것이라 한다.

 보통 Java에서 많이 썼던 정적 메서드하면 아직 경험이 부족한 나는 Math 클래스가 생각난다. 이와 같은 함수를 모아 둔 것이 '패키지'로 스칼라에도 있다고 하는데 그것이 scala.math 패키지란다.

 이 패키지를 첨부하는 것 단순히 import 하는 것이다.

 import math._

이게 끝이다. 여기서 눈치를 못 챘었는데 scala라고 시작하는 접두어(?)는 생략 가능하다는 점과 '_'(underline)'*'(wildcard)대신에 쓰인다는 것이다.

 또한 파라미터 없는 메서드는 괄호 없이 사용 가능하다.


[그림 5]

 내가 임포트하는 것 까먹은 걸 어떻게 알고 저리도 친절하게 모른다고 대답한다. import 수행 결과 예상하던 결과인 제곱근 2의 값을 얻어냈다.

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

위 것을 찾다가 부가적으로 알아낸 것이 있어 작성하는 추가사항.

 이름하여 apply 메서드.

 스칼라에서 함수 호출 형태와 같은 구문 사용은 매우 흔하다고 한다. 예를 들어 k라는 문자열 변수가 있을 때 k(2)는 문자열의 세번째 문자를 반환한다.


[그림 6]

이것이 어찌 가능하냐 하니, apply 메서드 덕택이란다.

 찾은 자료는 예시까지 아주 잘 들어놓았다. StringOps 클래스에서 우리는 다음의 메서드 정의를 확인할 수 있다.

def apply(n: Int): char

아직 메서드에 대해 잘 모르지만 각 구문이 대충 무엇을 의미하고 역할이 무엇인지는 유추가 가능할 것 같다.

즉 "Hi There This is not your food"(1)은 사실 "Hi There This is not your food".apply(1)의 축약형이다.

이는 많이 쓰인다. 다른 예로

BigInt("123123456456789789")은 BigInt.apply("123123456456789789")의 축약형이다.

이것들이 새로운 객체를 반환하다 보니 스칼라에서 객체를 생성할 때 주로 이 방법대로 하는 것이 전통(?)적으로 내려오고 있다고 한다. 그러면 익숙해져야지. 즉 new 키워드 없이도,

 BigInt("111111111") * BigInt("333333333")

을 통해 BigInt 하나를 얻어낸다.


[그림 7]

이상. 알아본 바의 정리 2파트이다.

2014년 3월 25일 화요일

[일기] Scala 확인 내역 보고.

[http://scala-lang.org/download/]에서 설치파일 받고 실행하는 것으로 설치 끝.
물론, 환경변수 PATH를 변경해야 했다.

윈도우 명령줄 창에서 'scala'를 입력하였다.



[그림 1]

8 x 5 + 2 를 수행하고 싶어졌다. 타이머가 8분 52초를 지나고 있었다.


[그림 2]

결과 42라는게 보였다.

 답은 변수 'res0'에 저장되며, 이를 입력으로 사용할 수 있었다.



[그림 3]

또한 다음과 같이 입력한 내용에도 오류 없이 결과가 나왔다.


[그림 4]

 결과가 나타내듯, 우리는 결과 자료의 타입도 함께 볼 수 있었다. 가령 Int, Double, String이 그것이다.

 뿐만 아니라 메서드도 사용할 수 있었다. 검색 결과 무난하게 toCharArray, toLowerCase, toString, toUpperCase가 있었다.

 먼저, res2에 '.'을 찍어 구분한 뒤, 'to'까지만 입력 후 탭 키를 눌렀다.


[그림 5]

가능한 메서드 목록이 뜬다. 다음은 수행 결과이다.


[그림 6]

 메서드에 괄호'()'를 입력하지 않았는데도 결과는 출력되었다. 철자 하나 잘못 썼더니 에러를 내었고 틀린 문자위치를 표현해 주었다. 
 위 메서드를 검색한 사이트에서 살펴본 결과, 비록 스칼라 프로그램이 명령을 받아 처리하는 것으로 보아 인터프리터로 보일 수 있다고 하였는데, 사실은 아니라고 한다. 장면 뒤에서 사용자의 입력이 즉시 바이트 코드로 컴파일되고 그 후 그 바이트코드가 자바 가상 머신에서 동작하는 방식으로 실행된다고 하였다. ('참고 : 내 컴퓨터가 문제인지 첫 문장 수행 때 시간이 좀 걸렸다.')

 변수 선언. 키워드는 2개가 존재한다.

 첫째, val


[그림 7]

 변수 하나 선언하려다 애 먹었다. 먼저, 맨 처음 변수명을 잘못 적어 취소하려 했더니 되지 않는다. 그냥 엔터를 누르니 '이어 적기'가 자동으로 수행되었다.

 두 번째는 '='가 오지 않았으니 처리하라고 불평하고 있는 내용이다.

 세 번째는 일부로 나누어 썼다. 결과 타입은 Int로 값은 42로 들어갔다(물론 변수명 또 틀렸는데 귀찮아서..).

 주의할 것은 우리는 변수의 타입을 지정하지 않았다는 것이다. 이 타입은 값이 초기화될 때 정해지며 변수는 선언과 동시에 반드시 초기화되어야 하므로 타입이 판단되지 않는 일은 없을 거라 생각한다.

 이 변수를 참조하여 표현을 만들 수 있다.



[그림 8]
 값이 21.0인 실수형 값이 나왔다.

 물론 타입을 직접 명시하는 것도 가능하다.


[그림 9]

타입을 'Any'로 지정하기도 했는데 사실 무의미하다. 콜론(':')으로 타입을 구분한다.
자바의 String specifyVariable = null 의 방식과 차이가 있다.

val 키워드를 이용하여 선언된 변수는 사실 '상수'이다.


[그림 A]

바꾸려 하니까 뭐라한다.
변수를 지정하기 위한, 그리고 아직 언급 안했던 키워드는 'var'이다.



[그림 B]

위쪽 에러는 초기화가 반드시 필요하다는 것이 증명된 부분이다.
그 외에는 변수에 값을 재할당해도 문제가 없음을 알 수 있다.

동시에 다수의 선언을 포함하는 것 또한 가능하다.


[그림 C]

이상. 왜 심심할 때 찾아보라고 하셨는지 이해가 된다.