트위터

어느덧 이제 블로그를 한지 굉장히 오랜 시간이지나 얼마나 했는지도 모를만큼 시간이 지났습니다. 다른 모든 개발자 하늘처럼 나는 이제 글도 별로 쓰지 않는데 뭔가 배우는 것이 없어서가 아니라 그냥 글을 쓰지 않기 때문에 글을 쓰지 않고 있네요.

트위터에는 종종 글을 쓰는데 와서 봐 줬으면 좋겠습니다. @Minkoo 입니디.

Similar Posts:

AutoValue in Java

https://github.com/google/auto/blob/master/value/userguide/index.md

@AutoValue 만 쓰면 인터페이스로부터 immutable value class를 만들어준다. 좋긴한데 뭔가 좋지 않다. 자바는 진짜 언어가 너무 복잡한거 아닌가.

Similar Posts:

typing.NamedTuple

파이썬의 typing, types를 보면 신기한 것들이 많이 추가되고 있다.

최근 @golbin 님으로부터 배운 것중 하나는 NamedTuple.

In [1]: from typing import NamedTuple

In [2]: class Employee(NamedTuple):
   ...:     name: str
   ...:     id: int
   ...:

In [3]: e = Employee('foo', 234)

In [4]: e
Out[4]: Employee(name='foo', id=234)

파이썬 3.7부터는 @dataclass라는 annotation으로 가능해진다.

@dataclass
class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

혹시 -> float 부분이 뭐지 싶으신 분은 Type Hints를 참고하시길.

Similar Posts:

choco

윈도우용 유니버셜 인스톨러.

https://chocolatey.org/

예를들어 ssh 도 이렇게 설치 가능하다.

C:\> choco install openssh

Similar Posts:

파이썬 코드에서 중간에 콘솔 띄우는 디버깅

파이썬 디버깅할 때 쉽게 하는 방법중 하나입니다.

코드를 실행하다가 중간에 로컬 변수를 모두 볼 수 있는 콘솔을 띄우려면

a = 3
code.interact(local=locals())

이렇게 합니다.

그러면 파이썬 콘솔이 a=3 뒤에 실행되고 곧바로 print(a) 로 그 내용을 볼 수 있습니다.

Similar Posts:

Rounding Half to Even

반올림을 할 때의 문제는 0.5 입니다. 0.5를 제외하고는 절반은 작은 수, 절반은 큰수로 가죠. 예를들어 round(0.3)=0 이고 round(0.6)=1입니다. [0, 1] 사이의 수에 반올림을 하면 소수점 이하가 0.5인 경우를 제외하고는 round()를 하고나면 절반은 0, 절반은 1이되어 기대값이 0.5가 됩니다. 문제는 0.5인데, 통상 우리가 아는 반올림은 round(0.5)=1입니다. 이 방법을 Round Half Up이라고 합니다.

이 방법의 문제점은 대칭적이지 않다는 것입니다. 예를들어 round(-1.5)=1, round(1.5)=2입니다 즉, x.5 를 반올림하면 항상 큰쪽으로만 값이 쏠립니다. 또 다른 예로 [0, 2] 사이의 임의의 값을 반올림할 경우 그 기대값이 1.0이 아니게됩니다.

이런 문제를 해결하기위한 방법 중 하나가 Round Half to Even입니다. 0.5가 걸리면 가까운 짝수로 가는 방법인데요. round(24.5)=24, round(23.5)=24 처럼 가까운 짝수로 보냅니다.

바로 이 방법이 Python 3.0에서 채용하고 있는 round 연산입니다.

In [4]: round(2.5)
Out[4]: 2

In [5]: round(1.5)
Out[5]: 2

Similar Posts:

모든 문제를 내가 해결할 필요는 없다

프로그래머는 버그가 발생하면 그 원인을 찾고 해결하는데 익숙합니다. 무언가 잘 동작되지 않으면 원인을 찾는데 그치지 않고 문제를 직접해결하게 되기도합니다.

그런데 회사에서 일을 할 때는 모든 문제를 꼭 내가 해결할 필요가 없다는 말을 적고 싶습니다. 해당 컴포넌트를 담당하는 사람이 있다면 그 사람에게 버그 리포팅을 할 수도 있을 것이고, 뭐가 잘못된건지조차 알 수 없다면 담당자에게 문의를 할 수도 있을 것입니다.

만약 담당자에게 일을 넘기지 않고 자신이 직접 문제를 해결하려하면 결국은 내 일의 진행이 늦어질 우려가 있습니다. 담당자에게 일을 넘기면 또 다른 할일을 그 시간에 처리할 수 있을텐데, 혼자 문제를 해결한다고 귀중한 내 시간을 써버리게 되기 때문입니다.

해당 부분의 담당자에게 일을 넘기는건 담당자에게도 득입니다. 담당자이니 당연히 해야할 일을 하는 것인데다가 다른 팀이 자기 컴포넌트를 잘 쓰게 도와주게되는 것이므로 자신의 임팩트가 커지게되는 셈이기 때문입니다.

반면에 내가 해당 컴포넌트를 고치는건 훌륭한 사용자가 되는 길이기는 하지만 자신에게는 큰 득이 되지 않을 수 있습니다. 앞서 말한 기회비용의 측면, 즉 내 프로젝트가 늦어진다는 이유 때문입니다.

이것이 프로그래머가 주어진 문제를 다 해결하려는 본능을 누르고 적절한 협업을 해야하는 이유입니다.

Similar Posts:
    None Found

AMP (Accelerated Mobile Pages)

https://www.ampproject.org/
성능을 우선한 설계와 캐싱으로 모바일 페이지 로딩 속도를 향상시키는 open source initiative. 이를 사용중인 회사에는 Google, LinkedIn, Twitter등이 있습니다.

Similar Posts:

TeraSort on Hadoop

아파치 하둡에서 예~전에 했던 테라소트에 대한 페이퍼가 TeraByte Sort on Apache Hadoop에 있습니다.

입력 데이터가 상당히 흥미로운데 대회 홈페이지입력 데이터 FAQ를 보면 JouleSort의 경우 key가 10바이트이고 key의 각 자리는 95개의 value에 대응된다고 합니다. 따라서 key가 같다고 value가 같지는 않지만 key 순으로 정렬하면 value도 정렬됩니다. 그래서 http://www.slideshare.net/mobile/tungld/terasort에 있는 것처럼 키값을 여러개의 범위로 나누고 각 범위를 reducer에 할당한 다음 reducer는 자기에게 주어진 값들만 정렬시키면 전체적으로도 정렬이 되는 방식입니다.

본래 쉬운 문제가 아닌데 key로 value를 요약해서 표현한 아이디어가 문제풀이를 한층 쉽게 만들어주고 있습니다.

Similar Posts:

Rvalue reference와 함수의 반환값

C++에서 const Klass&반환값 형태의 단점들을 쓴지도 시간이 많이 지났네요. C++11에서는 많은 것이 바뀌었습니다. 대표적인 것이 rvalue reference로 대표되는 Move semantics입니다. Move는 RVO(return value optimization)가 동작할 수 없을 때 객체의 복사비용을 줄이는 목적으로 사용됩니다. Move는 객체를 “복사”하는 대신 객체가 내부에 가진 포인터만 가져옵니다. 그런이유로 속도가 매우 빠릅니다.

이 글에서는 rvalue reference와 관련해 함수의 리턴 타입과 적절한 반환값에 대해서 살펴보겠습니다.

지역 변수를 반환할 때
Herb Sutter는 widget* load_widget() 처럼 포인터를 반환하는 구시대적(?) 리턴 타입을 비판하면서 현대적 방식으로 다음 두가지 규칙을 제안했습니다.
1. 만약 반환할 객체에 다형성이 필요하다면 unique_ptr로 반환한다.
2. 만약 다형성이 필요없다면 그 반환할 객체가 값임을 의미한다. 그러므로 복사나 이동이 가능한 값으로 반환한다.

1번 규칙은 Scott Meyer의 Modern Effective C++에서 factory가 unique_ptr을 반환해야한다고 제안한 것과 일맥 상통합니다. 또 누가봐도 메모리 관리가 편하고, 메모리의 소유권이 명확하며, 객체의 복사가 필요없는 unique_ptr을 반환하는건 명확해 보입니다.

2번이 문제인데, 대체 어떻게 값으로 반환해야하는가 역시 고민거리이기 때문입니다. 이에 대한 해답을 다음 코드로 제시합니다.

#include <iostream>
#include <memory>

using namespace std;

class Foo {
  public:
    Foo(): destructed_(false) {
      cout << "ctor" << endl;
    }

    Foo(const Foo& other) {
      cout << "copy ctor" << endl;
    }

    Foo(Foo&& other) {
      cout << "move ctor" << endl;
    }

    Foo& operator=(const Foo& rhs) {
      cout << "copy assign";
    }

    Foo& operator=(Foo&& rhs) {
      cout << "move assign";
    }

    ~Foo() {
      destructed_ = true;
    }

    bool destructed_;
};

Foo retVal() {
  Foo f;  // 여기서 만들어진 객체가 그대로 main에서 사용된다.
  return f;  // RVO가 동작하므로 복사도 이동도 불필요
}

Foo retMove() {
  Foo f;
  return move(f);  // move를 명시하므로 move가 우선해서 사용됨
}

Foo&& retDangling() {
  Foo f;
  // reference 반환시 객체가 파괴되므로 런타임 오류.
  // reference는 항상 살아있는 객체에 대해서만 반환 해야한다.
  return move(f);
}

Foo retParam(Foo param_f, bool b) {
  // if-else로 인해 RVO가 동작하지 않는 경우.
  if (b) {
    Foo local_f;
    // 만약 RVO가 동작하지 않으면 자동으로 move가 시도된다.
    // 사실 이 경우가 move가 등장한 배경 중 하나.
    return local_f;  // move!
  }
  return param_f;  // move!
}

int main() {
  cout << "retVal" << endl;
  Foo f = retVal();
  cout << endl << "retMove" << endl;
  Foo f2 = retMove();
  cout << endl << "retParam, true" << endl;
  Foo f3 = retParam(Foo(), true);
  cout << endl << "retParam, false" << endl;
  Foo f4 = retParam(Foo(), false);
  cout << endl << "retDangling" << endl;
  Foo&& f5 = retDangling();
  cout << "f5 is " << (f5.destructed_ ? "destructed" : "live") << endl;
  return 0;
}

출력은 다음과 같습니다.

retVal
ctor

retMove
ctor
move ctor

retParam, true
ctor
ctor
move ctor

retParam, false
ctor
move ctor

retDangling
ctor
f5 is destructed

위 코드로 미루어볼 때 아무것도 모르는 사람이 코딩하듯이 지역변수를 반환할 때는 move없이 값으로 반환하는 것이 최상임을 알 수 있습니다. 그러면 RVO가 가능하면 RVO가 되고, 안되면 move가 시도되는 것을 알 수 있습니다. 그도 안되면 copy가 되겠죠.

이 규칙에는 한가지 예외가 있습니다. 반환하는 지역변수의 타입과 함수의 리턴 타입이 일치하지 않는 경우입니다. 다음은 함수의 리턴 타입은 optional<Foo>인데 실제 반환하는 값은 Foo인 경우를 보여줍니다. 이 경우에는 RVO나 move가 자동으로 동작하지 않아 move()를 반드시 해줘야합니다. 그러나 애초에 이런 암시적인 형변환에 의한 반환 자체가 나쁜거겠죠. 처음부터 optional<Foo>를 반환하면 될일입니다.

optional<Foo> returnOptional() {
  Foo f;
  return move(f);
}

객체의 멤버를 반환할 때
객체의 멤버 변수를 반환할 때는 지역변수와 달리 반드시 move를 해야합니다. 이에 대한 예를 다음 코드에 보였습니다.

#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>

using namespace std;

class Foo {
  public:
    Foo(int v): value_(v) {
      cout << "foo ctor" << endl;
    }

    Foo(const Foo& other): value_(other.value_) {
      cout << "foo copy" << endl;
    }

    Foo(Foo&& other): value_(other.value_) {
      cout << "foo move" << endl;
    }

    int value() const {
      return value_;
    }

    ~Foo() {
      destructed_ = true;
    }

    bool isDead() {
      return destructed_;
    }

  private:
    int value_;
    bool destructed_ = false;
};

class C {
  public:
    C() {
      vals_.push_back(1);
      cout << "C ctor" << endl;
    }

    C(const C& other) {
      cout << "C copy" << endl;
    }

    C(C&& other) {
      cout << "C move" << endl;
    }

    ~C() {
    }

    void add(int v) {
      vals_.push_back(v);
    }

    vector<Foo> ret_val() &&;
    vector<Foo> ret_move() &&;


  private:
    vector<Foo> vals_;
};

// 함수명 뒤의 &&는 rvalue reference에 호출되는 함수임을 의미
vector<Foo> C::ret_val() && {
  // 그냥 반환하면 copy.
  // 지역변수의 경우와 달리 rvalue임에도 RVO나 move가 자동으로 되지 않는다.
  return vals_;
}

vector<Foo> C::ret_move() && {
  // move가 수행됨
  return move(vals_);
}

int main() {
  cout << "vals_ret" << endl;
  auto vals_ret_val = C().ret_val();
  cout << endl << "vals_move" << endl;
  auto vals_ret_move = C().ret_move();
  return 0;
}

다음은 실행 결과입니다.

vals_ret
foo ctor
foo move
C ctor
foo copy

vals_move
foo ctor
foo move
C ctor

Similar Posts: