LiveScript 살펴보기 – 02 연산자

이번 글에서는 LiveScript의 연산자에 대해서 살펴보겠다. 함수형 언어 스타일을 도입하면서 코드가 간결해지는 대신에 기본적인 사칙/비교 연산외의 여러 연산자들이 많이 사용된다.

연산자

기본적인 연산자는 JS와 동일하다. 단 모듈로 연산자가 추가되었다 (%%)

  • -3 % 4 : -3
  • -3 %% 4 : 1

제곱연산자는 오른쪽 연관이며, 다른 단항 연산자보다 높은 우선순위를 가진다. ^** 를 같이 쓸 수 있다. 그리고 증감연산자도 있다. (함수형 언어에서 증감 연산자는 가급적 안쓰는게 좋으며, LS 코드에서는 for 문도 순회의 개념이기 때문에 별로 쓸 일이 없다.)

비트연산

비트 연산은 .&. 과 같이 연산자 앞뒤로 .을 붙여서 사용한다.

비교 연산

기본적인 비교 연산자는 JS와 동일하다. 대신에 파이썬 처럼 연속으로 비교할 수 있다. (2 < 4 <= 7 > 3) 비교연산에서 특별한 점은 max, min 연산자가 있다는 점이다. 각각 >?, <? 을 쓸 수 있다.

get-max = -> it.reduce (>?)
get-max [5 1 2 4 3] #=> 5

기본적으로 LS의 비교는 엄격한 동질성을 따진다 (암시적 강제 형변환을 허용하지 않는다.)

2 + 4 == 6 # 2 + 4 === 6, true
2 + 4 == '6' # 2 + 4 === '6', false
# JS에서 2 + 4 == '6' -> 6 == '6' 으로 true이다.

명시적으로 JS의 모호한 비교를 쓰려면 ~= 연산자로 비교해야 한다.

캐스팅

+, - 는 문자열 앞에 쓰이면 숫자값으로 캐스팅하는 용도가 된다.

논리 연산

&&, || 도 똑같이 쓰이지만, and, or이 더 자연스럽다. 또한 이들은 함수/인자 사이에서 결합/분배가 발생한다.

(f or g) 1 # f(1) || g(1)
(f and g or h) 3 4 # f(3, 4) && g(3,4) || h(3, 4)

in 과 of

for 문에서 리스트의 각 원소를 순회하거나, 객체의 각 키를 순회하는데는 각각 in, of를 사용한다. 또한 이 키워드는 연산자로 쓰일 때는 특정 원소나 키가 리스트나 객체에 있는지를 검사한다.

list = [7 8 9]
2 in [1 2 3 4 5] # 'contains`의 의미로 쓰였다.
3 in list
\id of id: 23, name: \rogers

파이핑

|> 표현은 파이핑으로 좌변에서 계산된 표현식의 결과를 우변으로 넘겨줄 수 있다.

expression1 = <# ... >
expression1 |> func # func(expression1)

4
|> (+ 1)
|> even
# even( (function(x){ return x + 1;})(4))
# => false

함수 합성

>>, << 은 함수를 합성한다.

(f >> g) x # g(f(x))
(f << g) x y # f(g(x, y))

리스트 결합

  1. ++ 을 사용한다.
  2. * 3 과 같은 식으로 특정 리스트를 n 번 반복할 수 있다.
  3. * 'sep' 은 문자열 리스트를 구분자를 써서 하나의 문자열로 join 할 때 쓴다.

그외 단항 연산자는 리스트 앞에 붙여서 스프레딩 할 수 있다.

문자열 연산

문자열 연산은 여러 면에서 리스트와 유사한 점이 많다.

  • + 으로 두 문자열을 결합할 수 있다.
  • += 연산도 지원한다.
  • * 3 의 반복 표현도 지원된다.
  • .replace, .split 을 정규식 객체와의 연산으로 처리할 수 있다. str - /reg/, say year / \y
'say yeah' - \a #=> 'sy yeh'
'say yeah' / \y #=> ['sa', ' ', 'eah']
'x' * 3 #=> 'xxx'
string = 'hello' + ' ' + 'world'
string += ' everyone'

?

?는 기본적으로 주어진 심볼의 존재여부를 확인하는데, 여러 가지 맥락으로 쓰인다.

bigfoot ? 'grizzly bear' 
# bigfoot이 undefined이므로 'grizzly bear'가 대체 선택된다.

string = \boom if window?
# window가 있으면 string=\boom이다. 

document?.host # document가 있으면 .host를 액세스한다.

클래스와 타입

isinstanceof 는 해당 객체가 오른쪽의 타입의 인스턴스인지 검사한다. 이 때 우변은 리스트 일 수 있는데 리스트인 경우 나열된 타입 중 하나에 매칭하는지 검사한다.

typeof는 특정 객체의 타입을 조사한다. JS와 동일한데, typeof!로 쓰면 해당 타입의 이름을 얻을 수 있다.

now = new Date!
now isinstanceof Date # true
now isinstanceof [Date, Object] # true
# 이 때 타입명은 constructor이기도 하기 때문에 callable하다. 따라서 쉼표 넣어야 한다.

typeof /r/ # 'object'
typeof! /r/ # 'Regexp'

객체의 특정 키를 삭제하는 delete는 삭제된 키의 값을 리턴한다. JS의 delete는 삭제가 실제로 일어났는지 여부만 리턴하는데, 이는 delete!를 사용해야 한다.

프로퍼티 복사와 클론

<<< 은 우변에서 좌변으로 특정 객체의 고유 정보를 복사해준다. <<<<은 모든 프로퍼티를 복사한다. 객체를 클론하는 것은 ^^를 이용할 수 있다. 클로닝은 실제 값을 복사하는 것이 아니라, 동일한 프로토타입의 껍데기를 만든다. 따라서 프로토타입의 속성은 복사될 것이다. 아래는 객체의 사본을 만드는 것인데, 클로닝한 다음에 import해서 프로퍼티를 다시 복사 받아야 한다.

obj = { one: 1 }
obj2 = ^^obj <<< obj
obj2.two = 2
obj2 # {two: 2}

공식 홈페이지에서는 clone 하면 프로퍼티까지 복사되는 것으로 알고 있는데, 그렇지 않은 거 같다? 이 이슈는 깃헙에도 올라와 있는데, 공홈 내용을 안고치네…

반출과 반입

export 연산자는 모듈을 정의할 때 exports보다 편리하게 쓸 수 있다.

export func = ->
export value
export value-a, value-b, value-c
export
  a: 1
  b: -> 123

require!를 쓰면 외부 파일의 모듈을 반입할 수 있다.