Getting Started with CLISP (17) - 텍스트 출력 및 읽기 Printing and Reading Text
이번 포스팅에서는 사용자 인터페이스의 가장 기본인 command-line 인터페이스에 대해서 알아본다.
Printing and Reading Text
스크린에 프린트하기
1 2 3 4 5 6 7 | Break 83 [84]> (print "foo") "foo" "foo" Break 83 [84]> | cs |
"foo"가 2번 출력이 된다. 첫번째 "foo"는 실제 print 함수가 출력하는 부분이며, 두번째 "foo" 는 REPL이 입력된 표현식의 값을 그대로 출력해주기 때문이다.
숙련된 리스퍼들은 prin1 함수도 애용한다. print 와 prin1 의 차이점을 이해하기 위해 다음의 예를 살펴보자.
1 2 3 4 5 6 7 8 9 10 | Break 84 [85]> (progn (print "this") (print "is") (print "a") (print "test")) "this" "is" "a" "test" "test" | cs |
print 함수는 각각의 아이템이 별도의 라인으로 출력된다. 그러면 prin1 을 보자.
1 2 3 4 5 6 7 8 | Break 86 [87]> (progn (prin1 "this") (prin1 "is") (prin1 "a") (prin1 "test")) "this""is""a""test" "test" Break 86 [87]> | cs |
보는 바와 같이 prin1 하나의 라인에 출력이 된다. 좀 더 정확히 얘기하면, print 와 prin1 명령은 모든 면에서 같지만, print 함수는 값을 출력하기 전에 새로운 라인을 생성한다는 점만 다르다. 그리고 print 함수는 출력된 값 마지막에 스페이스를 둔다. prin1이 좀 더 심플하고 유연한 함수인 관계로 리스퍼들은 주로 prin1 을 많이 사용한다.
Saying Hello to the User
사용자의 이름을 물어보고 인사를 하는 함수를 만들어보자. 구식처럼 보여도 이름을 따옴표로 감싸는 것을 잊지말자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Break 87 [88]> (defun say-hello () (print "Please type your name.") (let ((name (read))) (print "Nice to meet you, ") (print name))) SAY-HELLO Break 87 [88]> (say-hello) "Please type your name." "alex" "Nice to meet you, " "alex" "alex" Break 87 [88]> | cs |
먼저, 사용자에게 이름을 물어보는 메시지를 출력하고, read 함수에 의해 읽혀진 값이 셋팅되는 name 이라는 지역 변수를 정의했다. read 함수는 REPL 에서 사용자가 무언가를 타이핑하는 것을 기다린 후 사용자의 타이핑이 끝나면 변수 이름이 그 값으로 셋팅된다.
위의 예에서 보듯이 print 와 read 함수는 여러분이 예상한대로 동작한다. 하지만 거기에는 확연히 눈에 띄는 이상한 점이 있다. 모든 표시되고 입력된 값이 따옴표에 의해 감싸진다는 것이다.
Starting with print and read
print 와 read 함수는 값을 컴퓨터의 입장에서 생각하는 것이지 인간의 입장에서 이해하는 것은 아니다. 컴퓨터는 문자열이 따옴표에 둘러 싸이는 것을 좋아한다. 텍스트가 따옴표로 둘러싸이면 아무리 구형 컴퓨터라도 그것이 문자열임을 알아챈다. print 와 read 함수는 이런 철학이 극단까지 담겨 있는 것이다.
다음의 코드는 앞선 코드와 유사하지만, 문자열 대신 숫자가 입력되는 부분이 다르다. 따옴표 없이 숫자를 어떻게 처리하는지 살펴보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Break 88 [89]> (defun add-five () (print "please enter a number:") (let ((num (read))) (print "When I add five I get") (print (+ num 5)))) ADD-FIVE Break 88 [89]> (add-five) "please enter a number:" 6 "When I add five I get" 11 11 Break 88 [89]> | cs |
몇 가지 예를 더 살펴보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Break 89 [90]> (print '3) 3 3 Break 89 [90]> (print '3.4) 3.4 3.4 Break 89 [90]> (print "foo") "foo" "foo" Break 89 [90]> (print '#\a) #\a #\a Break 89 [90]> | cs |
심볼의 경우 모두 대문자로 표시된다.
1 2 3 | Break 90 [91]> (print 'foo) FOO FOO | cs |
앞선 예에서는 명시적으로 각각의 값 앞에 따옴표 등을 넣었다. 만약 생략되더라도 심볼 이름을 제외하고는 무방하다. 마지막 예는 캐릭터가 리스프에서는 어떻게 입력되는지를 보여주고 있다. 리스프 캐릭터를 생성하기 위해서는 실제 캐릭터 앞에 #\ 를 집어넣으면 된다.
Reading and Printing Stuff the Way Humans Like It
princ 함수는 인간이 선호하는 방식으로 출력을 해낸다. 다음의 예를 보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | Break 92 [93]> (princ '3) 3 3 Break 92 [93]> (princ '3.4) 3.4 3.4 Break 92 [93]> (princ "foo") foo "foo" Break 92 [93]> (princ 'foo) FOO FOO Break 92 [93]> (princ '#\a) a #\a Break 92 [93]> | cs |
1 2 3 4 5 6 7 8 9 | Break 94 [95]> (progn (princ "This sentence will be interrupted") (princ #\newline) (princ "by an annoying newline character.")) This sentence will be interrupted by an annoying newline character. "by an annoying newline character." Break 94 [95]> | cs |
print 명령의 가장 큰 매력은 있는그대로 출력이 된다는 점이다. 하지만 이 얘기는 자의적인 텍스트 출력은 불가능하다는 이야기이다. 반면 princ 는 원하는대로 출력이 가능하다. 이상의 지식을 바탕으로, 처음의 say-hello 함수를 부호없이 코딩해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 | Break 95 [96]> (defun say-hello () (princ "Please type your name:") (let ((name (read-line))) (princ "Nice to meet you, ") (princ name))) SAY-HELLO Break 95 [96]> (say-hello) Please type your name:Bob O'Malley Nice to meet you, Bob O'Malley "Bob O'Malley" Break 95 [96]> | cs |
처음의 버전과 상당부분 유사하지만, 이름을 물어봤을 때 따옴표 없이 입력이 가능하다는 차이점이 있다. read-line 명령이 엔터가 입력될 때까지 들어간 모든 텍스트를 반환해주기 때문에 이름 뿐만 아니라 공백, 따옴표 등도 입력이 가능하다는 차이점도 있다.