JavaScript 응용

김민지·2024년 9월 23일
0

도입

명령형 프로그래밍: 홀수 n개 더하기

  • 상수 limit과 리스트 list를 인자로 전달받아서 list의 수 중 홀수를 제곱한 후 limit개만 더하는 함수 작성
function f1(limit, list) {
  let summed = 0; // 더한 값을 저장할 변수

  // 리스트의 각 요소를 순회 (for-of 문을 사용)
  for (const a of list) {
    if (a % 2) {  // a가 홀수인지 확인 (a % 2가 참이면 홀수)
      const b = a * a;
      summed += b;  // 홀수이면 제곱해서 summed에 더함

      // limit를 1 감소시키고, 감소된 값이 0이면 반복문 종료
      if (--limit == 0) break;
    }
  }

  console.log(summed);  // 최종 더한 값을 출력
}


f1(3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); // 9
  • --limit : limit를 1씩 감소시키는 표현임
    즉, if (--limit == 0)는 limit 값을 1 감소시킨 후, 그 값이 0이 되면 break를 통해 반복문을 종료시키는 표현

if를 filter로

function f1(limit, list) {
  let summed = 0; 

  for (const a of L.filter(a => a % 2, list)) {
    // if (a % 2) {  
      const b = a * a;
      summed += b; 
      if (--limit == 0) break;
    }
  }

  console.log(summed);  
}

값 변화 후 변수 할당 ▷ map

function f1(limit, list) {
  let summed = 0; 

  for (const a of L.map(a => a * a, L.filter(a => a % 2, list))) {
      // const b = a * a;
      summed += a;  
      if (--limit == 0) break;
    }
  }

  console.log(summed);  
}
  • L.map(a => a * a, 대상리스트): 대상리스트를 순회하며 모든 값을 제곱함

break ▷ take

function f1(limit, list) {
  let summed = 0; 

  for (const a of L.take(limit, L.map(a => a * a, L.filter(a => a % 2, list)))) {
      summed += a;  
      // if (--limit == 0) break;
    }
  }

  console.log(summed);  
}
  • L.take(limit, 대상리스트) : 대상리스트에서 limit개만큼만 꺼내줌

축약 및 합산 ▷ reduce

function f1(limit, list) {
  console.log(
  	_.reduce((summed, a) => summed + a,
             0,
             L.take(limit, 
                    L.map(a => a * a, 
                          L.filter(a => a % 2, list)))));
}

// 또는, (summed, a) => summed + a 이 함수를 꺼내서 더 단순하게 적기
const add = (a, b) => a + b;
function f1(limit, list) {
  console.log( // 5. 출력함
  	_.reduce(add, // 4. add 함수로 더한 것을
             L.take(limit, // 3. limit개만 뽑은 후, 
                    L.map(a => a * a, // 2. 모든 값들을 제곱하고, 
                          L.filter(a => a % 2, list))))); // 1. list에서 홀수만 걸러낸 후, 
}
  • reduce : 어떤 이터러블 (여기에서는 리스트)을 하나의 값으로 축약이나 합산하는 것

  • _.go 함수로 순서까지 뒤집어서 가독성 올리기

const add = (a, b) => a + b;
function f1(limit, list) {
  _.go(
    list, 
    L.filter(a => a % 2),
    L.map(a => a * a),
    L.take(limit),
    _.reduce(add),
    console.log);
}

명령형 프로그래밍

// 1. 0부터 end까지의 수를 출력하기
function f3(end) {
  let i = 0;
  while (i < end) {
    console.log(i);
    ++i;
  }
}
f3(10);
// 2. 0부터 end까지의 홀수를 전부 출력하기
// (i % 2)를 써서 판별할 수도 있지만, 아래와 같은 방식이 더 영리함
function f3(end) {
  let i = 1;
  while (i < end) {
    console.log(i);
    i += 2;
  }
}
f3(10);

while ▷ range

// 1. 0부터 end까지의 수를 출력하기
function f3(end) {
  _.each(console.log, L.range(end));
}
f3(10);
  • each 로 순회, range로 범위 설정
// 2. 0부터 end까지의 홀수를 전부 출력하기
// (i % 2)를 써서 판별할 수도 있지만, 아래와 같은 방식이 더 영리함
function f3(end) {
  _.each(console.log, L.range(1, end, 2));
}
f3(10);
  • _.는 즉시평가되도록 하고자 할 때, L.는 지연적으로 평가되고자 할 때 메소드 앞에 붙여줌

추억의 별 그리기

  • 이중 for문
// ver 1
_.go(
  L.range(1, 6), // 최종 출력에 필요없는 배열들은 L로 설정해서 
  L.map(L.range), // 굳이 불필요한 배열은 생성하지 않음
  L.map(L.map( => '*')),
  L.map(_.reduce((a, b) => `${a}${b}`)),
  _.reduce((a, b) => `${a}\n${b}`),
  console.log);

// ver 1-1: (a, b) => `${a}${b}` 부분 함수로 정의해서 빼기
const join = sep => _.reduce((a, b) => `${a}${sep}${b}`);
_.go(
  L.range(1, 6), 
  L.map(L.range), 
  L.map(L.map( => '*')),
  L.map(join('')),
  join('\n')),
  console.log);

// ver 2
_.go(
  L.range(1, 6), 
  L.map(s => _.go(
    L.range(s),
    L.map( => '*'), 
    _.reduce((a, b) => `${a}${b}`)
  )),
  _.reduce((a, b) => `${a}\n${b}`),
  console.log);

추억의 구구단

const join = sep => _.reduce((a, b) => `${a}${sep}${b}`);

_.go(
  _.range(2, 10),
  _.map(a => _.go(
    _.range(1, 10),
    _.map(b=> `${a}x${b}=${a*b}`),
    join('\n')
  )),
  join('\n\n'),
  console.log);

명령형 습관 지우기

  • reduce 남용하지 않기

1. reduce + 복잡한 함수 + acc 보다 map + 간단한 함수 + reduce

// users 데이터의 나이를 합산하는 코드: 
const users = [
  { name: 'AA', age: 35 },
  { name: 'BB', age: 26 },
  { name: 'CC', age: 28 },
  { name: 'DD', age: 34 },
  { name: 'EE', age: 23 },
];
// reduce에서, input된 두개의 값의 타입을 통일하면 계산이 더 단순해지므로 권장됨
// ver1: 통일 X
console.log(
  _.reduce((total, u) => total + u.age, 0, users));

// ver2: 통일 O
console.log(
  _.reduce((a, b) => a + b,
          L.map(u => u.age, users)));

// ver2 함수로 대체
const add = (a, b) => a + b;

console.log(
  _.reduce(add, L.map(u => u.age, users)));

2. reduce 하나 보다 map + filter + reduce

// users 데이터의 나이를 합산하는 코드: 
const users = [
  { name: 'AA', age: 35 },
  { name: 'BB', age: 26 },
  { name: 'CC', age: 28 },
  { name: 'DD', age: 34 },
  { name: 'EE', age: 23 },
];

// problematic code: reduce 하나에 복잡한 함수를 씀
console.log(
  _.reduce((total, u) => u.age >= 30 ? total: total + u.age, 
           0, 
           users)));
// updated ver
console.log(
  _.reduce(add,
       _.map(u => u.age,
           _.filter(u => u.age < 30, users))));

3. queryToObject

const obj1 = {
  a : 1,
  b : undefined,
  c: 'CC',
  d: 'DD'
}

// 명령형 코드
function query1(obj) {
  let res = '';
  for (const k in obj) { // k는 key(a, b, c, d)
    const v = obj[k]; // v는 value/
    if (v === undefined) continue;
    if (res != '') res += ' | ';
    res += k + ' = ' + v;
  }
}
console.log(query1(obj1))

// 함수형 코드
function query2(obj) {
  return Object
  	.entries(obj)
  	.reduce((query, [k, v], i) => {
    	if (v === undefined) return query;
    	return query + (i > 0 ? '&' : '') + k + '=' + v;
  	}, '');
}
console.log(query2(obj1))

0개의 댓글