StandardClassExtensions for Ruby

Tags:

StandardClassExtensions

루비 standard library의 확장들입니다. 예를들어, standard library중 하나인 matrix는 immutable하죠. 즉,

a = Matrix[[1,2], [3,4]]
a[0,0]    # => 1
a[0, 0]   # => Error. Because a matrix is immutable.

하지만 standard class extensions을 사용하면

class Matrix
  #
  # Sets element (+i+,+j+) of the matrix to +value+.  That is: row +i+, column +j+.
  #
  def []=(i, j, value)
    @rows[i][j] = value
  end
end

와 같은 코드를 사용해 Matrix를 확장해 Mutable 하게 쓸 수 있습니다. (그나저나 대체 왜 기본 matrix는 immutable한겁니까….)

Comments

9 responses to “StandardClassExtensions for Ruby”

  1. daesan Avatar
    daesan

    글쎄요, 연산의 측면에서 보자면 Matrix의 각 element들을 변경해야 할 필요는 없지 않나요? Matrix를 2-dimensional array로 사용한다면 모르겠지만 굳이 그럴 이유는 없는거구..

  2. MKSeo Avatar
    MKSeo

    음.. matrix가 immutable이라면 dynamic programming은 뭘로 하나요;; 그리고 현재의 Matrix 구현은 array를 인자로 줄 때 defensive copying도 안하더군요.

  3. daesan Avatar
    daesan

    dynamic programming을 어떤 의미로 사용하신지는 잘 모르겠습니다만, Matrix 클래스는 기본적으로 수학의 행렬 연산을 위해서만 존재하는 것 같습니다. 이 경우에는 한 번 만들어진 행렬의 값을 변경한다는게 아무 의미가 없죠, 새로운 값을 가지는 행렬은 다른 행렬들간의 연산을 통해서만 만들어질테니까요..

    제 생각엔 defensive copying도 꼭필요하지는 않다고 생각하는게 기본적으로 다른 Array(다른 연산의 결과로 나온)를 끌어다가 Matrix 클래스의 팩토리 메소드에 넘겨주는게 아니라, 행렬을 처음 정의할때 다른 적당한 표기법이 없기 때문에 Array를 쓰는것이라서, 이 Array들은 행렬이 정의된 이후에는 다른 변수에 저장되어 있지 않는게 맞습니다. 물론 민구님께서 지적한 것처럼, encapsulation 측면의 문제가 있기는 하죠. 앗싸리 Matix의 initialize 메소드에서 파라미터로 사용된 Array들을 freeze시켜버리면 해결될 것 같습니다만~ ^^

  4. MKSeo Avatar
    MKSeo

    음 네.. 정말 행렬 연산용 인것 같아요. 고치면서 쓰고 싶다면 배열의 배열을 만들어서 쓰는게 제 목적엔 맞겠죠? immutable 하게하고 싶은데도 defensive copying을 안하는건 일종의 루비적인 철학 같아요.. 익숙해지기 힘든;;;;;

  5. daesan Avatar
    daesan

    Java를 오래 쓰시던 분들에게는 당연한 느낌인 것 같습니다. Java는 “해서는 안되는 것들은 절대로 못하게 해라!”의 철학을 가진 것 같고, dynamic language들은 “어짜피 개발자가 책임자니까, 그가 원하는대로 하게 해라!”의 철학을 가진 것 같습니다.

    Rubys는 “개발자가 원하는대로 하게는 하되, 불필요한 실수는 하지 않도록 배려하고, 바람직한 코딩 스타일을 장려해라!”의 철학 정도? Matz가 말하는 Happy Programming 또는 Dave Thomas가 말하는 Pragmatic Programming의 철학이 이런게 아닐까요? =)

  6. MKSeo Avatar
    MKSeo

    그건 꼭 자바 뿐만 아니라 C++도 그렇습니다. 한가지 예로, 메모리 해제의 담당자를 명확히 알려주기위해 생성자에서 auto_ptr 만 받게하는 디자인이 좋다라는 주장을 하는 내용이 Modern C++ Design에서도 나오죠. auto_ptr을 모르면 쓰기는 어려워지지만, auto_ptr을 쓰게 되는 순간 사용자는 그 클래스의 메모리 라이프 사이클 관리에 대해 확실히 알게된다는 것이 주장이었죠.

    반면 루비의 스타일은 제게 C를 생각나게 했었습니다. 물론 C와 같이 잘못하면 무조건 리부팅해야했던 그런 암울한 시절과는 전혀 다르지만요.

    그리고 Pragmatic Programmer의 철학이라…. 그 책은 반정도 밖에 못읽었지만 와닿는게 무척 많았습니다. (여담이지만, The cat ate my source code라는건 챕터 제목은 정말 재밌습니다.) 거기에서 주장했던 domain specific language라던가 하는 것들을 읽을때는, 말이야 좋지 어떻게 이렇게 다 만들까라고 생각했었는데, 루비를 사용한다면 가능하단 생각이 듭니다.

    참.. 그리고 잠깐 Matrix를 이렇게 저렇게 사용해봤는데, rows 란 메소드는 내부의 인자를 복사해서 외부로 반환합니다. 그러니까, 생성자에선 그냥 받아서 곧바로 쓰고, rows같이 접근할때는 복사해서 반환하고 하는 식이죠.

  7. daesan Avatar
    daesan

    자바빈클래스에 getter, setter 메소드를 반복적으로 코딩하다가 Rails의 ActiveRecord를 처음 써보면, 메타프로그래밍의 파워가 정말 실감이 납니다, 수십줄짜리 코드를 단 2줄로 줄여줘버리니까.. PP를 읽고, 나중에 Ruby를 알고나서는 Dave Thomas가 Ruby의 영어권 리더가 된게 너무 당연하게 느껴지더라구요.

    rows는 안전하게 돌려주는군요~ 그래도 한시름 놓고 쓰실 수 있겠네요~ ^^

  8. Sam Kong Avatar
    Sam Kong

    제 생각에 immutable 하게 만든 까닭은 그 타입을 마치 primitive type(value type)처럼 사용하도록 하기 위함인 것 같습니다.
    대부분의 언어에서 String이 immutable한 것도 그런 까닭이구요.
    a = Matrix.new
    b = a
    이렇게 한 후에 a나 b 둘 중의 하나의 값을 바꿨을 때 나머지도 바뀌는 것을 원치 않는다고 생각한 것 같아요.
    Integer나 Bignum와 마찬가지로 다루고 싶은 거죠.
    아마도 행렬은 수학에서 비롯한 것이고, 수학에서는 기본적으로 Functional Programming을 추구하고 Functional Programming은 side effect를 싫어하므로 immutable로 만든 것이 아닐까 하는 제 추측입니다.

  9. MKSeo Avatar
    MKSeo

    @Sam Kong: 네.. 제가 comp.lang.ruby 에서 찾아본 바로도 그렇다고 하더군요. 그 클래스를 만든사람이 행렬을 일종의 primitive 로 다루고 싶어한다고요. 그런데 그러려면 모든 종류의 행렬연산을 미리 다 정의해두지 않으면 내부 구현에 의존적인 확장을 사용자가 만들어야할 가능성이 큰 것 같던데….

Leave a Reply

Your email address will not be published. Required fields are marked *