Practice/Linux

쉘스크립트에서 빈 문자열 검사 방법

쉘스크립트에서 빈 문자열의 검사할 때, 'test' 명령어와 '-z' 옵션을 사용한다.

test -z string

string이 빈 문자열이면 참을 반환한다.
'test'와 동일한 기능의 다른 표현으로 '['가 있다.[각주:1]


그런데, 오래된 고수들이 작성한 많은 쉘스크립트를 보면 아래와 같이 사용하는 것을 심심치 않게 본다.

if [ x$1 == x ]; then
    echo $1
fi

단순하게 빈 문자열과 비교하거나

if [ $1 == "" ]; then
    echo $1
fi

'-z' 옵션-빈 문자열을 검사하는 옵션-을 사용하지 않은 이유가 뭘까?

if [ -z $1 ]; then
    echo $1
fi


'-z' 옵션의 경우, $1이 "-n", "-p" 등처럼 옵션 문자열처럼 들어올 경우 문제가 될 수 있다.
예를 들어, $1이 문자열 "-n" 일 경우, 'test -z $1'이 'test -z -n'으로 처리될 수 있는데,
옵션에 따라서 옛날 버젼의 test에서는 문제가 될 수 있다.
반면, 'x$1 == x'의 방법은 'x-n == x'로 해석되어 문제를 일으키지 않는다.


'[ $1 == "" ]' 로 비교하는 경우, $1이 빈 문자열이면, [ == "" ]으로 평가되어 실행된다.
'=='의 왼쪽 피연산자가 존재하지 않음으로, "==: unexpected operator"와 같은 오류를 발생한다.
반면, 'x$1 == x'의 방법은 'x == x'로 평가됨으로 이러한 문제에서 자유롭다.


Bash를 대상으로 작성한다면, 이런 문제에서 좀 더 자유로운 확장 검사 명령어인 '[['를 사용하는 것을 추천한다.
여기서 예로 든 어떤 방법을 사용하다고 하더라고 문제가 되지 않는다.
확장 검사 명령어에 대한 것은 아래 참고 자료 링크를 참고한다.


참고 자료:
KLDP 포럼: bash스크립트에서 빈문자열 체크 방법
확장 검사 명령어

  1. 사실 '['는 마치 문법처럼 보이나, 실은 'ls', 'rm'과 같은 명령어이다. <pre class="brush: plain">$ ls -l /usr/bin/[ -rwxr-xr-x 1 root root 51920 Feb 18 2016 /usr/bin/[* </pre> '['는 끝에 오는 ']'를 연산의 내용으로 취급하지 않는다. [본문으로]
저작자 표시 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Practice/Lisp

Common Lisp으로 구현한 트리(Tree)를 중위 순회(In-order Traversal)하는 반복자(Iterator)

사소해 보이는 연산 뒤에 숨어있는 것 은 인턴 전화 면접에서 깨달은 내용을 담고 있다.
이 글은 인턴 면접을 소재로 하는데,
면접의 내용 중에 트리(Tree)를 중위 순회(Inorder Traversal)하는 반복자를 구현하는 것이 있다.
이 글의 필자는 C++[각주:1]로 구현하였다.


평소 업무에서 Common Lisp을 쓸 기회가 없어서 자꾸 잊어버린다.
도구를 갈고 닦지 않으면 녹슬기 마련이다.
간단하게 트리의 중위 순회를 Common Lisp으로 작성해봤다.


순회 순서


  1. 왼쪽의 하위 트리를 순회한다.
  2. 자신(중간)
  3. 오른쪽의 하위 트리를 순회한다.


순회할 첫번째 노드 구하기 : begin

  • 최상위 노드의 가장 왼쪽에 있는 노드가 첫번째 노드이다.
    구현은 왼쪽 하위 노드를 재귀적으로 찾는다.
(defun begin (node)
  (if left-node
    (begin left-node)
    node))


다음 방문할 노드 얻기 : next

  • 순회 순서에 의해서 자신 이후에는 오른쪽 하위 트리를 순회한다.
    따라서, 다음 노드는 오른쪽 하위 트리의 가장 왼쪽의 노드이다.
    (begin (right node))
  • 오른쪽 하위 트리가 없다면, 이 단계에서는 더 이상 방문할 곳이 없다.
    가장 가까운 상위 단계의 중간 노드를 찾는다.[각주:2]
    (mid node)
(defun next (node)
  (if right-node
    (begin right-node)
    (mid node)))


다음에 순회할 중간 노드 찾기 : mid

  • 오른쪽(순서 3)에서 올라왔다면, 이 단계에서는 더 이상 방문할 곳이 없다.
    (if (eq node (right parent-node))
      ;; ...
      )
    다시, 가장 가까운 상위 단계의 중간 노드를 찾는다.
    (mid mid-node)
  • 아니라면, 왼쪽(순서 1)에서 올라왔다.
    순회 순서에 따라서 중간 노드를 반환한다.
    mid-node
(defun mid (node)
  (when mid-node
    (if (eq node (right mid-node))
      (mid mid-node)
      mid-node)))


최종 코드

(defun begin (node)
  (let ((left-node (left node)))
    (if left-node
      (begin left-node)
      node)))

(defun next (node)
  (when node
    (labels ((mid (node)
               (let ((mid-node (parent node)))
                 (when mid-node
                   (if (eq node (right mid-node))
                     (mid mid-node)
                     mid-node)))))
      (let ((right-node (right node)))
        (if right-node
          (begin right-node)
          (mid node))))))


2016/01/13 - [Practice/Lisp] - 암호 문자열 감추기 문제 풀이 : Common Lisp, Scheme

2013/08/20 - [Theory/Data Structure] - GLib의 GTree로 알아보는 균형 이진 트리 알고리즘

  1. C라고도 볼 수 있는데, 필자가 연산자 오버로딩을 언급해서 C++로 보았다. [본문으로]
  2. 상위 단계로 올라갈 때, <ul style="list-style-type: disc;"> <li>(순서 1)이라면, 중간 노드를 찾는 것이고,</li> <li>(순서 3)이라면, 다시 상위 단계로 올라가서 이를 반복하게 된다.</li> </ul> 결과는 <ul style="list-style-type: disc;"> <li>순회하지 않은 <b>중간 노드</b>를 찾거나</li> <li><b>중간 노드</b>를 못찾거나 (왜? 모두 방문했으니까)</li> </ul> 둘 중에 하나이다.<br /> 위 과정은 mid 함수 구현에서 다시 설명한다. [본문으로]
저작자 표시 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Practice/Lisp

Lisp에서 클로저(closures)

클로저란 함수가 lexical[각주:1] 환경(environment)의 변수를 참조하는 것을 말한다.


예를 들면, 아래 코드를 살펴보자.

(defparameter *plus-10* (let ((x 10)) (lambda (y) (+ x y))))

X와 Y를 더하는 함수 객체를 만들고, *PLUS-10*이 이를 가르키게 한다.


Lexical 변수 X는 LET 구문 블럭이 생성한 환경에 갇혀서 외부에서는 접근할 수 없다.
LET 구문 블럭이 종료되었다는 것은 실행 흐름이 LET이 생성한 환경에서 벗어났다는 것을 의미한다.
그럼에도 불구하고, *PLUS-10*를 호출하면 X 변수를 참조할 수 있다.

CL-USER> (funcall *plus-10* 20)
30


함수 객체가 다른 곳(예제에서는 dynamic 환경의 *PLUS-10*)에서 참조되고 있으면,
이 함수 객체가 속한 환경 역시 사라지지 않는다.
변수(예제에서는 X)를 만나면 자신이 속한 환경을 거슬러 올라가며 찾는다.


함수의 호출 과정에서도 lexical 변수를 생성한다.
함수의 환경을 생성하고, 이 환경에 메인 함수의 인자를 생성하여, 전달받은 값을 참조하게 한다.


이것을 응용하면 좀 더 재미있는 함수를 만들어볼 수 있다.

(defun plus-x (x) (lambda (y) (+ x y)))

PLUS-X 함수는 고정된 어떤 값을 더하는 함수를 자유자재로 만들 수 있다.[각주:2]
15를 더하는 함수를 다음과 같이 만들 수 있다.

(defparameter *plus-15* (plus-x 15)


이와 같이 lexical 환경에서 정의된 함수가 lexical 변수를 참조하는 것을 클로저라고 한다.


2016/02/18 - [Practice/Lisp] - Lisp에서 lexical과 dynamic 변수 타입


  1. lexical이 무엇인지는 <a href="http://unipro.tistory.com/190" target="_blank">Lisp에서 lexical과 dynamic 변수 타입</a>를 참고하라. [본문으로]
  2. curring과 비슷한 개념이라고 생각하면 되겠다. [본문으로]
저작자 표시 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Practice/Lisp

Lisp에서 lexical과 dynamic 변수 타입

Lisp에는 lexical과 dynamic이라는 두가지 변수(variables) 형태가 존재한다.


lexical 변수는 C나 Java 같은 언어에서 말하는 지역(local) 변수를 생각하면 된다.
함수의 매개변수(parameters)나 LET 구문의 변수가 이런 타입니다.


함수의 매개변수

(defun foo (x y) (+ x y))

함수를 호출하면, 이 함수에 전달하는 인자(arguments)와 연결(binding)하는 매개변수를 생성한다.
이 변수는 함수 내로 범위가 한정된다.


LET 구문의 변수

(let ((x 10) (y 20)) (+ x y))

LET 구문 역시 함수 호출과 동일한 효과를 가진다.
LAMBDA로 표현하면 함수의 매개변수와 동일한 형태라는 것을 알 수 있다.

((lambda (x y) (+ x y)) 10 20)


dynamic 변수는 전역(global) 변수와 유사한다.


dynamic 변수는 DEFVAR, DEFPARAMETER 구문으로 생성한다.

(defvar *x* 10)
(defparameter *y* 20)


dynamic 변수를 special 변수라고도 불린다.
dynamic 변수는 자동적으로 전역적인 special 변수가 되기 때문이다.
special 변수와 같은 이름의 변수를 lexical 환경에서 생성하면 lexical이 아닌 dynamic 연결을 가진다.


(defparameter *y* 10)
(defun plus-*y* (x) (+ *y* x))

의 환경에서 *Y*는 special 변수이다.


special 변수가 환경에 따라서 어떻게 다르게 동작하는지 PLUS-*Y*로 실험해보자.

CL-USER> (plus-*y* 5)
15

예상한대로 '10 + 5'의 결과인 '15'를 반환했다.

다른 환경에서, PLUS-*Y*로 실험해보자.

CL-USER> (let ((*y* 20)) (plus-*y* 5))
25

예상과 다르게 '25'를 돌려주었다.


PLUS-*Y*의 몸체

(+ *y* x)
에서 정의된 *Y*는
(defparameter *y* 10)

를 참고할 것 같다.
그런데, 앞서 설명한대로, *Y*는 special 변수이고 PLUS-*Y*를 호출한 환경

(let ((*y* 20)) (plus-*y* 5))

에 동일 이름의 *Y*가 존재한다.
결국, LET 구문의 *Y*의 변수가 dynamic 연결을 가짐으로 값 '20'이 PLUS-*Y* 에서 사용되었다.[각주:1]


2013/01/17 - [Practice/Lisp] - DEFPARAMETER와 DEFVAR의 차이

2016/02/18 - [Practice/Lisp] - Lisp에서 클로저(closures)



  1. dynamic과 closure를 혼동하는 경우가 많다.<br /> closure에 대해서는 <a href="http://unipro.tistory.com/191" target="_blank">Lisp에서 클로저(closures)</a>를 참고하라. [본문으로]
저작자 표시 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Theory/Problem Solving

애덤 그랜트의 창의성에 관한 인터뷰 중 관심있는 내용 발췌

집단은 아이디어를 생산하지는 못하지만 가장 좋은 아이디어는 정확하게 뽑아낼 수 있습니다.


하나의 큰 아이디어보다는 적어도 8~9개의 다른 아이디어를 제시할 수 있어야 합니다.


창의성은 위험감수(risk taking)와 다릅니다. 위험감수는 실패할 확률이 높은 것에 도전하는 행위입니다. 생각이 다른 것이 위험을 감수하는 것은 아닙니다.


자녀의 창의성을 길러주는 방법 중에 하나는 "규칙을 최소화"하라 입니다. 창의력은 배양하기 어렵지만 꺾기는 쉽습니다


출처: 머리를 맞대고 아이디어 찾는다고? 창의성 죽이는 지름길


애덤 그랜트 미 펜실베이니아대 와튼스쿨 최연소 종신교수


2014/03/31 - [Theory/Problem Solving] - 창의적인 일을 찾지 말고 창의적으로 일을 하라.


저작자 표시 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
 [ 1 ]  [ 2 ]  [ 3 ]  [ 4 ]  [ 5 ]  [ 6 ]  [ ··· ]  [ 35 ] 

알림

이 블로그는 구글에서 제공한 크롬에 최적화 되어있고, 네이버에서 제공한 나눔글꼴이 적용되어 있습니다.

카운터

Today : 99
Yesterday : 112
Total : 158,259

티스토리 툴바