Post

racket 언어 문법 조금만 살펴보기

lisp 언어를 이 책 통해서 처음 접해서 전문성이 없는 글입니다. 참고바랍니다.

소개

  • lisp/scheme 계열인 racket 언어로 프로그래밍 구조 예시를 보여준다 .

실행방법

A. Racket + VSC 사용

B. Replit

Replit에서 Racket 언어를 제공해준다.

이 책을 어떻게 봐야할까?

인터넷에 이 책에 대한 평가가 좋은데 그에 비해 내용이 잘 읽히지 않아 당혹스러웠던 적이 있다. lisp은 괄호가 워낙 많아 타언어에 비해 가독성이 안 좋고 요즘 언어들과 조금 다른 스타일의 문법을 가지고 있기 때문이다. 그렇기 때문에 나같이 코딩 경력이 짧은 사람들은 다른 언어로 번역해가면서 그 의미를 이해해보는 것도 나쁘지 않다.

여기서는 주로 python과 JS/TS와 비교해서 이 책이 말하고자하는 바를 파악하려고 한다.

기본 문법

조합

프로시저 적용(procedure application)이라고도 부를 수 있는 조합 combination은 괄호로 감싼다.

1
(연산자 피연산자1 ... 피연산자n)
  • 흔히들 stack식 계산 방식인전위 표기법 으로 연산처리한다.
  • 어셈블리 언어에서도 가끔 보이는 표현방식이다.
    • add $s1, $s2, $s3 수식은 $s1 = $s2 + $s3 표현과 같다.

람다표현식

수를 지칭하기 위해 수치 리터럴1 을 사용하는 것처럼, 프로시저를 지칭할 때는 lambda로 시작하는 람다표현식을 자주 사용한다.

1
(lambda (x) (* x x))

위 표현식은 js, python으로 나타내면 다음과 같다.

1
2
my_func = lambda x : x * x
my_func(4) # 16

참고로 파이썬의 경우 lambda 함수는 한 줄만 작성할 수 있어 위 racket의 lambda나 js의 arrow function이랑 다르다.

1
2
const func = x => x*x;
func(4) // 16

정의

define 키워드로 특정 값, 프로시저에 이름을 부여하는 방식이다.

1
2
(define pi 3.141592653589793)
(define square (lambda (x) (* x x)))

이건 C언어의 매크로랑 유사하다.

1
2
#define pi 3.14159265
#define square(x) ((x)*(x)) // 괄호 처리 잘 안 하면 연산 순서가 의도대로 동작 안 할 수 있다.

racket처럼 프로시저 겹치는 방식도 가능은 하나, 다른 언어들에 불편하니 참고만 하자.

조건문

  • racket에서는 조건문을 나타내는 방식이 총 2가지다.
  • cond를 사용하면 switch~case 문 같은 느낌으로 사용할 수 있다.
    • else if구문이 없으니 세 가지 이상의 조건문을 달 때는 cond를 사용하자.
1
2
3
4
5
6
(define (abs x)
	(cond ((< x 0) (- x)) ; x<0 이면 -x 반환
			((= x 0) x) ; x = 0 이면 x 반환
			((> x 0) x) ; x>0이면 x 반환
	)
)
  • cond의 마지막 절에는 술어로 사용할 수 있는 else 키워드를 사용할 수 있다.
    1
    2
    3
    4
    5
    6
    
    (define (abs x)
      (cond ((< x 0) (- x))
              ((= x 0) x)
              (else x) ; else 구문 사용!
      )
    )
    

  • if문은 이분법적인 상황에 쓰인다.
    • 위 예제랑 똑같이 음수일 때만 절댓값 반환하는 함수를 만들어보자.
1
2
3
4
5
6
(define (abs x)
	(if (< x 0)
		(- x) ; x < 0이 참이라면 -x 반환
		x ; x <0이 거짓이라면 x 반환
	)
)

list, vector

  • list는 흔히 대부분 언어에서 사용하는 array와 유사한 데이터 형식이다.
  • racketlist의 특이한 점은 linkedlist 자료구조랑 비슷하다.
    • 목록은 요소 두 개짜리 쌍으로 구현하는
1
2
3
4
5
6
7
(define a-list (list 6 946 8 356 12 620))
a-list
(6 946 8 356 12 620)
(list? a-list)
#t true
(list? 3)
#f false
1
2
3
4
5
6
(cons a b) ; 인수 두 개만 사용되며 (a b) 리스트를 생성한다.
(define a-pair (cons 1 2)) ; a-pair = (1 2)
(car a-pair) ; car는 가장 앞의 요소를 반환한다.
1
(cdr a-pair) ; cdr는 car 요소를 제외한 나머지 요소들을 반환한다.
2

책의 예제에는 안 나와서 cons a-list 1와 같이 list와 숫자 리터럴 순서로 넣으면 어떻게 되는지 궁금해서 직접 실행해봤다. 그랬더니 car로 호출되는 요소는 a-list 통째로 나왔다.

  • vector는 우리에게 좀 더 친숙한 배열의 느낌의 데이터 형식이다.
    • vector-ref 키워드는 배열의 인덱스 접근하는 방식으로 쓰면 된다.
1
2
3
4
5
(define a-vector (vector 1 2 3 4))
a-vector
#(1 2 3 4) ; 출력결과는 #붙는다.
(vector-ref a-vector 2) ;a-vector[2]
3

이 다음 글이 날아가서 다음시간에 본격적으로 책 초반을 어떻게 이해해 나가야할지 알아보자..

Reference

  1. 상수형 변수보면 메모리 주소와 데이터 값이 있는데 리터럴은 주소가 아닌 데이터 값 그 자체를 말한다. ↩︎

This post is licensed under CC BY 4.0 by the author.