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:

if else 대신 빠른 return의 코딩 스타일

회사에서 코드 리뷰를 하다가 알게된 if-else 대신 빨리 return하는 코딩 스타일인데 접해보지 않은 분들도 계실것같아 올려봅니다. stack overflow에도 Programming style: should you return early if a guard condition is not satisfied?란 제목으로 글이 올라와 있기도 하고 effective go에도 간단히 언급되어 있는 스타일입니다.

stackoverflow에 있는 질문을 옮겨보자면 다음 두가지 코딩 스타일 중 어느것이 나은가라는 것입니다.

1. 반환값을 만들고 if문 또는 if else를 사용하는 경우

returnVal = null;
if (bar()) {
  returnVal = baz();
}
return returnVal;

2. 조건이 안맞으면 바로 리턴하는 경우

if (!bar()) {
  return null;
}
return baz();

저는 이 두가지 방법중 2번 방법을 선호합니다. 그 이유는 bar() 조건이 안맞는 경우 빨리 리턴해버림으로써 그 아래 코드를 읽을때 bar 조건이 안맞는 경우를 머리 속에서 제외하고 생각할 수 있기 때문에 코드 읽기가 쉬워지기 때문입니다. 위의 코드 정도야 간단해 보이지만 다음과 같이 복잡한 if-else 경우를 생각해보죠.

if (condition1) {
  foo1();
} else if (condition2) {
  foo2();
} else {  // condition1 == false && condition2 == false
  foo3();
}

이 예에서는 간단히 foo1(), foo2(), foo3() 정도만 호출했지만 실제로는 블럭 사이에 코드가 길어질 수 있고 그 경우 코드를 위에서 아래로 읽어오다가 마지막 else 문을 만나면 앞서 확인한 조건들이 무엇인지 잊어버리기 쉽습니다. 그런 이유로 위에처럼 주석에 어떤 조건들이 제외되었는지 적어주는 스타일이 사용되기도 하죠.

그런데 이 경우는 return 을 빨리 해버리면 더 쉽게 읽고 쓸 수 있습니다.

if (condition1) {
  foo1();
  return;
}
if (condition2) {
  foo2();
  return;
}
foo3();

위에서 아래로 오면서 condition1 이 만족되면 바로 return 해버립니다. 그렇기 때문에 그 아래 if 문을 읽을땐 condition1을 머리속에서 지워버릴 수 있고, 마찬가지로 condition2에서 return 을 빨리 해버리면 condition2를 머리속에서 지워버릴 수 있어서 foo3()을 읽을때 앞서 제외된 조건이 무엇이었는지 기억하는데 부담이 덜합니다.

좀 더 극단적인 경우를 비교해보겠습니다. 다음은 if-else를 중첩하여 사용한 경우입니다.

if (condition1) {
  foo1();
  if (condition2) {
    foo2();
  } else {
    foo3();
  }
} else {
  foo4();
}

같은 코드를 적극적으로 return 하는 형태로 바꾸면 다음과 같습니다.

if (!condition1) {
  foo4();
  return;
}
foo1();
if (condition2) {
  foo2();
  return;
}
foo3();

한 함수의 길이가 20-30 라인으로 제한되면 좋다는 것이야 누구나 알지만, 바쁜 일정속에 리팩토링에 몸을 던지는 용감한 희생자가 없는한 코드는 길어지기 마련이라 복잡한 if-else는 종종 볼 수 있습니다. 더구나 마지막 예에서 보인바와같이 if-else가 한없이 깊어지기만 할 때도 있죠. 하지만 위와같이 빠른 return을 사용하면 그 복잡한 중첩들을 없앨 수 있게됩니다.

Similar Posts: