함수

  • 함수란?
  • 함수명 정하기
  • [보너스 섹션] 함수를 인자로 갖는 함수
    • mapreduce
  • [보너스 섹션] 무명 함수 Anonymous function
  • [보너스 섹션] let으로 로컬 바인딩하기

함수란?

count, conj, first, rest 같은 몇 가지 함수들을 살펴봤습니다. 마찬가지로, 우리가 사용한 모든 산술 연산도 함수입니다 : +, -, *, / 그렇다면 함수란 무엇일까요?

함수란 독립적이고 개별적인 코드로, (인자라고 하는) 값을 받고 값을 반환합니다.

참고: Basics of Function

  • count, conj, first
  • +, -, *, /
  • 값을 받고 값을 반환하는 코드

함수 예제

  • defn은 함수를 정의합니다.
  • forward-right은 이 함수의 이름입니다.
  • 다음 줄의 문자열은 함수에 대한 설명입니다. 이는 선택 사항입니다.
  • [turtle]인자의 리스트입니다. turtle이라는 한 개의 인자를 갖고 있습니다.
  • (forward turtle 60) (right turtle 135) 은 함수의 본체입니다. 함수를 사용할 때 실행되는 부분입니다.
(defn forward-right
  "Moves specified turtle forward and tilts its head"
  [turtle]
  (forward turtle 60)
  (right turtle 135))

forward-right 함수 사용하는 법

forward-right를 사용하기 위해선, 함수를 호출해야 합니다. 앞서 사용해본 함수들을 다뤘던 것처럼 말이죠.

(forward-right :trinity)  ;=> {:trinity {:angle 135}}
(forward-right :neo) ;=> {:neo {:angle 135}}

인자를 여러 개 갖는 함수

함수는 한 개 이상의 인자를 가질 수 있습니다. turtle과 길이를 인자로 갖는 forward-right-with-len함수를 만들어 보겠습니다.

(defn forward-right-with-len
  "Given turtle and length, forward the turtle and tilts its head"
  [turtle len]
  (forward turtle len)
  (right turtle 135))

(forward-right-with-len :trinity 90) ;=> {:trinity {:angle 135}}
(forward-right-with-len :neo 80)  ;=> {:neo {:angle 135}}

연습문제 1: 함수를 이용해 거북이 움직이기

  1. 함수 작성하기
    • walk.clj로 이동합니다.
    • 편집기에서, 슬라이드에 있는 forward-right 함수를 작성합니다. (아래쪽)
    • (선택 사항) walk.clj를 저장합니다.
    • forward-right 함수 전체를 선택하고 Eval Selection을 누릅니다.
  2. 함수 사용하기
    • 우측의 REPL 창에 (forward-right :trinity)를 입력합니다.
    • 위의 입력을 최소 8번 반복합니다.(위 화살표와 엔터를 사용하세요.)
(defn forward-right
  "Moves specified turtle forward and tilts its head"
  [turtle]
  (forward turtle 60)
  (right turtle 135))

연습문제 2: 인자를 가진 함수를 이용해 거북이 움직이기

  • walk.clj로 이동합니다.
  • 편집기에서, 3개의 인자를 갖는(turtle, len, and angle) forward-right-with-len-ang 함수를 작성합니다.(forward-right-with-len의 확장판)
  • forward-right-with-len-ang 함수 전체를 선택하고 Reload Selection을 누릅니다.
  • REPL 창에서, (forward-right-with-len-ang :trinity 60 120)를 씁니다.
  • REPL에서 함수를 평가하면서 여러 번 반복합니다.

함수명 정하기

이름은 심볼이다

함수명은 심볼입니다. 값에 이름을 할당할 때 def와 함께 사용했던 심볼들처럼 말이죠.

숫자가 아닌 문자로 시작해야 합니다. *, +, !, -, _, ?와 함께 영숫자 문자를 포함할 수 있습니다. 이러한 유연성은, 우리가 함수명에 사용하는 관례와 관련되기 떄문에 함수에서 중요합니다.

함수의 두 가지 유형

클로저는 두 가지 유형의 함수가 있습니다:

  1. 값을 반환하는 함수,
  2. 참/거짓을 반환하는 함수. 두 번째 유형의 함수를 진위 함수(predicate)라고 합니다.
진위 함수 예제

클로저에서, =는 놀랍게도 진위 함수입니다. 그 외에도, 다른 많은 컴퓨터 언어들처럼 클로저는 크기 비교를 위한 진위 함수를 갖고 있습니다. 대부분의 진위 함수는 ?로 끝납니다.

  • =, not=
  • >, <, >=, <=
  • true?, false?, empty?, nil?, vector?, map?

[보너스 섹션]

함수를 인자로 갖는 함수

컬렉션과 함께 사용할 수 있는 가장 강력한 함수들 중 몇몇은 함수의 인자로 다른 함수가 올 수 있습니다. 클로저의 가장 마법같은 부분입니다. –다른 많은 프로그래밍 언어들도 그러하지만, 익숙하지 않은 개념이라 처음에는 이해가 안 갈 수 있습니다. 예제를 보고 더 공부해 봅시다.

참고: Higher-order Function

map 함수

map은 인자로, 다른 함수와 컬렉션을 받습니다. 컬렉션의 각 요소들을 대상으로 인자로 주어진 함수를 호출합니다. 그런 다음 해당 함수 호출의 결과를 담은 새로운 컬렉션을 반환합니다. 생소한 개념이지만, 일반적인 함수형 프로그래밍과 클로저의 핵심입니다.

(map inc [1 2 3]) ;=> (2 3 4)
(map (partial + 90) [0 30 60 90]) ;=> (90 120 150 180)

참고: partial

reduce 함수

함수를 인자로 받는 또 다른 함수를 살펴봅시다. reduce함수가 있습니다. 컬렉션을 단일 값으로 변환할 때 사용합니다.

reduce함수는, ‘두번째 인자로 제공된 컬렉션’에서 처음 두 요소를 취한 후, ‘첫번째 인자로 제공된 함수’에 그 요소들을 인자로 주어 호출합니다. 그 후에도 ‘인자로 주어진 함수’를 계속해서 호출 하는데, 이제부터는 이 함수의 인자로 ‘이전 함수 호출의 결과’와 ‘컬렉션의 다음 요소’를 취합니다. reduce함수는 이 과정을 컬렉션의 끝에 도달할 때 까지 반복하고 반복합니다.

(reduce str (turtle-names)) ;=> ":trinity:neo:oracle:cypher"
(reduce + [30 60 90])       ;=> 180

연습문제 3 [보너스]: 평균값 찾기

  • 맵의 벡터를 받는 average라는 이름의 함수를 만듭니다.
  • [{:angle 30} {:angle 90} {:angle 50}]를 입력으로 사용합니다.
  • :angle의 평균값을 계산합니다.

  • 힌트: map, reduce, count함수를 이용하세요.

[보너스 섹션]

무명 함수 Anonymous functions

이름이 없는 함수

여태껏 봐왔던 모든 함수들은 이름을 갖고 있었습니다. +, str, reduce처럼 말이죠. 하지만, 함수는 이름이 꼭 필요하지는 않습니다. 값이 이름을 가질 필요가 없듯이요. 이렇게 이름이 없는 함수를 무명 함수라고 합니다. 무명 함수는 fn으로 생성됩니다. 이렇게 말이죠:

참고: Anonymous Function

(fn [s1 s2] (str s1 " " s2))

무명 함수가 아닌 경우

진도 나가기 전에, 함수명은 항상 자유롭게 지을 수 있다는 걸 이해해야 합니다. 그렇게 하는 것이 잘못된 것은 아닙니다. 하지만, 무명 함수로 이루어진 클로저 코드를 앞으로 볼 것입니다. 때문에 무명 함수를 알고 있어야 합니다.

(defn join-with-space
  [s1 s2]
  (str s1 " " s2))

무명 함수 사용 예제

왜 무명 함수를 쓸까요? 무명 함수는 꽤나 유용합니다. 바로 함수 섹션에서 배운 map이나 reduce같은 함수들처럼, 다른 함수를 인자로 갖는 함수를 가질 때 말입니다. 무명 함수의 사용 예제를 살펴봅시다.

(map (fn [t] (forward t 45)) (turtle-names))
;=> ({:trinity {:length 45}} {:neo {:length 45}} {:oracle {:length
45}} {:cypher {:length 45}})

(reduce (fn [x y] (+ x y)) [1 2 3]) ;=> 6

(reduce (fn [a b] (str a ", " b)) (map name (turtle-names)))
;=> "trinity, neo, oracle, cypher"

[보너스 섹션]

let으로 로컬 바인딩하기

함수를 만들 때, 코드를 읽기 쉽게 하거나 값을 다시 사용하기 위해 값에 이름을 할당하고 싶을 수 있습니다. 하지만 함수 안에서는 함수 밖에서처럼 def을 사용해서는 안됩니다. 대신에 let이라고 불리는 특별한 양식을 사용해야합니다.

let으로 로컬 바인딩하기

letdef처럼 사용해서 값에 이름을 할당할 수 있습니다. 값에 이름을 할당하면, 그 이름을 심볼이라고 부릅니다.

Reference: Assignment let

(let [mangoes 3
      oranges 5]
  (+ mangoes oranges))
;=> 8

let 예제

여태껏 봐왔던 것들 중에서 가장 복잡합니다. 각 단계를 진행해봅시다. 먼저, 함수의 이름과 함수를 설명하는 문자열과 인자를 갖고 있습니다. 다른 함수들과 같죠.

다음엔, let이 보입니다. let은 이름과 값이 번갈아 나오는 벡터를 첫 인자로 받습니다. t1이 이름이고,(first names)의 결과를 t1에 할당합니다. 또 (last names)의 결과를 t2에 할당합니다.

이름과 값의 벡터 다음에, let 함수의 본체가 나옵니다. 여타 함수의 본체처럼 실행하고 값을 반환합니다. let안에 t1t2가 정의되어있습니다.

walk.clj로 이동해서 opposite 함수를 작성합니다. 그런 다음, 함수 정의의 마지막 줄에서 opposite 함수를 평가해봅 니다. 또한, opposite 함수의 사용 예제를 평가해 봅니다.

;; function definition
(defn opposite
  "Given a collection of turtle names, moves two of them in different directions."
  [names]
  (let [t1 (first names)
        t2 (last names)]
    (forward t1 40)
    (backward t2 30)))

;; function usage
(opposite (turtle-names))

첫번째 슬라이드로 돌아가거나, curriculum outline으로 가세요.