2019년 8월 23일 금요일

[on lisp] 5.2 Orthogonality (직교성)

An orthogonal language is one in which you can express a lot by combining a small number of operators in a lot of different ways.
직교성이 있는 언어는 작은 여러 연산자들을 이용하여 많은 것들을 표현할 수 있다.(그리고 그 방법 또한 여러가지임)

Toy blocks are very orthogonal; a plastic model kit is hardly orthogonal at all.
레고블록은 직교성이 있지만, 특정 모델 키트는 직교적이지 않다.
The main advantage of complement is that it makes a language more orthogonal.
complement함수의 주된 장점은 언어를 더욱 직교적으로 만든다. (complement는 반대를 말함. (보수))

complement, 커먼리습은 함수의 쌍을 가진다. remove-if/remove-if-not, subst-if/subst-if-not 같은 것.
complement와 함께먼 이것들의 반은 필요 없게 된다.

The setf macro also improves Lisp's orthogonality.
setf 매크로 또한 Lisp의 직교성을 향상시켜준다.
Earlier dialects of Lisp would often have pairs of functions for reading and writing data.
리스프의 초기 방언들은 종종 데이터를 읽고 쓰는 기능을 가지고 있었다.
With property lists, for example, there would be one function to establish properties and another function to ask about them.
예를들어, 프로퍼티 리스트를 함수로 간주하여 거기에다가 값을 넣고 뺄 수 있다고 해보자.
프로퍼티 리스트에는 값을 넣고 빼는 기능이 각각 하나씩 있을 것이다.
In Common Lisp, we have only the latter, get.
커먼리스프에는 후자만 있다.(빼는 기능) get

To establish a property, we use 'get' in combination with setf:
프로퍼티를 설정하려면 setf와 함께 'get'을 사용한다.
(setf (get 'ball 'color) 'red)
(print (get 'ball 'color))
'red
이렇게 넣을 수 잇다.


We may not be able to make Common Lisp smaller, but we can do something almost as good: use a smaller subset of it.
우리는 커먼리습을 작게 만들 수는 없을 것이다, 하지만 거의 그렇게 할 수 있다:작은 집합을 사용하는 것.
Can we define any new operators which would like complement and setf, help us toward this goal?
setf나 complement같은 연산자를 더 만들면 우리의 목표에 더 가까워질까?

There is at least one other way in which functions are grouped in pairs.
기능이 쌍으로 그룹화되는 다른 방법이 최소한 하나 이상있다.
Many functions also come in a destructive version: remove-if/delete-if, reverse/nreverse
한 가지 기능에서 destructive version(리턴하지 않고 값을 바꿔버리는)을 직교 기준으로 보고 여러개의 함수를 제공할 수도 있다. remove-if/delete-if, reverse/nreverse 그리고 append/nconc
처럼 이번에는 그룹의 방식의 기능의 정반대강 아니라 destructive냐 아니냐로 나눈것.
By defining an operator to return the destructive counterpart of a function, we would not have to refer to the destructive functions directly.
이제 특정 함수에 대해서 destructive counterpart를 리턴하는 함수를 함수를 정의하여, 직접적으로 destructive함수를 부를일 없게 만들자.(기준을 만들고 반으로 잘라서 없애버리는 거다)

어떻게? Fig5.1을 보면서 이야기 하자.
;; FIg5.1 : Returning desturctive equivalents
(defvar *!equivs* (make-hash-table))

(defun ! (fn)
  (or (gethash fn *!equivs*) fn))

(defn def! (fn fn!)
  (setf (gethash fn *!equivs*) fn!))
그림5.1은 descturvie counterparts 개념을 지지하는 코드를 가지고 있다.
글로벌 해시테이블 *!equivs* 맵은 destructive equivalents를 위한 기능을 한다.
!함수는 destructive equivalent를 리턴한다.
def!는 *!equivs*에 값을 저장한다.

The name of the ! (bang) operator comes from the Scheme convention of appending ! to the names of functions with side-effects
이름에 !를 붙이는 연산자는 스킴에서 따온 건데 사이드이팩트가 있으면 붙이는 것이다.

자 어떻게 쓰는지 보자.
(def! #'remove-if #'delete-if)
delete-if를 없애버린 것이다. 이제 remove-if만으로 개발이 가능하게 되었다.
아래를 보자.
(delete-if #'oddp lst) ;; 기존에는 이렇게 썼다. 이제 remove-if만으로 개발이 가능하다.

(funcall (! #'remove-if) #'oddp lst)

(defvar *!equivs* (make-hash-table))

(defun !- (fn)
  (or (gethash fn *!equivs*) fn))

(defun def!- (fn fn!)
  (setf (gethash fn *!equivs*) fn!))

;; test
(setf lst '(1 2 3 4 5))
(def!- #'remove-if #'delete-if)


(print (remove-if #'oddp lst))
(print lst)

(print (funcall (!- #'remove-if) #'oddp lst))
;; or ((!- #'remove-if) #'oddp lst)
(print lst)
자 이렇게 새로운 직교규칙을 만들어서 쪼개버린다. 그렇게하여 작은언어 아니 작은 집합으로 쪼개진 언어를 가지게 된다.
(더 작은 블록은 가지게 된 것같은 느낌인데? 이렇게 desctructive에 대한 함수가 각각 두개씩 있는 것에는 블록에 유형이 있을 것이다.
우리는 그것이 뭔지는 모르지만 이렇게 임의로 쪼개버릴 수 있다.)
추가로 나는 online compiler에서는 !(느낌표)를 정의할 수 없어서 !-로 했다.

As well as greater orthogonality, the ! operator brings a couple of other benefits.
직교성을 높인 것 뿐만 아니라. !연산자는 다른 이점을 준다.
It makes progames clearer, because we can see immediately that (! #'foo) is destructive equivalent of foo.
프로그램을 깔끔하게 한다. (! #'foo)를 보면 바로 foo의 destructive버전인 것을 알 수 있다. (remove-if/delete-if는 헷갈릴 수밖에 없다)
Also, it gives destructive operations a distinct, recognizable form in sourece code, which is good because they should receive special attention when we are searching for a bug.
또한 destructive 연산자에 뚜렷하고, 눈에띄는 소스코드 형태를 제공한다. 이렇게 함으로써 버그를 찾을 때, 특별한 주의를 줘야 하는 부분(이렇게 값이 변하는 곳)을 쉽게 찾을 수 있게 된다.

Since the relation between a function and its destructive counterpart will usually be known before runtime,
destructive에 직교성에 대한 함수 관계까 런타임 이전에 알 수 있기 때문에
it would be most efficient to define ! as a macro, or even provide a read macro for it.
!를 매크로로 만드는 것이 유용할 것이다, 또한 더 나아가 이것을 위한 read macro를 제공한다. (여기서 read macro는 뭔지 모르겠음 아직 안나온 이야기)

직교성이 이렇게 중요한 것이었구나...
라는 생각을 한다.

댓글 없음:

댓글 쓰기