이 글은 node.js modules you should know about 시리즈의 세번째 연재다.

1회 연재 - dnode (RPC 라이브러리)

2회 연재 - optimist (옵션 파서)

이번 연재에서 소개할 모듈은 내가 직접 작성한 node-lazy이라는 lazy 리스트 모듈(역자주: 적절한 번역 용어가 떠오르지 않아냥 lazy라고 했습니다)이다.

기본적으로 여러분은 새로운 lazy 객체를 생성하고, data 이벤트(event emitter)를 통해  실제 data를 lazy 객체에 전달한다. 그런 다음 다양한 Functional Programming 메서드들을 이용한 체이닝을 통해 이러한 데이터를 처리할 수 있다.

여기 간단한 에제가 있다. 이 예제에서 우리는 새로운 lazy 객체를 생성하고 짝수만 리턴하는 filter를 정의했다. 그리고 5개의 요소들을 take 한 다음, 그것들을 map 했다. 마지막으로 결과값을 리스트로 join 했다(스레드의 join 메서드를 연상해라).

var Lazy = require('lazy');

var lazy = new Lazy;
lazy
  .filter(function (item) {
    return item % 2 == 0
  })
  .take(5)
  .map(function (item) {
    return item*2;
  })
  .join(function (xs) {
    console.log(xs);
  });

data 이벤트가 발생하면 위 예제 코드를 통해 리턴된 객체로 실제 데이터가 넘어 가고, 그것들은 로직대로 처리될 것이다.

예를 들어, 다음과 같이 실행 코드를 작성했다면,

[0,1,2,3,4,5,6,7,8,9,10].forEach(function (x) {
  lazy.emit('data', x);
});
setTimeout(function () { lazy.emit('end') }, 100);

이제 5개의 원소가 체이닝의 끝에 도달하면 console.log에 의해 결과값이 출력될 것이다.

결과는 다음과 같다 : [0, 4, 8, 12, 16]

다음은 node-iptables(나의 또다른 모듈)에서 사용된 실전 예제이다.

var Lazy = require('lazy');
var spawn = require('child_process').spawn;
var iptables = spawn('iptables', ['-L', '-n', '-v']);

Lazy(iptables.stdout)
    .lines
    .map(String)
    .skip(2) // skips the two lines that are iptables header
    .map(function (line) {
        // packets, bytes, target, pro, opt, in, out, src, dst, opts
        var fields = line.trim().split(/\s+/, 9);
        return {
            parsed : {
                packets : fields[0],
                bytes : fields[1],
                target : fields[2],
                protocol : fields[3],
                opt : fields[4],
                in : fields[5],
                out : fields[6],
                src : fields[7],
                dst : fields[8]
            },
            raw : line.trim()
        };
    });

이 코드 조각은 iptables -L -n -v에서 얻은 출력값을 나중에 활용하기 위해서 자료구조 형태로 변환한다.

우선 기존 스트림 ( iptables.stdout 스트림)으로부터 새로운 Lazy 객체를 생성한다. 다음으로 스트림을 \n문자로 구분하기 위해 특별한 lines getter를 호출해서 라인 단위의 스트림으로 변경한다. 이후 스트림은 String 생성자를 통해 문자열로 변경된다. 다음으로 맨앞 두 라인은 skip(2) 에 의해 스킵되고, 이외 라인들은 map에 의해 자료 구조 형태로 변환된다.

node-lazy를 이용하면 무한 범위를 포함한 모든 범위의 숫자 리스트를 생성할 수 있다. 다음 ranges.js를 살펴보자.

var Lazy = require('lazy');

Lazy.range('1..20').join(function (xs) {
    console.log(xs);
});

Lazy.range('444..').take(10).join(function (xs) {
    console.log(xs);
});

Lazy.range('2,4..20').take(10).join(function (xs) {
    console.log(xs);
});

실행 결과는 다음과 같다.

$ node ranges.js
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ]
[ 2, 4, 6, 8, 10, 12, 14, 16, 18 ]
[ 444, 445, 446, 447, 448, 449, 450, 451, 452, 453 ]

아래는 node-lazy로 생성할 수 있는 다양한 숫자 범위들의 예이다.

Lazy.range('10..')       - infinite range starting from 10
Lazy.range('(10..')      - infinite range starting from 11
Lazy.range(10)           - range from 0 to 9
Lazy.range(-10, 10)      - range from -10 to 9 (-10, -9, ... 0, 1, ... 9)
Lazy.range(-10, 10, 2)   - range from -10 to 8, skipping every 2nd element (-10, -8, ... 0, 2, 4, 6, 8)
Lazy.range(10, 0, 2)     - reverse range from 10 to 1, skipping every 2nd element (10, 8, 6, 4, 2)
Lazy.range(10, 0)        - reverse range from 10 to 1
Lazy.range('5..50')      - range from 5 to 49
Lazy.range('50..44')     - range from 50 to 45
Lazy.range('1,1.1..4')   - range from 1 to 4 with increment of 0.1 (1, 1.1, 1.2, ... 3.9)
Lazy.range('4,3.9..1')   - reverse range from 4 to 1 with decerement of 0.1
Lazy.range('[1..10]')    - range from 1 to 10 (all inclusive)
Lazy.range('[10..1]')    - range from 10 to 1 (all inclusive)
Lazy.range('[1..10)')    - range grom 1 to 9
Lazy.range('[10..1)')    - range from 10 to 2
Lazy.range('(1..10]')    - range from 2 to 10
Lazy.range('(10..1]')    - range from 9 to 1
Lazy.range('(1..10)')    - range from 2 to 9
Lazy.range('[5,10..50]') - range from 5 to 50 with a step of 5 (all inclusive)

lazy는 다음과 같이 npm을 통해 설치할 수 있다.

npm install lazy

 

If you love these articles, subscribe to my blog for more, follow me on Twitter to find about my adventures, and watch me produce code on GitHub!