비트 연산이란?

비트 연산이라 함은 &, |, ~, ^ 을 이용한 연산을 의미한다.

비트 연산은 언제 사용하는가?

우선 비트 연산을 사용하면 bit라는 최소 정보단위로 다른 추상화된 정보들과는 달리 재현의 과정을 거치지 않는다.(그렇기에 속도는 빠르다)

즉, 각 비트들의 0 혹은 1이라는 값을 특정 의미를 가지는 플래그로 활용한다.

비트 연산 예시

우선 아래의 예시를 보자.

const OPTION_A = 0x01;
const OPTION_B = 0x02;
const OPTION_C = 0x04;
const OPTION_D = 0x08;

위의 OPTION A, B, C, D의 값은 16진수로 표현하였다.

즉 16진수는 4비트로 표현이되며 A는 0001(1), B는 0010(2), C는 0100(4), D는 1000(8)을 의미한다.

만약 1바이트(8비트)를 표현하고 싶다면 어떻게 해야 될까?

아래의 예제를 보며 이해를 해보길 바란다.

const OPTION_E = 0x10;

E는 0001 0000을 의미한다. 추가적으로 비트를 늘릴 때는 위의 원리를 이용하면 된다.

해당 비트를 1로 변경할 때는 아래의 예제를 참고하자.

var flag |= OPTION_A

해당 비트가 1인지 확인할 때는 아래의 예제를 참고하자.

var flag &= OPTION_A

해당 비트를 0으로 변경할 때는 아래의 예제를 참고하자.

var flag &= ~(OPTION_A)

정리

비트 연사을 사용하면 하나의 정수로 여러 플래그의 표현이 가능하다.

비트 연산은 알고리즘 문제에 대해서도 많이 사용되므로 정확히 이해하고 넘어가면 좋을 것 같다.

'Language > Javascript' 카테고리의 다른 글

Name Spacing Pattern  (0) 2017.07.28
promise - 2 [Promise 사용 시 주의할 것]  (0) 2017.07.28
promise - 1 [then, catch, resolve, reject]  (0) 2017.07.28
closure  (0) 2017.07.28
scope - 3 [변수 숨기기]  (0) 2017.07.28

[Lint Code] Split String

문제 : 문자열이 주어졌을 때 한 개의 문자 혹은 두 개의 문자로 구성된 문자열들로 구성할 수 있는 전체 경우를 구하시오

[예시]

Input -> "123"
 
Output -> [["1", "2", "3"], ["12", "3"], ["1", "23"]]

[풀이]

이 문제는 [start:start+1]과 [start:start+2]의 경우를 DFS를 이용하여 모든 경우의 수를 탐색하여 풀면 된다.

[코드]

class Solution:
    """
    @param: : a string to be split
    @return: all possible split string array
    """
 
    def func(self, s, start, end, row, matrix):
        # 기저사례: end가 문자열 s의 길이면 matrix 배열에 추가 후 종료
        if(end == len(s)):
            matrix.append([])
            for i in range(len(row)):
                matrix[len(matrix)-1].append(row[i])
            return
 
        # end+1이 len(s) 보다 작다면 : case one character
        if end+1 <= len(s):
            # s[end:end+1]를 row에 추가후 재귀호출
            row.append(s[end:end+1])
            self.func(s, end, end+1, row, matrix)
            # s[end:end+1]에 값을 row 배열에서 pop
            row.pop(len(row)-1)
 
        # end+2이 len(s) 보다 작다면 : case two character
        if end+2 <= len(s):
            # s[end:end+2]를 row에 추가후 재귀호출
            row.append(s[end:end+2])
            self.func(s, end, end+2, row, matrix)
 
    def splitString(self, s):
        # write your code here
 
        matrix = []
 
        row = []
 
        # s의 문자열의 길이가 1개이상일 경우 s[0]번째를 row에 넣고 재귀함수 호출 시작
        if len(s) >= 1:
            row.append(s[0:1])
            self.func(s, 0, 1, row, matrix)
 
        # s의 문자열의 길이가 2개이상일 경우 s[0]과 s[1]이 합쳐진 문자열을 row에 넣고 재귀함수 호출 시작
        if len(s) >= 2:
            row = []
            row.append(s[0:2])
            self.func(s, 0, 2, row, matrix)
 
        return matrix
 


'Language > Python' 카테고리의 다른 글

리스트  (0) 2017.11.20

list 클래스

파이썬에서 연속적인 데이터를 저장하기 위해 사용하는 클래스이다.

클래스 생성 방법으로는 아래와 같다.

  1. list1 = list()

  2. list1 = []

  3. list2 = list([2,3,4,])

  4. list2 = [2,3,4]와 동일

  5. list3 = list(["red", "green", "blue"])

  6. list3 = ["red", "green", "blue"]

  7. list4 = list(range(3, 6))

  8. list5 = list("abcd")

list 슬라이싱 [start: end: step]

list 슬라이싱을 이용하여 문자열 추출을 쉽게 할 수 있다.

list1 = [4,2,9,1,32]
 
list1[1:4] // [2, 9, 1]

연결 연산자, 반복 연산자, in/not in

>>> list1 = [4,2,9,1,32]
 
>>> list2 = [3, 3]
 
>>> list1 + list2 // [4, 2, 9, 1, 32, 3, 3]
 
>>> list2 * 3 // [3, 3, 3, 3, 3, 3]
 
>>> 4 in list2 // False
 
>>> 3 not in list2 // False
 
>>> 1 in list1 // True

리스트 복사

파이썬에서 아래와 방식으로 리스트를 복사하게 되면 참조가 된다.

list1 = [1,2]
 
list2 = list1
 
list1[0] = 3
 
//list1과 list2 모두 [0] 번재 원소가 3으로 변경된다.
print(list1, list2)

따라서 리스트를 복사할 때는 아래의 2가지 방법을 이용할 수 있다.

익명 배열에다가 원소를 넣어서 대입하므로 참조가 일어나지 않는다.

list2 = [x for x in list1]
 
list2 = [] + list1

아래의 예시를 보면서 참조에 대한 이해를 해보자

def main():
    x = 1 # x는 int 변수이다.
    y = [1, 2, 3] # y는 리스트이다.
 
    m(x, y) # x, y 인자를 사용하여 m을 호출한다.
 
    print("x is", x) // 1
    print("y[0] is", y[0]) // 5555
 
def m(number, numbers):
    number = 1001
 
    numbers[0] = 5555
 
main()

number와 같은 일반 변수는 참조가 일어나지 않지만 리스트는 참조가 일어남을 확인할 수 있다.

'Language > Python' 카테고리의 다른 글

[Lint Code] Split String  (0) 2017.12.24

Namespacing Pattern

객체를 생성하는 패턴들로 Name space를 사용하게 되면 필요로 하는 전역 변수의 개수를 줄일 수 있습니다.

객체 리터럴 네임스페이싱(Object Literal Namespacing)

가장 기본적인 네임스페이스 패턴으로 하나의 전역 객체를 선언 후 함수, 객체, 변수를 여기에 추가하는 방식입니다.

단, namespace를 사용하고 있는 객체는 this를 사용해서 참조하면 안된다.

var APP = {};
 
APP.function1 = function() {
  console.log('function1');
}
APP.function2 = function() {
  console.log('function2');
}
 
// 변수를 추가하는 경우
APP.variable = 1;
 
// 객체 컨테이너를 추가하는 경우
APP.modules = {};
APP.modules.module1 = {};
APP.modules.module1.data = 1;

범용 네임스페이스 함수

이 방식은 재정의를 막기 위해 미리 선언되었는지 확인하고 정의해주는 함수를 말합니다.

APP.namespace = function(string) {
  // 단일 var 패턴으로 변수들 선언
  // string 문자열을 '.' 를 기준으로 분리
  var parts = ns_string.split('.'),
  parent = APP,
  i;
 
  if(parts[0] === "APP") {
    //parts 배열에서 parts[0] 원소를 삭제
    parts = parts.slice(1);
  }
  for(i=0 ; i<parts.length ; i++) {
    // 중복되지 않으면 생성
    if(typeof parent[parts[i]] == "undefined") {
      parent[parts[i]] = {};
    }
    parent = parent[parts[i]];
  }
  return parent;
}


'Language > Javascript' 카테고리의 다른 글

다중 플래그값을 위한 비트 연산  (0) 2017.12.30
promise - 2 [Promise 사용 시 주의할 것]  (0) 2017.07.28
promise - 1 [then, catch, resolve, reject]  (0) 2017.07.28
closure  (0) 2017.07.28
scope - 3 [변수 숨기기]  (0) 2017.07.28

Promise의 올바른 사용

Promise를 배우고 좀 사용하다 보면 Promise가 너무 무분별하게 사용되는 건 아닌지, 잘 사용하고 있는지 고민될 때가 있습니다.

composing promise

비동기 프로그래밍 시 callback hell 의 문제를 해결 하기 위해 promise를 사용하는데 promise hell? 처럼 promise를 사용하면서도 pyramid 형식으로 작성하는 것을 피해야 합니다.

// A bad example
promise1()
.then(function(result) {
  promise2()
  .then(function(result) {
    promise3()
    .then(function(result) {
      /* */
    })
    .catch(function(error){
      console.log(error);
    })
  })
});

위의 방식을 아래 방식처럼 표현한 것을 composing promise라 부릅니다.

// A good example
promise1()
.then(function(result) {
  return promise2();
})
.then(function(result) {
  return promise3();
})
.catch(function(error) {
  console.log(error);
});

catch를 항상 사용하자

에러가 발생하지 않는다고 확신해도 catch를 붙이는 습관을 갖도록 합니다.

promise1()
.then(function(result) {
  /* */
})
.catch(function(error) {
  console.log(error);
});

return 을 이용하자

then() 메서드 안에 return 과 throw를 이용합니다. return은 비동기적으로 발생된 값 뿐만 아니라 동기적으로 발생된 값도 전달할 수 있습니다.

promise1().then(function() {
  // return value or undefined
  if(동기적으로 발생한 값을 리턴할 경우)
    return value;
 
  // throw error
  if(에러일 시)
    throw new Error("error");
 
  // return another promise1
    return promise2();
 
});

아래 예제는 return 을 사용하지 않았을 때 p2와 p3 가 동기적으로 실행되지 못한 것을 확인할 수 있습니다. return을 사용하지 않아 p2와 p3가 비동기적으로 실행이 되어 part 2 부분에서 flag 값이 undefined가 출력되었습니다.

var p1 = function() {
  return new Promise(function(resolve, reject) {
    console.log(1);
    resolve(1);
  });
};
 
var p2 = function() {
  return new Promise(function(resolve, reject) {
    console.log(2);
    resolve(2);
  });
};
 
var p3 = function() {
  return new Promise(function(resolve, reject) {
    console.log(3);
    resolve(3);
  });
};
 
var flag = 1;
 
p1()
.then(function(data) {
  // part 1
  flag = data;
  console.log("flag : " + flag);  // flag : 1
  p2();
})
.then(function(data) {
  // part 2
  flag = data;
  console.log("flag : " + flag);  // flag : undefined
  p3();
});

then 은 함수 형태로 전달되어야 한다.

then은 함수 형태로 전달해야 한다는 기억해야 합니다. 아래의 예제에서 result 값으로 bar 가 출력됨을 예상하겠지만 foo 가 출력됩니다. 그 이유는 then() 함수에 함수의 형태가 아닌 것을 전달하기 때문입니다.

Promise.resolve('foo')
.then(Promise.resolve('bar'))
.then(function(result) {
    console.log(result);  // foo
})

아래의 형태로 함수를 then() 메서드로 함수 형태로 promise 를 리턴해야 합니다.

Promise.resolve('foo')
.then(function(result) {
  return Promise.resolve('bar');
})
.then(function(result) {
  console.log(result);
})


'Language > Javascript' 카테고리의 다른 글

다중 플래그값을 위한 비트 연산  (0) 2017.12.30
Name Spacing Pattern  (0) 2017.07.28
promise - 1 [then, catch, resolve, reject]  (0) 2017.07.28
closure  (0) 2017.07.28
scope - 3 [변수 숨기기]  (0) 2017.07.28

promise란?

자바스크립트의 callback Hell 문제를 해결하기 위해 사용합니다. 하지만 그 외에도 비동기 프로그래밍을 하면서 사용하기 어렵게 된 throw와 return을 사용할 수 있게 해줍니다.

Promise.prototype.then()

then() 메서드를 사용하게 되면 Promise를 리턴하게 되고 성공했을 때 실행되는 콜백함수 resolve, 실패했을 때 실행되는 콜백 함수 reject 를 인수로 받습니다.

var promise1 = new Promise(function(resolve, reject) {
  resolve("success!");
  // 또는
  // reject ("error!");
});
 
promise1.then(function(data) {
  console.log(data); // 성공!
}, function(error) {
  console.log(reason); // 오류!
});

위의 개념을 이용하여 chaining 개념을 잡을 수 있습니다.

var promise2 = new Promise(function(resolve, reject) {
  resolve(1);
});
 
promise2.then(function(data) {
  console.log(data); // 1
  return data + 1;
}).then(function(data) {
  console.log(data); // 2
});
 
promise2.then(function(data) {
  console.log(data); // 1
});

Promise.resolve(data)

Promise.prototype.then()에서 인자로 받았던 resolve, reject 중 Promise.resolve 함수를 사용하게 되면 결정된 Promise를 반환합니다.(즉, reject 부분은 무시하게 됩니다.)

Promise.resolve("sucess").then(function(data) {
  console.log(data); // "Success"
}, function(error) {
  // 호출되지 않음
});

Promise.reject(error)

Promise.resolve 와 마찬가지로 거부된 Promise를 반환하게 하여 resolve 부분을 무시하게 합니다.

Promise.reject("test promise reject").then(function(reason) {
  // 호출되지 않음
}, function(reason) {
  console.log(reason); // "test promise reject"
});

Promise.prototype.catch()

catch() 는 거부된 Promise를 반환받게 되었을 때 실행됩니다.

var promise1 = new Promise(function(resolve, reject) {
  resolve('success');
});
 
promise1.then(function(data) {
  console.log(data); // "success!"
  return Promise.reject('reject');
}).catch(function(e) {
  console.log(e); // "reject"
});

promise는 비동기 콜백 내에서 발생한 오류는 잡을 수 없습니다. 즉, 직접적으로 Promise 스코프에 있는 Error 처리만 잡을 수 있습니다.

var promise1 = new Promise(function(resolve, reject) {
  throw 'reject error in promise1';
});
 
promise1.catch(function(e) {
  console.log(e); // "reject error in promise1"
});
 
var promise2 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    throw 'reject error in async callback function';
  }, 1000);
});
 
p2.catch(function(e) {
  console.log(e); // 이는 전혀 호출되지 않음
});

Promise.all(iterable)

인자로 Promise 값들로 이루어진 배열을 받아 resolve 된 Promise 값으로 이루어진 배열을 반환합니다.

var promise1 = Promise.resolve(1);
var promise2 = 2; // Promise가 아닌 값은 자동으로 Promise.resolve 값으로 계산
 
var promise3 = new Promise(function(resolve, reject) {
  resolve(3);
});
 
Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values); // [1, 2, 3]
});

만약 전달 된 배열 중에서 Promise 값들 중 하나라도 reject 되면 Promise.all은 reject 된 Promise를 반환하게 됩니다.

var promise1 = Promise.resolve(1);
var promise2 = 2;
var promise3 = new Promise(function(resolve, reject) {
  reject("reject");
});
 
Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values); // [1, 2, 3]
})
.catch(function(error) {
  console.log(error);
});


'Language > Javascript' 카테고리의 다른 글

Name Spacing Pattern  (0) 2017.07.28
promise - 2 [Promise 사용 시 주의할 것]  (0) 2017.07.28
closure  (0) 2017.07.28
scope - 3 [변수 숨기기]  (0) 2017.07.28
Scope - 2 [정적, 동적, 블록, 중첩 스코프]  (0) 2017.07.21

Closure

클로저란 자바스크립트에 있어서 C언어의 포인터와 같은 관점으로 볼 수 있습니다. 먼저 scope chain에 대해 알아야 합니다.

scope chain

scope는 비동기 함수가 호출될 때까지 계속해서 지속되어 참고가 됩니다.(scope의 지속성) 새로운 scope를 생성함으로써 비동기적으로 호출 될 때의 scope를 조율할 수 있습니다.

아래의 예제에서는 outer() 함수 호출 후 inner() 내에 변수 a 에 x 를 대입할 때 실행문을 포함하고 있는 함수의 변수 스코프부터 먼저 검색됩니다. 만약 없는 경우 상위 스코프로 검색을 시작하게 되며 전역 스코프에도 없는 경우 에러를 발생시킵니다.

var x = 1;
function outer() {
  var y = 2;
  function inner() {
    var a = x;
  }
}
 
outer();

Clusure

클로저란 함수가 함수 내부에 선언된 변수에 접근할 수 있는 함수를 말합니다.

클로저는 독립적인 (자유)변수를 가리키는 함수이며 클로저 안에 정의된 함수는 만들어진 환경을 '기억'합니다.

아래의 예제를 통해 알아보겠습니다.

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}
 
var myFunc = makeFunc();
myFunc();

위의 예제에서 name 변수는 makeFunc라는 함수 스코프에 존재합니다. 즉 함수 외부에서는 접근을 할 수 없습니다. 하지만 displayName라는 함수 내부의 함수를 name가 name 변수를 참조하게 하여 함수 외부에서도 접근할 수 있도록 return 을 하여 myFunc() 함수에서도 name을 접근할 수 있습니다.

생성 시 환경을 기억하는 클로저

클로저는 만들어질 때의 환경을 기억합니다. 다음 예제를 살펴보도록 하겠습니다.

var i;
for (i = 0; i < 10; i++) {
  setTimeout(function() {
    console.log(i);
  }, 100);
}

위 예제는 0~9까지의 출력을 예상할 수 있지만 setTimeout 함수에 의해 0.1초를 기다리는 동안 i가 10번을 돌게 되어 익명 함수 호출시에는 i가 이미 10이 되어 10을 10번 출력하게 됩니다.

이 상황을 클로저를 이용하여 생성 당시의 환경(i의 값)을 기억하도록 하여 setTimeout 함수에 의해 0.1초 후 익명함수가 호출되더라도 기억하고 있는 i의 값을 출력합니다.

var i;
for (i = 0; i < 10; i++) {
  (function(j) {
    setTimeout(function() {
      console.log(j);
    }, 100);
  })(i);
}

객체와 클로저를 이용한 데이터 은닉

일반적으로 객체를 정의할 때 prototype을 이용하지만 이 방법을 사용하게 되면 외부로부터 데이터를 보호할 수 없게 됩니다.

function Hello(name) {
  this._name = name;
}
 
Hello.prototype.say = function() {
  console.log('Hello, ' + this._name);
}
 
var hello = new Hello("person");
 
hello._name = "person2";

이 경우 클로저를 이용하여 외부로부터 데이터를 보호할 수 있습니다.

function Hello(name) {
  var _name = name;
  return function() {
    console.log('Hello, ' + _name);
  };
}
 
var hello = Hello("person");
 
hello();

Object VS Closure

먼저 객체 생성 시 객체의 프로토타입을 연결하는 방식이 더 좋다. 그 이유는 Object를 사용하게되면 생성자가 호출 될 때마다 메서드가 다시 할당된다.

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };
 
  this.getMessage = function() {
    return this.message;
  };
}

클로저를 이용하게 되면 이 부분을 보완할 수 있으며 prototype에 다시 정의하는 방법보다 기존 프로토타입에 정의를 하는 것이 좋습니다.

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
 
/*
MyObject.prototype = {
  getName: function() {
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};
*/
 
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};

위 코드에서 즉시 함수 실행 기법 을 사용하면 더 좋은 성능을 얻을 수 있습니다. (함수 표현은 함수 정의 및 저장 후 실행하는 과정을 거치지만 즉시 실행함수를 사용하게 되면 함수 정의 후 바로 실행하여 위의 과정을 거치지 않습니다.)

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
 
(function() {
    this.getName = function() {
        return this.name;
    };
    this.getMessage = function() {
        return this.message;
    };
}).call(MyObject.prototype);

클로저 사용 후 정리하기

c 와 c++ 에서 동적할당 후 마지막에 동적해제하는 과정을 거칩니다. 그 이유는 메모리를 회수하기 위함입니다. 자바스크립트에서는 스코프 체인 성질에 의해 어떠한 변수 혹은 함수가 참조를 하고 있으면 내부 변수가 차지하는 메모리를 GC(Garbage Collector)가 회수하지 않습니다. 따라서, 클로저 사용 후 참조를 제거하는 것이 좋습니다.

hello = null;


변수를 숨기는 방법들

var를 이용하는 방법

아래의 예제에서 var를 이용하여 b를 외부로부터 숨겼습니다. var를 사용하지 않고 선언할 경우 전역으로 선언되기 때문입니다.

function f1(a) {
  function f2(a) {
    return a - 1;
  }
 
  var b;
  b = a + f2(a);
  console.log(b);
}
 
f1(2);  // 3

같은 이름을 선언하는 방법

아래의 예제는 같은 이름을 선언하여 외부의 x에 접근하지 못하도록 하였습니다.스코프 체인 성질 을 이용하여 정의되고 있는 함수의 변수 스코프에서부터 먼저 검색되기 때문에 외부의 x에 접근하기 전에 내부 블록에 x를 선언 후 정의함으로써 외부 블록의 x에 접근을 못하게 합니다.

{
  let x = 'blue';
  console.log(x); // blue
  {
    let x = 'red';
    console.log(x); // red
  }
  console.log(x);  // blue
}

모듈 패턴 사용

아래의 예제는 모듈화를 하여 변수에 접근가능한 함수만을 제공하여 직접적으로 변수에는 접근하지 못하게 하였습니다.

function examModule() {
    var something = "cool";
    var another = [1,2,3];
 
    function doingSome() {
        console.log(something);
    }
 
    function doingAnother() {
        console.log(another);
    }
 
    return {
        doSomething: doSomething,
        doAnother: doAnother
    };
}


+ Recent posts