간단한 자바스크립트 변환기

개발자들은 통상적으로 계산을 몇 개의 독립된 단계로 나누기를 희망한다.무대가 작을수록 추리, 발전과 유지가 쉽다.예를 들어 우리는 함수f,g,k에서 일부 계산을 세 단계로 나누어 input => f(g(k(input))) 또는 RamdaR.comp(f,g,k) 또는 함수 조합 연산자를 가진 다른 라이브러리를 얻었다.
이런 방법의 문제는 함수 간에 중간 데이터를 전달하는 것이다. 각 하위 단계는 결과를 다음 단계로 전달하기 전에 계산을 완전히 완성해야 한다.어떤 서버의 요청 흐름이라면, 그들이 처리하는 데이터의 양이 매우 많을 수도 있고, 심지어는 무한대일 수도 있다.데이터가 무한한 상황에서 k는 영원히 제어권을 되돌려 주지 않는다.이것은 자주 발생하는 작업이기 때문에 많은 해결 방안이 있다. 예를 들어nodejs streams는 .pipe() 작업이stream transformer를 체인에 추가한다.
더 나쁜 방법은 두 단계 사이에 하나의 값을 전달하고 그것을 변이하는 것이다.공유 데이터의 변이를 추리하기 어렵다. 특히 그것이 특정한 귀속 구조일 때, 예를 들어 프로그래밍 언어의 추상적인 문법 트리이다.
이 글에서 묘사한 센서는 간단한 문제 해결 방안으로 간주될 수 있을 뿐만 아니라 중간 데이터도 없고 데이터의 돌연변이도 없다.
센서는 조립하기 쉽다.사실상 그것들은 함수일 뿐이고 함수 조합만으로도 충분하다. 위의 표현식(input => f(g(k(input)))R.comp(f,g,k)은 센서에 대해 같다.이로 인해 생기는 센서는 생산자로부터 데이터를 받아 소비자에게 전달하는 계산 파이프다.생산자와 소비자는 네트워크 데이터, 파일, 데이터베이스를 읽거나 쓰거나 메모리 진열에서만 할 수 있다.
ClojureTransducers are coming의 블로그 글에서 센서라는 용어를 소개하고 Ramda를 포함한 몇 개의 라이브러리를 통해 자바스크립트에 이식한 후 센서라는 용어가 유행하기 시작했다.Clojure 스타일의 센서는 본 논문과 다르다.Clojure에서는 환원제라고 불리는 소비자들을 변화시켰다.이런 변환 생산자의 센서와는 다르다.생성기 함수의 존재로 인해 이러한 차이는 ES6에서 그것들을 사용할 때 더욱 쉽게 정의할 수 있다.
Clojure의 최초 블로그 기사 유형은 다음과 같습니다.
;;reducing function signature
whatever, input -> whatever

;;transducer signature
(whatever, input -> whatever) -> (whatever, input -> whatever)

소비자가 아닌 생산자를 바꾸는 것을 예로 든 비교적 빠른 논문이 있다Lazy v. Yield: Incremental, Linear Pretty-printing in Haskell.데이터 유형은 다음과 같습니다.
type GenT e m = ReaderT (e -> m()) m
type Producer m e = GenT e m ()
type Consumer m e = e -> m ()
type Transducer m1 m2 e1 e2 = Producer m1 e1 -> Producer m2 e2
소비자를 이해하려면 Clojure 대체품State e a = s -> m (a, s)부터 소비자 정의까지의 축소기가 있다.
Consumer (State whatever) input
= input -> State whatever ()
= input -> whatever -> ((), whatever)
= whatever, input -> whatever
논문 속의 프로듀서는 더욱 복잡한 유형을 가지고 있다.Haskell에는 삽입식 발전기가 없다.
다행히 JavaScript는 이미 성공했습니다.프로듀서의 가치는 가늠할 수 없다.그것은 일부 메모리 진열이나 생성기 함수일 수도 있다.Consumer는 Iterable 값을 받아들여 어떤 방식으로 해석하는 함수입니다. 예를 들어 결과를 파일에 저장하거나 자바스크립트 표준 Array.from 함수가 결과를 메모리 그룹에 저장합니다.서열이 무한해도 이런 방법은 작용할 수 있다.
센서가 입력 생성기 (교체기) 와 다른 선택할 수 있는 파라미터를 가져와 다른 생성기 교체기를 되돌려주고 그 위에 다른 계산을 중첩합니다.
일반적인 모델은 다음과 같습니다.
function* myFun(parameter, input) {
  // local variable storing this transducer's internal state
  // state with initial values
  let sum = 0;
  for(const i of input) {
    // use the current value of `i` along with the current
    // state `sum` to compute its next output value `o`
    sum += i;
    const o = sum * 10;
    yield o;      
  }
}
예를 들어, 각 요소에 함수를 적용하는 매핑 함수는 다음과 같습니다.
function* map*(fun, input) {
  for(const i of input) {
     yield fun(i); 
  }
}
또는 filter 어떤 술어를 만족시키는 요소만 전달한다.
function* filter(pred, input) {
  for(const i of input) {
    if (pred(i))
      yield i;  
  }
}
첫 번째 요소num:
const take = num => function*(input) {
  let count = 0
  for(const i of input) {
    yield i
    if (++count === num)
      return
  }
}
다음은 더 복잡한 함수chunk.이것은 임의의 길이의 그룹 흐름을 받아들여 고정 길이의 그룹으로 나눈다size.
const chunk = size => function*(input) {
  const buf = []
  for(const i of input) {
    buf.push(...i)
    while (buf.length > num)
      yield buf.splice(0, num)
  }
  if (buf.length)
    yield buf
}
층별 데이터도 처리할 수 있다.예를 들어 @effectful/transducer JavaScript 구문 변환 항목
그러나 어느 단계에 비동기 코드가 있다면 모든 것이 그리 간단하지 않다.JavaScript의 입출력은 보통 비동기적이기 때문에 Producer에서 필요할 수 있습니다.비동기 리셋에서 next 교체기를 호출할 수 있지만 yield 호출할 수 없습니다.
최근 EMCAScript는 비동기 생성기 및 for await-of 구문 확장을 지원했습니다.for-offor await-of로 대체된 것을 제외하고 본 이야기의 모든 내용은 비동기 발전기에 적용된다.본고는 더욱 상세한 비동기 발전기를 센서로 하는 사례 연구가 있다.

좋은 웹페이지 즐겨찾기