Computer Science/프로그래밍 언어론

Scheme 기본 문법

Beomsu Koh 2023. 4. 4.

Scheme 기본 문법

Scheme 언어의 특징은 아래와 같다

  • Simple syntax - 간단한 문법
  • Functional programming - 함수가 일급 객체 취급 받는다
  • Dynamic typing - 동적 타입 지원
  • Tail recursion optimization - 꼬리 재귀 최적화

Basic Data Type

s-expression은 symbolic expression의 약어로, Lisp 언어에서 모든 코드나 데이터를 나타내는 방식을 말합니다.
이는 리스트 구조를 이용한 트리 구조로 표현됩니다.
따라서 Lisp에서는 코드도 데이터와 마찬가지로 리스트 형태로 표현되어 처리됩니다.

S-expression의 데이터 유형

  • Atom은 숫자, 심볼 등과 같은 단순한 데이터 유형을 나타냅니다.
  • List는 여러 Atom이나 다른 List를 요소로 포함하는 복합 데이터 유형입니다.
    • List는 괄호로 둘러싸여 있으며, 각 요소는 공백으로 구분됩니다.
  • Others는 구조체, 문자열, 벡터 등 다른 데이터 유형을 포함합니다.

예시

  • 포인터로 연결 되어 있다고 생각하면 이해하기 수월합니다

Operations

Scheme 언어에서 다루는 operation들의 이름, 기능, 예시는 아래와 같습니다.

Car : Return First Element : 첫 번째 요소 반환

  • (car '((a) b c))
    • 결과 : ’ (a)
  • (car ’ (( )))
    • 결과 : ’ ( )
  • (car ’ ( ))
    • 결과 : error, Null, false or empty List

Cdr : Return Except First element(list type) : 첫 번째 요소 제외하고, 나머지 반환

  • (cdr '(a (b c)))
    • 결과 : '((b c))
  • (cdr '((a b ) c ))
    • 결과 : '©
  • (cdr ’ (()))
    • 결과 : ’ ()
  • (cdr ’ ())
    • 결과 : error, Null, False or Empty List

Cadr, Cdar

  • cadr = car + cdr (오른쪽에서 왼쪽으로 계산)
  • = (cadr ’ (a b c))
  • = (car (cdr ’ (a b c )'))
  • = (car ’ (b c))
  • b

Cons : 주어진 원소를 리스트의 앞부분에 추가하는 함수

  • cons와 append의 차이는 뭘까
    • cons는 원소를 리스트의 앞에 추가하는 반면, append는 2개를 연결하여 새로운 리스트를 만듭니다
  • 예시: (cons 'a '(b c)) => (a b c)
  • 리스트도 하나의 요소이기 때문에, 리스트와 리스트를 cons 하면 list의 list가 나옵니다

Append : 두 개 이상의 리스트를 이어 붙임

  • 예시: (append '(a) '(b c)) => (a b c)

Define : 변수나 함수 정의

define을 사용하여 변수를 정의할 때는 변수 이름과 초기값을 지정합니다.

함수를 정의할 때는 함수 이름, 매개 변수 및 함수 몸체를 지정합니다.
define을 사용하여 정의된 변수나 함수는 다른 식에서 참조할 수 있습니다.

이를테면, (define x 5)x라는 변수를 정의하고 초기값으로 5를 할당합니다.
이후에는 x를 참조하여 5를 사용할 수 있습니다.

함수를 정의할 때는 다음과 같이 사용할 수 있습니다.
(define (add x y) (+ x y))add라는 함수를 정의하고, 매개 변수로 xy를 받으며, xy를 더한 값을 반환합니다.
이후에는 (add 3 4)와 같이 add 함수를 호출하여 3과 4를 더한 값을 반환받을 수 있습니다.

Set! : 변수 값을 변경하기 위해 사용되는 형식

수를 새로운 값으로 할당하는 것으로 변수가 가리키는 값이 변경됩니다. 이것은 기존의 변수를 수정할 때 사용됩니다. 예를 들어, 다음과 같이 사용할 수 있습니다.

  • 사전에 값을 정의하지 않으면 에러가 발생한다
(define x 10)       ; x에 10 할당 
(set! x 20)         ; x의 값을 20으로 변경 
(display x)         ; 20이 출력됩니다.

set!은 일반적으로 전역 변수의 값을 변경하기 위해 사용되지만, 로컬 변수에도 적용될 수 있습니다.
그러나 로컬 변수의 경우 let 또는 let*를 사용하여 변수를 할당하고 값을 변경하는 것이 좋습니다.

Numeric Operaions

    • (+) => 0
    • (+ 1 2 3) => 6
    • (+ 1.5 2.5) => 4.0
    • (- 3 2) => 1
    • (- 5) => -5
    • (- 1.5 0.5) => 1.0
    • (*) => 1
    • (* 2 3) => 6
    • (* 2.0 3.0) => 6.0
    • (/ 10 5) => 2
    • (/ 1.0 2) => 0.5
    • (/ 10 0) => 에러 발생
    • (> 2 1) => #t
    • (> 1 2) => #f
    • (> 1 1) => #f
    • (< 2 1) => #f
    • (< 1 2) => #t
    • (< 1 1) => #f
    • (>= 2 1) => #t
    • (>= 1 2) => #f
    • (>= 1 1) => #t
    • (<= 2 1) => #f
    • (<= 1 2) => #t
    • (<= 1 1) => #t
    • (= 1 1) => #t
    • (= 1 2) => #f
    • (= 1 1 1) => #t
    • 문자들에게는 적용이 되지 않음 - Equal?을 써야 한다
    • (min 1 2) => 1
    • (min 2 1) => 1
    • (min 1 1) => 1
    • (max 1 2) => 2
    • (max 2 1) => 2
    • (max 1 1) => 1

Logical Operations

    • (and #t #t #t) => #t
    • (and #t #f #t) => #f
    • (and '() 1 2 3 4 5) => 5
    • (or #t #f #t) => #t
    • (or #f #f #f) => #f
    • (or ’ () 1 2 3 4 5) => '()
    • (not #t) => #f
    • (not #f) => #t

==Other Operations

  • cons, append, list의 차이는 무엇인가

  • List : 주어진 요소를 하나의 리스트로 만듭니다

    • (list 'a 'b 'c) => (a b c)
    • (list (list 'a 'b) (list 'c)) => ((a b) ©)
    • (list '()) => (())
  • ==Member : 리스트에서 요소를 찾습니다

    • (첫 번째 매치 되는 요소부터 쭈욱 반환합니다)
    • (member 'a '(1 a b)) => (a b)
    • (member (list 'a) '(a (a) c (a))) => ((a) c (a))
    • (member 'd '(a b c)) => #f
  • ==Assoc : 주어진 키가 리스트 내에 존재하는지 확인, 존재하면 키와 연관된 값 반환

    • (assoc 'b '((a 1) (b 2) (c 3))) => (b 2)
    • (assoc '(a) '(((a) 1 2 3) (b 4) (c 5))) => ((a) 1 2 3)
    • (assoc 'd '((a 1) (b 2) (c 3))) => #f
  • Length : 무조건 리스트로 주어져야 길이를 계산 함

    • (length '(a b c)) => 3
    • (length '()) => 0
    • (length '((a b) ())) => 2
  • Reverse

    • (reverse '(a b c)) => (c b a)
    • (reverse '((a b) c)) => (c (a b))
  • Equal

    • (equal? 'a 'a) => #t
    • (equal? 1 1) => #t
    • (equal? '(a (b)) '(a (b))) => #t
    • (equal? '(a (b)) '(a ©)) => #f
  • ==apply : 리스트 형태로 넘겨진 인자를 함수의 인자로 전달하는 함수

    • (apply + '(1 2 3)) => 6
    • (apply min '(5 2 8 1)) => 1
    • (apply append '((1 2) (3 4))) => (1 2 3 4)

Loop Operations- 여기

  • ==Map
    • 리스트에 대해 함수를 반복 적용하여 새로운 리스트 생성
    • (map car '((a 1) (b 1) (c 2))) => '(a b c)
  • ==for-each
    • (for-each (lambda (n) (set! x (+ x n))) '(1 2 3 4))
      • => side effect, return 값 없음
  • Let: 지역 변수를 정의하고, 로컬 변수를 사용하여 식을 계산하고 결과를 반환
    • (let ((x 1) (y 2)) (+ x y)) => 3
    • ==(let ((x 1) (y 2)) (set! x (+ x y)))
      • => side effect, return 값 없음
      • 전역 변수 값 설정 할 때 사용

==Control Statement

  • Scheme에서는 if, case, cond 등의 Control Statement를 지원합니다.
  1. if
    1. 조건식이 참이면 then 부분의 표현식을 실행하고, 그렇지 않으면 else 부분의 표현식을 실행합니다. else 부분은 선택적입니다.
    2. (if cond expr1 expr2) (if cond1 expr1 (if cond2 expr2 expr3))
  2. case
    1. key값과 index를 비교하여 일치하는 경우 해당 expr을 반환합니다. else 부분은 선택적입니다.
    2. (case key (index1 expr1) (index2 expr2) (else exprn))
  3. cond : 다중 if 문을 염두해둔 것이다
    1. cond부분의 조건이 참일 때, 해당 expr을 반환합니다. else 부분은 선택적입니다.
    2. (cond (cond1 expr1) (cond2 expr2) ... (cond_n expr_n) (else expr))
(if (= 3 (+ 1 2)) (+ 3 4) 2) => 7  
(case (* x y) (3 'three) (4 'four) (else 'none)) => 'four  
(cond ((> 3 2) 'greater) ((< 3 2) 'less) (else 'none-above)) => 'greater

ETC : Fuction, Let, Block Structing, Let Loop, Recursion

  • 함수(function)
    Scheme에서 함수를 정의할 때는 define을 사용하며, define을 사용하여 함수를 정의할 수 있습니다.
    함수는 입력 매개변수와 함께 작동하는 명령어의 집합으로 정의됩니다.
    예를 들어, (define (add2 x) (+ x 2))는 add2라는 함수를 정의합니다.
    이 함수는 x라는 매개변수를 입력으로 받고, x에 2를 더한 값을 반환합니다.
    이 함수를 (add2 4)와 같이 호출하면, 4를 매개변수로 사용하여 6을 반환합니다.

  • let
    지역 변수(local variable)를 정의하는 데 사용됩니다.

  • 예를 들어, (let ((x 1) (y 2) (z 5)) (+ x y z))는 x, y, z라는 세 개의 지역 변수를 정의하고, x와 y를 더한 다음 z를 더하여 8를 반환합니다.

let으로 지역 함수도 만들 수 있다!

> (define (cm item lst)
    (let loop((temp lst))
      (cond
        ((null? temp) '())
        ((equal? item (car temp)) temp)
        (else (loop (cdr temp)))
        )
      )
    )
> (cm 'b '(a b c))
(b c)
  • block structuring
    (define (fn arg) …)은 함수를 정의하며, (let ((a 1) (b 2)) …)은 지역 변수를 정의합니다.
    (begin …)은 여러 개의 Scheme 표현식을 그룹화하고 순차적으로 실행하며, (if …)는 조건문을 나타냅니다.

  • 재귀(recursion)
    재귀는 함수가 자기 자신을 호출하여 실행되는 것을 말합니다.
    예를 들어, (define (factorial n) (if (= n 0) 1 (* n (factorial (- n 1)))))는 팩토리얼을 계산하는 함수를 정의합니다.
    이 함수는 재귀를 사용하여 자기 자신을 호출하면서 입력된 n까지의 모든 정수를 곱하여 반환합니다.
    또한, 재귀를 보다 효율적으로 수행하기 위해 꼬리 재귀(tail recursion)를 사용할 수 있습니다.
    꼬리 재귀는 함수 호출이 반환 값 계산의 마지막 단계에 있을 때 재귀 호출을 제거하므로, 스택 오버플로우와 같은 문제를 방지할 수 있습니다.

레퍼런스

부족한 점이나 잘못 된 점을 알려주시면 시정하겠습니다 :>

'Computer Science > 프로그래밍 언어론' 카테고리의 다른 글

어휘 분석  (0) 2023.04.05
프로그래밍 언어의 구성  (0) 2023.04.05
람다 대수(Lamda calcuus)란 무엇인가  (0) 2023.03.29
함수형 프로그래밍  (0) 2023.03.29
프로그래밍 언어의 변천사  (0) 2023.03.27

댓글