배열의 음수 인덱싱과 WHERE() 함수

최근, Maserati 드라이버 이상우 옹께서 “배열 내 마지막 원소값을 조회하는 방법”에 대한 글을 올리셨습니다. 음수를 이용하여 배열을 인덱싱하는 기능은, Python 같은 현대 언어에서 이미 사용되고 있던 것입니다.  IDL에서는  8.0버전부터 IDL의 기본 문법을 살짝 진보시키며 등장한 기능들 중 하나입니다. 편리하고 직관적이 되지요. 음수로 배열을 인덱싱하면, 배열의 맨 끝에서부터 원소를 뽑아내게 됩니다. 다만 IDL에서 빈번히 사용되는 WHERE 함수와 미묘하게 충돌이 되는 부분이 있어 잠깐 살펴보겠습니다. 새 버전의 IDL을 사용하실 때 살짝 주의하시면 됩니다.

IDL> x=[1,2,3,4]
IDL> ok=where(x lt 0)

;X 배열에서 0보다 작은 값은 없으므로 당연히 이와 매칭되는 요소가 없지요. 이 때 좌변 OK 변수에 어떤 값이 리턴되나요?
IDL> print, ok
-1

;-1이 리턴됩니다. 음수로 배열을 인덱싱하는 일을 꿈도 꾸지 않았던 아주 옛날부터 WHERE 함수는 매칭되는 원소가 없을 경우 -1을 리턴하도록 했습니다.
;그 결과는 이제 IDL 8.x에 와서 조금 이상한 결과로 이어집니다.

IDL> print, x[ok]
4

;위에 보다시피 배열번호 -1로 인덱싱을 하므로 배열의 맨 뒤쪽의 원소를 뽑아 냅니다. 원하는 결과는 아닐 것입니다.
;IDL 8.0부터는 !NULL이라는 시스템 상수도 새로 등장을 했습니다. 아무것도 없다는 의미, 즉 NULL을 상징하는 값입니다.
;그래서, 새 버전의 WHERE 함수는 다음과 같이 /NULL 키워드를 사용할 수 있습니다.
IDL> ok=where(x lt 0, /NULL)
IDL> print, ok
!NULL

;배열 요소 중에 매칭되는 값이 없을 경우 !NULL 상수를 리턴하게 하는 것입니다. 결과는 다음과 같이 훨씬 안전해집니다.
IDL> print, x[ok]
!NULL

;사실, 지금 버전에서도 여전히 유효하고, 예전부터 꼭 사용하라고 강조되던 방법인데요, WHERE로 검색된 요소의 개수를 체크하는 것입니다.
;일종의 안전장치를 거는 거죠.
IDL> ok=where(x lt 0, ct)          ;아시죠? ct 변수에는 이제 조회된 원소가 몇개인지 count가 저장됩니다.
IDL> print, ok
-1
IDL> print, ct   ;일치하는 값이 없으므로 ct 변수에는 0이 들어 있죠.
0

;이제 if 문을 이용하여 ct 변수를 검사하면, 불상사를 막을 수 있습니다.
IDL> if ct gt 0 then print, x[ok]

IDL 8.0 이전 버전에서도 count를 조사하지 않으면 불상사는 일어났습니다. 이 때는 배열을 음수로 인덱싱할 경우 “말도 안되는 인덱스다”라는 에러를 냈거든요. 의도하지 않는 결과인데 은근슬쩍 넘어가며 틀린 답을 내는 것보다는 차라이 에러를 내는 것이 낫지만, 어느쪽이든 피하는 것이 상책입니다. 자, 이제는 두가지 안전장치가 있습니다. 1) count를 이용하여 매칭되는 원소 개수를 검사하는 방법과 2)/NULL 키워드를 이용하여 매칭되는 원소가 없음을 명시하는 방법… 어느쪽이든 꼭 사용하시기를 권장합니다.