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.
(defun compose (&rest fns) ;; &rest는 복수의 함수를 매개변수를 받겠다.
    (if fns ;; 함수가 존재한다면
        (let ((fn1 (car (last fns))) ;; 리스트의 마지막 함수 가져오기(compose는 오른쪽부터 합쳐진다)
                   (fns (butlast fns))) ;; 마지막 함수를 제외한 함수 리스트
              #'(lambda (&rest args) ;; fn1 함수가 어떤 매개변수를 받을지 모르니 args로 다 받음
                        (reduce #'funcall fns ;; 하나하나 함수를 누산한다.
                                :from-end t ;; 아 뒤에서 부터 실행하라는 뜻
                                :initial-value (apply fn1 args)))) ;; 첫번째 함수를 초기값으로 실행 
             #'identity)) ;; 함수가 없으면 identity 리턴
              
;; 사용법
(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)
;; 코드리뷰
(defun fif (if then &optional else)
    #'(lambda (x) ; if, then, else를 담은(closure) 람다함수 리턴
              (if (funcall if x) ; if 함수가 참이면
                  (funcall then x) ; then 함수 실행
                  (if else (funcall else x))))) ; else 함수 실행
Figure 5.4는 일반적으로 발생하는 유형의 함수에 대한 몇 가지 다른 생성자를 포함합니다.
두 번째, fint는 다음과 같은 경우입니다.
트루이면 그 상태에서 멈추는 거니까 거기까지만 일을 했다는 것! 특이한 건 그때그때 함수를 실행할 것이겠지?
and이니까 그럴 것이다.(and는 매크로로 보임)
(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를 사용한다.


댓글 없음 :

댓글 쓰기