2018년 9월 15일 토요일

카카오톡 블라인드 테스트..

를 쳐봤는데, 당연하게도 알고리즘에 a도 안파던 놈답게 2번까지가 한계였습니다..

const testInput =
["Enter uid1234 Muzi", "Enter uid4567 Prodo", "Leave uid1234", "Enter uid1234 Prodo", "Change uid4567 Ryan"]
console.log(solution(testInput));



function solution(record) {
if(typeof(record)!="object" || !record.length || record.length > 100000){
throw new Error('[ERROR] givin parameter record type is not array!');
}
const tempLogArr = stringDivider(record);
const userIdMap = storeUniqueUserInfo(tempLogArr);
let result = [];
convertLog(userIdMap,tempLogArr,(err,res)=>{
if(err){
throw new Error(err);
}else{
result = res;
}
});
return result;
}

function stringDivider(raw){ //주어진 raw data 를 작업이 편하게 나눕니다.
let arrays = new Array();
try{
for(var i in raw){
arrays.push(raw[i].split(' ',3));
}
return arrays;
}catch(e){
throw new Error(`[ERROR][stringDivider func]${e}`);
}
}

function storeUniqueUserInfo(raw){ //현제 주어진 raw data 에 고유한 userId를 map에 K:userId , V:nick 으로 끝단부터 읽습니다.
//거꾸로 읽으며, 중복은 무시하기 떄문에, Chnage 를 해도 문제가 없습니다. 현제 맵에는 최신 nick를 저장하기 때문 입니다.
var uniqueUsers = new Map();
for(var i = raw.length-1 ; i >= 0 ; i-- ){
if(!uniqueUsers.get(raw[i][1])){
uniqueUsers.set(raw[i][1],raw[i][2]);
}
}
return uniqueUsers;
}

function convertLog (userIdMap,tempLogArr,cb){ //최종적으로 주어진 데이터들을 출력물에 맞게 만드는 함수입니다.
//input parameter's data type are like
//tempLogArr = [ ['status','userId','userNick'],['status','userId','userNick'] ] ;
//userIdMap = Map K : userId, V : userNick

var totalLog = new Array();
try{
for(var i in tempLogArr){
const curStatus = tempLogArr[i][0];
if(curStatus !=='Change'){
const curLog = `${userIdMap.get(tempLogArr[i][1])}님이 ${curStatus ==='Enter' ? '들어왔습니다.' : '나갔습니다.'}`;
totalLog.push(curLog);
}
}
cb(null,totalLog);
}catch(e){
cb(new Error(`[ERROR][convertLog func]${e}`));
}
}

testInput의 로그창이 들어오면, 클라이언트가 읽어보기 쉽도록 문자열을 변형하여 출력하는 문제였습니다..
 무식하게 for문만 써서 한시간이나 걸렸네요 -_-;;

var N = 4
var stages = [4, 4, 4, 4, 4];
var expect = [4, 1, 2, 3]
console.log(solution(N,stages));

function solution(N, stages) {
if(typeof(N)!='number' || N < 1 || N >500){
throw new Error(`[ERROR]GIVIN PARAMETER N IS OUT OF RANGE`);
}
if(typeof(stages)!=='object' || stages.length < 1 || stages.length > 200000 ){
throw new Error(`[ERROR]GIVIN PARAMETER stages IS OUT OF RANGE`);
}
const sortedStages = orderUserStages(stages);
let stageFailArr = new Array();
for(var curStage = 1 ; curStage < N+1 ; curStage++){
const curStageUsersLen = findusersArr(sortedStages,curStage);
stageFailArr.push([curStage,calculate(curStageUsersLen)]);
}
var answerMap = [];
answerMap = divideNConqure(stageFailArr);
return extrectStageNumber(answerMap);
}

function orderUserStages(rawstages){
var sortedArr;
sortedArr = rawstages.sort(function(a,b){
return b - a;
});
return sortedArr;
}//userStage 를 정렬합니다. 오름차순 입니다.

function calculate(sortedStagesLen){
try{
return sortedStagesLen[1] / sortedStagesLen[0];
}catch(e){
return 0;
}
}//계산부 입니다.

function findusersArr(sortedStages,curStage){
for(var i in sortedStages){
if(sortedStages[sortedStages.length-1] === curStage){
return countUserArr(sortedStages,curStage);
}else if(sortedStages[i] < curStage){
sortedStages=sortedStages.slice(0,i);
return countUserArr(sortedStages,curStage);
}
}
}//정렬된 userStage 배열을 전체적으로 스켄하여, 선택한 스테이지의 유저들을 모두 불러옵니다.

function countUserArr(sortedStages,curStage){
var nCount = 0 ;
for(var i in sortedStages){
if(sortedStages[i] ===curStage)
nCount += 1;
}
return [sortedStages.length,nCount];
}// 정렬된 userstage 를 [깬사람, 풀고있는사람] 으로 리턴합니다.

function divideNConqure(arr){
if(arr.length > 3){
const half = Math.floor(arr.length / 2);
const left = divideNConqure(arr.slice(0,half));
const right = divideNConqure(arr.slice(half,arr.length));
var temparr = new Array();
var leftPiv = 0, rightPiv = 0;
while(temparr.length != arr.length){
try{
if(left[leftPiv][1] >= right[rightPiv][1]){
temparr.push(left[leftPiv]);
leftPiv += 1;
}else if(left[leftPiv][1] < right[rightPiv][1]){
temparr.push(right[rightPiv]);
rightPiv += 1;
}
}catch(e){
if(left[leftPiv] ==undefined){
temparr.push(right[rightPiv]);
rightPiv += 1;
}else{
temparr.push(left[leftPiv]);
leftPiv += 1;
}
}
}
return temparr;
}else{
for(var i = 0 ; i < arr.length ; i ++){
for(var j = i+1 ; j < arr.length ; j++){
if(arr[i][1] < arr[j][1]){
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
}// array[ array[ stageNum, stageUsers ],] 를 파라메터로 받습니다. 분할정복법으로 퀵소트 합니다. 오름차순 정렬입니다.

function extrectStageNumber(arr){
var result = [];
for(var i in arr){
result.push(arr[i][0]);
}
return result;
} //최종적으로 stage 번호를 출력합니다.

1부터 N번째 스테이지까지의 클리어 실패율을 도출 해 내는 문제였습니다.
stage 배열은, 각 유저별로 몇번째 스테이지에 있는지 정보입니다.
이 두 값을 통해, 실패율이 가장 높은 스테이지부터 차례로 배열에 넣어 출력하는 문제입니다.
고수님들 답지를 보니 20라인만에 다 짜버리는데 100라인짜리 내 코드를 보니 자괴감 들고 괴로워..

(숙련된 개발자의 코드. 효율성 테스트도 다 통과했다고 합니다)

이 코드를 분석해서 내껄로 만들어 응용하는 단계정도는 되야, 어느정도 js를 하는 개발자가 되겠거니 하며 기록을 남겨둡니다..

2018년 9월 12일 수요일

c++ namespace


처음 c++를 배울때에 우리는 보통 <iostream> 을 임포트 하여 시작했습니다.
iostream 에서는 printf 가 없기 때문에, 우리는 std::cout 을 사용하는데, 왜 이래야 하는가 에 대해 정확히 살펴 본 적은 없습니다. 그저 두루뭉실하게
java 따위에서 타 클래스를 명시하여 함수를 이용하는것이다 정도 였는데요,

마침 수업중에 namespace 에 대해서 정리해 오라는 과제를 던지는것을 봤습니다.
나도 궁금한 사항이니 이에 대해 알아보고, 정리해 보았습니다.

개발환경에 학습 이상으로 넘어가게 되면, 각 개발자마다 맡아 작업하는 cpp 파일들이 따로 있게 될 것 입니다.

따로 작업한 cpp 파일들을 합쳐, 작동시키는데에는 문제가 없을것 입니다.

하지만 변수 혹은 함수의 이름이 동일하게 되면 어떨까요?

class a
 int a = 0 ;

class b
 int a = 0;


메인 :  "a ?"

이제 골치가 아파지기 시작합니다. 하지만 놀랍게도 실무에서는 변수나 함수 이름을 고의로 통일시키는 경우도 있습니다. 전역적으로 두 소스파일에서 같은 변/함수를 사용할때 같이 말이죠.

이런 경우에는 선언할 변수를 가진 소스파일은 그대로 두나, 타 소스파일의 변수를 같이 사용하고지 하는 소스의 경우는 데이터 타입 앞에 extern 을 붙히게 됩니다.
calss a
 int a = 0
class b
 extern int a

메인 : "a 는 class a 에서 만든 변수이며, class b는 class a의 변수를 참조한다!"

이렇게 사용이 가능하다는 말이죠.
이떄 extern을 명시한 변수는 초기화를 선언과 동시에 해서는 안됩니다. 이럴경우 extern이 아닌 class b 에서 생성된 변수 a로 바뀌어 버립니다.

그럼 의도적으로 동일한 이름의 변/함수가 아닌 경우에는 어떻게 할까요?
가장 쉬운 방법은 이름을 바꾸는것 입니다. 하지만 이건 너무 무식하지요, 조금더 쓸모있게 해 봅니다.
class a
 static int a = 0
class b
 static int a = 10
메인 : "class a 에서만 존재한 변수 a 와, class b 에서만 존재하는 변수 a 두개가 있다!"

근본적인 문제는 해결했으나, 만일 class a 나 class b 가 a 변수를 서로 다른 class에게 공유를 할 계획이라거나, 더 많은 소스파일들로 인해, extern도 사용하기가 힘들어 진다면?
static은 해당 class 내에서만 작동하기 때문에, 지금 코드로는 문제가 있습니다.

이를해결하기 위해 나온것이...
namespace 가 등장했습니다!

namespace 는 변수나 함수, 심지어 class에 사용이 가능합니다.
namespace first {
 int a = 0;
}
namespace second {
 int a = 1;
}
namespace third {
 int a = 2;
}

main(){
cout << first::a << second::a << third::a << endl;
//expect result is 0 1 2
}

c+ 을 해보신 분이라면 어느정도 파악은 하고 계시겠지만, namespace를 명시하지 않아도, 묵시적으로 namespace가 선언되어 있습니다.
그럼 어디로? 전역 네임스페이스 공간으로 되어있습니다. 우리가 보통 명시하지 않은 변수 함수 선언은 전역 네임스페이스 공간으로 선언이 되기 때문에,

좋아요, 그럼 변수와 함수를 여러개 사용하는 예제를 보시죠

namesapce myNamespace{
 void func(){}
 int a = 0 ;
}
using myNameapce ::func
main(){
 cout << myNamespace::a << endl
 // myNamespace는 using이 아니기 때문에 명시가 필요
 func()
// using으로 myNamespace 내부의 func를 스코프 했기 때문에, myNamespace :: func()
}

이즘되면 클래스랑 동일하다는 생각을 가질수 도 있는데, 그렇지는 않습니다.
클래스와 네임스페이스는 다르다는것을 명심해야 합니다.


( 그낭 글씨치기엔 귀찮아서 코드 사진으로 대체)
위코드는 myNamespace 를 변수로 선언해서 사용해 부며,
myNamespace 안의 class를 사용하는 예제입니다.
위에서 언급됬다싶이, namespace 와 class는 비슷해 보이지만 다르기 떄문에, 혼동하지 않도록 해야할 것 입니다!

2018년 9월 4일 화요일

기록보관용 jellojay에서 ajax 기본 틀

최근 작업중 ajax작업을 다시 만지게 되었는데, 예전 ajax작업을 비교하니 너무나 빈약하고 outdate 기록도 많은상태!

하나의 틀을 정해서 모든 js 페이지를 통일할 계획..

하지만 지금은 바쁘니깐..계획만 해놓고 기본틀을 여기다가 적어놓습니다.

$.ajax({
type : "GET",
url : "/?",
data : {
"foo" : "bar"
},
dataType : "JSON",
success : (data)=>{

},
statusCode : {
400 : {},
401 : {},
500 : {},

},
error : ()=>{

}
});

2018년 9월 3일 월요일

Difference between arrow func and func declare, plus three-dot-operator


지금까지 js를 공부하면서 arrow function에 대해 파편적으로 배웠습니다.
애초에 js를 따로 공부한적도 없기 때문에, 아는것만 아는 수준입니다.
 
Arrow function은 비교적 최근(ES2015)에 나온 방법인데, 문법은 이러합니다. ()=>{}

그런데 최근에 들어, arrow function과 일반 function 선언 두가지 방식에 대한 차이점이 궁금해 지기 시작합니다.

이리하여 arrow function 과 function declare에 대해 알아보았습니다

Function expression(함수 표현식)
우선 들어가기에 앞서, function expression 에 대해 알아보도록 합니다.

const func = function(foo){ return foo; }

이것이 바로 function expression(함수 표현식) 입니다.

Function declaration
일반적인 function선언은 아래 코드와 같습니다.

function func(foo){ return foo; }

이 둘의 차이는 끌어올려지는가(is hoisting) 입니다.

Hoisting?
hoisting은, 해당 function을 선언을 둘러싼 함수의 최상부, 혹은 전역범위(global scope)로 끌어올려진다 입니다.

console.log(func(1));  // return 1
function func(foo){return foo;} // this function is hoisted.

그리고, 위 코드에서와 같이, 비 순차적으로(선언을 사용한 뒤에 되어있는)코드가 있을 때, 해당 함수를 작동하면, hoisting이 됩니다. 이러한 Hoisting이 된 코드의 경우, 선언된 위치에 구애받지 않고, 작동을 보장합니다.

Console.log(func(1));  // return undefind.
Const func = (foo)=>{return foo;} // this function is not hoisted.
하지만 function declare 로 선언이 된 함수는, hoisting이 되지 않습니다.

이제 Arrow function을 보도록 합니다. Arrow functionthis,args,super 혹은 new.targer을 바인딩하지 않는다 라고 합니다.
그 말은 arrowanonymouse function 입니다.

함수안에 또 다른 함수(recursion)를 쓰기 위해서는 이전버전(ES2015)에는 arguments.callee 를 이용했었다고 하나, 여러가지 문제로 인해 named function을 사용한다 합니다.
(그래도 지난 게시물을 해결할 열쇠로 보이진 않네요)


Arrow function
arrow function의 사용은 this 의 범위가 바인딩 되지 않는 것이 큰 차이점으로 보입니다.
const test = {
 a : 10,
 b : ()=>{ console.log(this.A);}
 c : function(){ console.log(this.A);}
}
test.b()// return undefined
test.c()// return 10

의 간단한 예제로 바인딩의 범위를 볼 수 있습니다.

Declaring Arrow function
Arrow function은 두가지의 선언방법이 있으며,

1. concise 바디 (중괄호가 없는)
const foo = bar => bar * 2; //return은 생략 가능하며, bar*2가 들어있다.

2. Block 바디(중괄호가 있는)
const foo = (bar) => { return bar * 2 }//return을 생략할 수 없다

가 있습니다.

이 이외에도 상당히 다양한 차이가 존재하나, 가장 눈여겨 볼 것은 bind의 차이로 불 수 있어 보입니다. 무엇이 더 좋은가를 굳이 따지기 보다, 언제 쓰는게 올바른가를 알아야 할거 같습니다.
(물론 짧은게 최고 라고 합니다만)




ps
추가적으로 알아보다가 얻었던 궁금증인  :
Three-dot-operator (…) 
arguments에서 길이가 가변적인 array로 볼수 있습니다.

function func(args1, args2, ...args3) { console.log(args3)} ;
func(1, 2, 3, 4, 5, 6, 7); // returning (array)[3,4,5,6,7]

를 리턴한다 합니다.

2018년 8월 29일 수요일

while study..call stack overflow on js

알고리즘 부분은 수업정도만 들었지, 다른 컴공친구들 처럼 알고리즘 공부를 집중적으로 하지는 않았기 때문에, 자신이 없었습니다.
특히 몇군대 알고리즘 예선전 문제의 난이도를 보고 더 자신감이 떨어지기도 했습니다;

간만에 다른 알고리즘 예제 문제를 하나 접하게 되었는데, 속으로 그다지 어렵지 않아보이는데? 와 js도 지원하네? 로 인해 한문제를 풀어보게 되었습니다.

문제자체는간단했습니다.

integer로 이루어진 array에서 중복되는 숫자가 있는지를 검출하는 내용이었습니다.

아무생각없이 이터레이터 방식으로 처음에 해결을 하였으나..


(효율성 테스트에서 형편없다는 문제점을 받게되었습니다.)

이제 슬슬 오기가 붙어서, 재귀적으로..


(삽질을 시작했으나...정확성 테스트에서 한가지가 계속해서 런타임 fail로 실패를 당합니다..)

지금생각하면 당연한 문제인데도 불구하고, 다른 오류(int가 아니다거나) 검출을 실수했나 싶어 한참을 찾았습니다만..크흣..

 마침내, 많은 테스트 케이스가 문제가 아닐까 라는 추측에 마침내 도달했고, 가이드라인에 따라 10만개의 array를 만들어 테스트를 돌려봤습니다..


 (예상은 했지만 막상 닥쳐보니 눈앞이 깜깜..)

분할정복법을 이용해 만든 재귀함수인데, 10만 이라하면 함수호출이.. 두번째 식에 따라선 으음..nlogn이니깐.. 최소 5만?번은 더 호출이 되야했기 때문에.. 이러한 문제가 발생한다는것을 꺠달습니다 -_-


(미친듯이 함수 호출이 찍혔지만, 재귀로 인해 끝나지 않기에, 가비지 컬렉트가 안되어 지옥이 발생!)


여기서 고민과 검색을 좀 했는데, 지금 당장 제 수준에서는 어떻게 이 재귀함수를 오버플로 없이 해결 할지 감이 오지 않습니다..

힙 메모리가 일정 수준이고, 비교할 array가 기대치보다 높다면 그냥 이터레이션으로 할까? 하는 좀 멍청한 생각도 났기 때문에, 검색을 좀 해봤는데 :
함수를 몇개 호출하는지 따위의 제한을 둘 수가 없는게, 해당 함수가 얼마만큼의 메모리를 할당하느냐, 해당 클라이언트에 얼마만큼의 메모리를 힙으로 쓰느냐를 비교해야 하기때문에, 이것도 영...아니다 라는 생각에 포기했습니다.

그래서 궁여지책 끝에, 분할정복을 하는 기준을 높여, 함수 호출의 수를 줄여보는게 어떨까에 도달..

이전의 경우에는, 검사할 array의 index가 3 이상이면 분할정복을 했었기 때문에,
이 값을 5로 올려보았습니다.

if(array.length >= 5 )
  devideNconquer(left)
  devideNconquer(right)

당연하게도 이렇게 함으로써 함수 호출은 극적으로 줄어들게 되었고 -_-
정확성 테스트와 효율성 테스트에서 이전보다 나은 점수를 얻게 됩니다...

하지만 아직도 대부분의 효율성 테스트에서는 구동조차 실패했기 떄문에, 어떻게든 손은 더 봐야 할거 같습니다..

간만에 다시 컴공으로 알고리즘 공부만 파는애들에게 경외심이 드는 하루 -_-...
저런애들을 어떻게 이겨...