이번 글에서는 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))
리스트 결합
++
을 사용한다.* 3
과 같은 식으로 특정 리스트를 n 번 반복할 수 있다.* '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!
를 쓰면 외부 파일의 모듈을 반입할 수 있다.