2019년 1월 11일 금요일

[on lisp] 5.4 Composing Functions 함수 조합(되게 유익)

5.4 Composing Functions
함수 f의 보수는 ∼f로 표기된다.

5.1절에서 closure가 ∼을 Lisp 함수로 정의할 수 있게 함을 보였다.
함수에 대한 또 다른 공통 연산은 연산자 ◦로 표시되는 '합성'이다.
f와 g가 함수라면, f ◦g는 함수이고 f ◦g (x) = f (g (x))이다.
클로저는 또한 ◦을 Lisp 함수로 정의하는 것을 가능하게합니다
아래 Figure5.3은 여러 함수를 취해 그 혼합물을 리턴하는 compose함수를 정의한다.
;; Figure 5.3: An operator for functional composition.

;; 1. &rest는 복수의 함수를 매개변수를 받겠다.
;; 2. 함수가 존재한다면
;; 2.1 리스트의 마지막 함수 가져오기 (compose는 오른쪽부터 합쳐진다)
;; 2.2 fn1 함수가 어떤 매개변수를 받을지 모르지 args로 다 받음
;; 2.3 마지막 함수를 제외한 함수 리스트를 구함(마지막 리스트는 초기값에 들어감)
;; 2.4 reduces로 하나하나 함수를 누산한다.
;; 2.5 :from-end t 뒤에서 부터 실행하라는 뜻
;; 2.6 :initial-value 첫번째 함수를 초기값으로 세팅
;; 3. 함수가 없으면 identity 리턴
(defun compose (&rest fns) ;; 1
    (if fns ;; 2
        (let ((fn1 (car (last fns))) ;; 2.1
                   (fns (butlast fns))) ;; 2.2
              #'(lambda (&rest args) ;; 2.3
                        (reduce #'funcall fns ;; 2.4
                                :from-end t ;; 2.5
                                :initial-value (apply fn1 args)))) ;; 2.6
             #'identity)) ;; 3
              
;; 사용법
(compose #'list #'1+) ;; 아래 값과 같다.
#'(lambda (x) (list 1+ x))
compose에 대한 인수로 주어진 모든 함수는 마지막 인수를 제외하고, 모두 하나의 인수를 받는 녀석들이어야 한다.
마지막 함수는 아무런 제약이 없다. 무엇이든지 인수가 주어지면 compose에 의해 함수가 초깃값으로 반환될 것이다.
> (funcall (compose #’1+ #’find-if) #’oddp ’(2 3 4))
4
위에 내용은 함수들을 closure로 감싼 함수를 리턴한 것과 같다.
;; Figure 5.4: More function builders.
(defun fif (if then &optional else)
    #'(lambda (x)
              (if (funcall if x)
                  (funcall then x)
                  (if else (funcall else x)))))
(defun fint (fn &rest fns)
    (if (null fns)
        fn
        (let ((chain (apply #'fint fns)))
             #'(lambda (x)
                       (and (funcall fn x) (funcall chain x))))))

(defun fun (fn &rest fns)
    (if (null fns)
        fn
        (let ((chain (apply #'fun fns)))
             #'(lambda (x)
                       (or (funcall fn x) (funcall chain x))))))

not은 리스프 함수이기 때문에, complement는 compose의 특별한 경우이다.
이녀석은 이렇게 정의될 수 있다.
(defun complement (pred)
  (compose #'not pred))
함수들을 조합(composing)하는 것 이외의 다른 방법으로 기능을 결합(combine)할 수 있다.
(mapcar #'(lambda (x)
                  (if (slave x) ; 노예면
                      (owner x) ; 오너를
                      (employer)) ; 아니면 고용주를
                      people) ; 사람 , 노예 아니면 직장인
위와 같은 함수를 자동으로 생성하는 연산자를 정의할 수 있다.
Figure 5.4의 fif를 사용하면 다음과 같은 효과를 얻을 수 있다.

(mapcar (fif #'slave #'owner #'employer)
        people)
아주 간단해졌다.

fif코드에 대해 알아보자.
1. if, then, else를 담은(closure) 람다함수 리턴
1.1 람다는 true/false 값을 받는다.
2. then 걸린 함수 실행
3. else
(defun fif (if then &optional else)
    #'(lambda (x) ; 
              (if (funcall if x) ; 1
                  (funcall then x) ; 2
                  (if else (funcall else x))))) ; 3

Figure 5.4는 일반적으로 발생하는 유형의 함수에 대한 몇 가지 다른 생성자를 포함한다.
두 번째, fint는 다음과 같은 경우입니다.
트루이면 그 상태에서 멈추는 거니까 거기까지만 일을 했다는 것! 특이한 건 그때그때 함수를 실행할 것이겠지?
and이니까 그럴 것이다.(and는 매크로로 보임)
;; 아래 소스는 signed,sealed,delivered가 모두 참인경우
(find-if #'(lambda (x)
                   (and (signed x) (sealed x) (delivered x)))
         docs)
find-if의 인수로 주어진 predicate(술어)는 그 안에서 호출되는 세 개의 predicates의 교차점(intersection)이다.
그러니까 모두 참인경우를 말한다. 교집합
"function intersection"을 뜻하는 fint를 사용해보자.
(find-if (fint #'signed #'sealed #'delivered) docs)
이렇게 유사한 연산자를 정의하여 predicate집합의 합집합을 반환 할 수 있따.
fun 함수는 fint와 비슷하지만 and 대신에 or를 사용한다.
fun = find-union (이렇게 보면 될듯)

댓글 없음:

댓글 쓰기