2018년 12월 11일 화요일

[javascript] Object.create 함수로 크로스 브라우저 호환성 보완 - 05

/*
Object.create 함수로 크로스 브라우저 호환성 보완
*/
(function () {
  if(!Object.create) {
    Object.create = (function () {
      function F() {}
      return function (o) {
        F.prototype = o;
        return new F();
      }
    })();
  }
});

[javascript][prototype] ECMA의 class와 extends를 통한 상속 - 04

/* 
class와 extends를 통한 상속
자바스크립트의 new기능의 모호함을 해소하기 위해
ECMA6에서는 class와 extends를 정의한다.
*/

class Person {
  constructor() {
    this.name = "anonymous";
  }
}

class User extends Person {
  constructor() {
    super();
    this.name = "User";
  }
}

var user1 = new User();
console.log(user1 instanceof Person);
console.log(user1 instanceof User);
console.log(user1.constructor); // [Class: user]

[javascript][prototype] Object.create과 new 키워드로 생성자의 연결이 깨지지 않도록 - 03

/* Object.create와 new 키워드 조합 
Object.create과 new 키워드를 조합하여 생성자의 연결이 깨지지 않도록 해보자.
*/

function Person() {
  this.name = "unknown";
}

function User() {
  this.name = "user";
}

// 두 번째 매개변수는 키는 속성명으로 
// 새로 만든 객체에 추가될 속성 설명자(descriptor)를 지정합니다.
// writable option defaults to false.
// https://stackoverflow.com/questions/7757337/defining-read-only-properties-in-javascript
User.prototype = Object.create(Person.prototype, {
  constructor: { // Person.prototype.constructor 는 이제 읽기전용이다. 
    value: User  // 왜냐하면 기본이 writable = false 이다.
  }
})

var myuser = new User();
console.log(myuser instanceof User);
console.log(myuser instanceof Person);
console.log(myuser.constructor);
이 녀석은 이전

[javascript][prototype] 자바스크립트 프로토타입 맛보기 -02

function Car() {
  this.wheel = 4;
  this.beep = "BEEP";
}

Car.prototype.go = function () {
alert(this.beep);
  };

function Truck() {
  this.wheel = 6;
  this.beep = "TRUCK!!";
}

Truck.prototype = new Car();

function SUV() {
  this.beep = "SUV!!";
}
SUV.prototype = new Car();

var truck = new Truck(),
suv = new SUV();

console.log(truck.wheel);  // 6
console.log(suv.wheel);  // 4
console.log(truck.beep);  // TRUCK!!
console.log(suv.beep);  // SUV!!
Car라는 객체를 프로토타입에 넣어보니
Truck과 Suv 는 go라는 함수를 공통으로 사용할 수 있으며, 특히 SUV의 경우 wheel을 정의하지 않았지만
Car에 정의되어 있는 wheel = 4로 바퀴갯수 프로퍼티를 가질 수 있게 되었다.

[js] Object.create을 사용한 자바스크립트 상속 - 01

// 실제로는 Object.create 이라는 함수가 있다. 더글라스 크락포드가 주장했던 함수형태

Object.my_create = function(o) {
  function F() {};
  F.prototype = o;  // O는 처음부터 prototype역할을 하는 녀석일 것이다.
  return new F(); 
}

function Person(name) {
  this.name = name;
}

Person.prototype = {
  yell: function() {
    alert("My name is " + this.name);
  }
};

var u = Object.my_create(Person.prototype); // prototype을 넘기는 것을 주의하자.

u.name = "U";
u.yell();  // alert
/*
이렇게 Object.create 함수를 통해 객체를 생성하면 
개발자가 new 키워드를 사용하지 않고 함수 호출로 객체가 생성된다.

new 키워드를 사용할 때와는 달리 전체적으로 소스에 생성자의 개념이 사라지고 (new가 없으니까)
객체의 인스턴스와 인스턴스 간의 상속을 강조하는 것이 Object.create의 특징 (???)
*/

[js] 자바스크립트 상속기존의 자바스크립트 상속과 그 이면 - 00

function Person() {
  this.name = "anomymous";
  this.sayHello = function() {
    console.log(this.name + "!!");
  };
}

function Nam() {
  this.name = "Nam";
}
Nam.prototype = new Person();

var nam = new Nam();
nam.sayHello();
console.log(nam instanceof Nam);  // true
console.log(nam instanceof Person);  // true

console.log(nam.constructor);  // ?? Nam이 아니라 Person!!
/*
생성자 속성은 객체가 가지고 있는 것이 아니라. 프로토타입이 가지고 있다.
객체는 내부 링크로 프로토타엡에 있는 생성자 속성을 참조한다.

지금 Nam프로토타입은 
그런데 new Person()으로 원래 객체(여기선 Nam)의 생성자를 가지고 있는 프로토타입을 덮어씌우면
Nam의 생성자는 연결고리를 잃는다. (원래 Nam의 생성자는 function Nam() {...} 을 가리키고 있어야 한다.)

따라서 nam.constructor를 실행하면 [Function: Person] 이 나온다.
뭐 잘 작동하기는 하지만 연결이 끊어진다는 것은 좋은 것이 아니다.
*/


괜찮은 링크.
https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67

2018년 11월 6일 화요일

[java][thread][unsafe] 자바가 숨겨놓은 악마의 열매 sun.misc.Unsafe - 1

sun.misc.Unsafe

이름부터 강력함을 뽐내는 이 클래스는 아무나 쓸 수 없는 녀석이다.
사실 이 강력함은 우리를 다치게 할 수 있다. 그 어떤 강력한 것도 항상 제물이 필요하다.

열정을 더 불태우기 위해 내 몸을 불 속에 몸을 던지는 행위와 같이
이 세상 모든 뜨거운 것에는 장작이 필요하다.

Unsafe가 그러하다. 자바를 더욱 핫하게 만들어줄 이 녀석은 사실 우리는 쓰지 못하도록 되어 있다.
이 강력한 힘은 사용자가 쓰지는 못하도록 만든 것이다.
더 나아가, 필자의 Eclipse에서는 아예 존재하지 않는 것처럼 보인다.

너무 강력하고 위험하기 때문에, 아예 import조차 못하도록 막은 것이다. (해당 설정을 바꾸면 가능하다)

자바에서도 C와 같은 강력한 기능을 사용하려면 JNI를 이용하여 C언어 개발을 해야 할 것이다.
하지만 C를 모르고 알고 싶지도 않은 사람이 있을 것이다. 하지만 java.misc.Unsafe를 이용하면 Java API를 이용하여
low-level 프로그래밍을 할 수 있게 해준다.

일단 Unsafe를 내가 찾게 된 연유를 보자. 나는 AtomicInteger라는 녀석의 구현을 보고 싶었다.
어느때와 같이 AtomicInteger에서 숫자를 변경하는 메소드를 보았으나. 내 눈앞에 보이는건 Unsafe클래스였다.
안으로 들어가도 바이트코드만 보이는 것을 보고. 뭔가 심각한 녀석임을 눈치챘다.

녀석은 생성자가 private으로 만들어져 있다. 그리고 싱글턴 인스턴스가 private으로 존재한다. 그러므로 getUnsafe라는 녀석이 있는 것으로 봐서
녀석을 실행시키면 된다고 생각했다. 하지만 예외만 던져질 뿐이었다. 왜 그러한지 한 번 둘러보자.
public static Unsafe getUnsafe() {
  Class cc = sun.reflect.Reflection.getCallerClass(2);
  if (cc.getClassLoader() != null)
    throw new SecurityException("Unsafe");
  return theUnsafe;
}
여기서 볼 수 있듯이 일단 theUnsafe라는 녀석이 싱글턴 인스턴스다.
그리고 눈에 보이는 이상한 것들이 있는데 바로 sun.reflection.Reflection 이다.
이녀석은 현재 스레드의 메소드 스택을 확인하여 주어진 숫자 만큼 call-stack-frame 밑으로 내려가서 해당 메소드의 클래스를 리턴한다.
(이 구현을 변경될 수도 있다) 지금 숫자에는 2가 들어있으니까 콜스택의 3번째 값을 가져온다.

index 0. Reflection (getCallerClass)
index 1. Usnafe (getUnsafe)
index 2. Unsafe를 실행하는 나의 어플 내에 있는 메소드

이게 index 2 값을 가져와서 getClassLoader를 실행시킨다. 소스 상에서는 null이어야 한다.
null 레퍼런스는 HotSpot virtual machine 위에 bootstrap class loader를 가리킨다. (이 내용은 {@link Class#getClassLoader()}에 문서화 되어 있다.

아니면 예외를 던진다. (좀 더 자세한 내용은 참고 깃허브 참고)


하지만 트릭은 언제나 존재한다.

바로 이렇게 콜스택으로 체크 하는 방식이 아주 대충 디자인 된 것이라고 한다.
바로 리플렉션의 사용을 막지 않았기 때문에 바로 가져오면 된다.
public void getUnsafe() throws Exception {
  Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
  theUnsafe.setAccessible(true);
  Unsafe unsafe = (Unsafe) theUnsafe.get(null);
  System.out.println(unsafe);
}
이렇게 하면 되지만 안드로이드에서는 Unsafe클래스의 이름이 "theUnsafe"가 아니라. THE_ONE이라는 이름으로 불린다고 하니 이런 코드는 플랫폼에 의존적이다.
다르게 가져와보자.
public Unsafe getUnsafeInstance() throws Exception {
  Constructor unsafeConstructor = Unsafe.class.getDeclaredConstructor();
  Unsafe unsafe = unsafeConstructor.newInstance();
  System.out.println(unsafe);
  return unsafe;
}

좋다. 이렇게 가져와 봤다.
이게 얼마나 위험하기에 이렇게 힘겹게(?) 가져와야 하는 것일까?

해당 내용은 다음에 만들 이야기

[java][thread][unsafe] 자바가 숨겨놓은 악마의 열매 sun.misc.Unsafe - 2

에서 확인해보자.

2018년 11월 5일 월요일

[java][thread] volatile 대체 어디서 쓸만한 걸까?

참고 사이트 (내 깃허브): https://github.com/ssisksl77/java-demo/tree/master/src/main/java/concurrent/volatile_test

volatile은 멀티스레드에서 값들을 캐시하지않고 변경된 내용을 바로바로 가져올 수 있게 해준다.
오! 그렇다면 아무 곳에나 volatile만 넣어준다면 마법처럼 동기화가 되면서 공유상태를 사용할 수 있는 것일까?
아래 소스코드와 결과를 보면 그렇제 않은 것 같다.
package volatile_test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class VolatileTest1 {
 private volatile static int counter = 0;
 
 public static void main(String[] args) {
  Runnable r1 = () -> { System.out.println(counter); counter++;};
  
  ExecutorService es = Executors.newCachedThreadPool();
  
  for(int i = 0; i < 10; i++) {
   es.execute(r1);
  }
  
  es.shutdown();
//              OUTPUT
//  0
//  0
//  0
//  3
//  4
//  5
//  6
//  7
//  8
//  9

 }
}
0이 3번이나 나왔다. 왜그럴까? 특이한 것은 결과적으로 +1이 잘 되었지만 조회가 잘 안되었다는 것인데 다시 한 번 해보자.
0
0
0
0
0
0
6
6
8
8
전혀 다른 값이 나왔다. 그 말은 값이 달라졌다는 것이다. volatile을 사용한다고 동기화가 되는 것이 아니다. 읽기-수정-쓰기 가 동기화가 되야 하는 것이다. 그렇다면 volatile은 언제 써야 할까? 아래와 같은 경우를 보자.
package volatile_test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class VolatileTest2 {
 // 아래 내용을 보자. 과연 잘 돌아가는 경우가 얼마나 있을까?
 private static boolean flag = true;
 
 public static void main(String[] args) {
  Runnable r1 = () -> { 
   while(flag) {
     // 여기에 막히게 된다. 
   }
   System.out.println("bye");
  };
  
  Runnable r2 = () -> { System.out.println("flag false start"); flag = false;};
  ExecutorService es = Executors.newCachedThreadPool();
  
  for(int i = 0; i < 10; i++) {
   es.execute(r1);
  }
  es.execute(r2);
  
  es.shutdown();

 }
}
여기서는 전혀 돌아가지는 않는다. 이것은 volatile의 부재일 수도 있지만, 컴파일러 마다 최적화를 하면서 결과값이 다르게 나올 수도 있다고 한다. 여튼 위 내용은 현재 flag를 false로 변경했지만 먹히지 않는다. 이때 volatile을 넣어보자.
package volatile_test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class VolatileTest3 {
 // 이럴때 volatile로 바꾸는 것만으로 효과가 있다.
 private volatile static boolean flag = true;
 
 public static void main(String[] args) {
  Runnable r1 = () -> { 
   while(flag) {
    // System.out.println("hi");
   }
   System.out.println("bye");
  };
  
  Runnable r2 = () -> { System.out.println("flag false start"); flag = false;};
  ExecutorService es = Executors.newCachedThreadPool();
  
  for(int i = 0; i < 10; i++) {
   es.execute(r1);
  }
  es.execute(r2);
  
  es.shutdown();

 }
}

2018년 10월 29일 월요일

[python] 월별로 난수 생성하기

파이썬에는 아쉬운게 timedelta에 month를 더하는 것이 없다. 다른 유틸을 받아서 사용하면 된다고 하는데 이렇게 해도 잘 되는 것 같다.
import random
import string
import csv
import datetime
import calendar

random_numbers = set()
for _ in range(120):
res = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(16))
random_numbers.add(res)

def add_months(sourcedate, months) -> datetime.date:
month = sourcedate.month - 1 + months
year = sourcedate.year + month // 12
month = month % 12 + 1
return datetime.date(year,month,1)


somedate = datetime.date(2018, 4, 1)
with open('random_number.txt', 'w', newline='\n') as f:
w = csv.writer(f, delimiter=' ', quoting=csv.QUOTE_NONE)
for idx, num in enumerate(random_numbers):
w.writerow([add_months(somedate, idx).strftime('%Y-%m'), num])


# print(len(random_numbers))

[codility][python] max counter

codility max counter

https://app.codility.com/demo/results/trainingUBUCBJ-RVE/

def solution(N, A):
  res = [0] * N
  max_counter = 0

  for a in A:
    if a <= N:  # N카운터 존재
      res[a-1] +=1  # 카운터 올림
      if max_counter < res[a-1]: # max_counter 보다 크면 현재 카운터를 tmp_max로 변경
        max_counter = res[a-1] 
    else: # max counter 실행
      for i in enumerate(res):  # 전부 max_counter로 덮어씌운다.
        res[i] = max_counter
  return res
빅오노테이션이 O(MxN) 인 관계로 이중포문에서 하나를 없애야 한다.
# N 카운터가 있음. 0으로 세팅
# max counter : 모든 카운터들이 아무 카운터의 최대 값으로 세팅됨.

# A[K] = X 이면, K오퍼레이션은 X를 증가시킨다.
# -> 값이 X면 X를 증가시킴 (X범위 1<=X<=N)
# A[K] = N + 1 이면 K는 max counter  (??? K가 N보다 크면 max??)
# 아아아 max counter라는 걸 실행함!!!

def solution(N, A):
  res = [0] * N
  max_counter = 0
  tmp_max = 0
  for a in A:
    if a <= N:  # N카운터 존재
      if max_counter > res[a-1]:  # 1 max_counter가 더 크면 max_counter가 갱신된 것
        res[a-1] = max_counter  # 2 max_counter 값으로 변경해준다.
      res[a-1] +=1  # 3 그리고 그 다음에 카운터 시킨다
      if tmp_max < res[a-1]:  # 4 tmp_max보다 크면 현재 카운터를 tmp_max로 변경
        tmp_max = res[a-1]

    else:  # max_counter 에 현재 tmp_max를 넣는다. (max_counter는 그때그때 실행할 것이다.)
      max_counter = tmp_max

  # 아직 max_counter가 변경 된 이후에 counter가 변경된 이력이 없으면 max_counter로 덮어씌워준다.
  # 물론 max_counter보다 작은 경우.
  for idx, _ in enumerate(res):  
    if (res[idx] < max_counter):
      res[idx] = max_counter

  return res

a = [3,4,4,6,1,4,4]
N = 5

print(solution(5, a))

[hackerrank][python]Journey to the Moon

hackerrank: Journey to the Moon


1st attempt

# Determine how many pairs of astronauts from different countries they can choose from.
# UN 연합은 달에 사람들을 보내려고 한다.
# 그들은 이 사람들이 서로 다른 나라였으면 한다.
# 당신은 두 쌍의 우주조종사 id 리스트를 받을 것이다.
# 각 쌍은 같은 나라 우주조종사 id의 두 쌍으로 이루어져 있다.
# - 얼마나 많은 조종사 쌍을 만들 수 있는지 말해라.

# n : number of astronaut
# p : the number of pairs
from collections import defaultdict

# 일단 연결된 것들을 만든다.
def dfs(graph, start):
  visited, stack = set(), [start]
  while stack:
    v = stack.pop()
    visited.add(v)
    # 해당 정점에 연결된 것들을 stack에 넣는다.
    # 방문한 것은 제외
    stack.extend(graph[v] - visited)
  # 연결된 것들을 리턴.
  return visited

def journeyToMoon(n, ast_pairs):
  graph = defaultdict(set)
  asts = set()
  # 그래프 연결...
  for a1, a2 in ast_pairs:
    graph[a1].add(a2)
    graph[a2].add(a1)
    asts.add(a1)
    asts.add(a2)
  
  # 고립된 애들이 있을 수 있나? 일단 받아놔... (있는듯...)
  iso_asts = set([i for i in range(n)]) - asts
  
  groups = []  # divied by contry
  while asts:
    ast = asts.pop()
    connected_asts = dfs(graph, ast)
    groups.append(connected_asts)
    # 한번 방문한 팀은 다시 방문하지 않는다.
    asts = asts - connected_asts
  
  for i in iso_asts:
    groups.append({i})
  
  idx1, idx2 = 0, 1
  group_sum = 0
  while idx1 < len(groups)-1:
    idx2 = idx1 + 1
    while idx2 < len(groups):
      group_sum += len(groups[idx1]) * len(groups[idx2])
      idx2 += 1
    idx1 += 1
 
  return group_sum
    
  
n, p = list(map(int, input().strip().split(' ')))
ast_pairs = []
for _ in range(p):
   astronaut = [int(a) for a in input().strip().split(' ')]
   ast_pairs.append(astronaut)
   
result = journeyToMoon(n, ast_pairs)
print(result)
def dfs(graph, start):
  visited, stack = set(), [start]
  while stack:
    v = stack.pop()
    visited.add(v)
    stack.extend(graph[v] - visited)
  return visited

def journeyToMoon(n, ast_pairs):
  graph = defaultdict(set)
  asts = set()
  for a1, a2 in ast_pairs:
    graph[a1].add(a2)
    graph[a2].add(a1)
    asts.add(a1)
    asts.add(a2)
  
  iso_asts = set([i for i in range(n)]) - asts
  
  groups = []  # divied by contry
  while asts:
    ast = asts.pop()
    connected_asts = dfs(graph, ast)
    groups.append(connected_asts)
    # 한번 방문한 팀은 다시 방문하지 않는다.
    asts = asts - connected_asts
  
  for i in iso_asts:
    groups.append({i})
  
  # 뒤에 추가된 것들을 따로 분리해야 한다. 그래야 추가된 녀석들이 만드는
  # 패턴을 알 수 있다.
  # A*B + A*C + B*C -- A*B + A*C + B*C -- A*B + (A+B)*C 
  # A*B + A*C + A*D + B*C + B*D + C*D 
  # - A*B + (A+B)*C (A+B+C)*D
  # A*B + A*C + A*D + A*E + B*C + B*D + B*E + C*D + C*E + D*E
  # - A*B + (A+B)*C + (A+B+C)*D + (A+B+C+D)*E 
  idx1, idx2 = 0, 1
  group_sum = 0
  tmp_sum = 0
  while idx1 < len(groups):
    group_sum += tmp_sum*len(groups[idx1])
    tmp_sum += len(groups[idx1])

    # group_sum += tmp_sum*(len(groups[idx1 + 1]))
    idx1 += 1
  
  return group_sum
    
  
n, p = list(map(int, input().strip().split(' ')))
ast_pairs = []
for _ in range(p):
   astronaut = [int(a) for a in input().strip().split(' ')]
   ast_pairs.append(astronaut)
   
result = journeyToMoon(n, ast_pairs)
print(result)

[javascript] 접근자 defineProperties를 이용한 생성자 설정.

function Person(name) {
this.name = name;
}
Person.prototype = {
  yell: function() {
    console.log("my name is " + this.name);
  }
}
/* 수정불가 */
var user = Object.create(Person.prototype, {
  name: {
    value: 'new name'
  }
})

/* 수정가능 */
user = Object.create(Person.prototype, {
  name : {
    value: 'new name2',
    configurable: true,
    enumerable: true,
    writable: true
  }
})

/* 접근자 활용의 예 */

Object.defineProperties(user, {
  firstName: {
    value: "Younghwan",
    writable: true
  },
  lastName: {
    value: "Yang",
    writable: true
  },
  fullName: {
    get: function() {
      return this.firstName + " " + this.lastName;
  },
  set: function(value) {
    var res = value.split(' ');
    if (res.length > 1) {
      this.firstName = res[0];
      this.lastName = res[1];
    } else {
      console.log('wrong format');
    }
  }
});

console.log(user.fullName);
user.fullName = "Hello world!";
console.log(user.firstName); // Hello
console.log(user.lastName); // World!