[clojure] 누군가 Clojure 변환기를 간단한 용어로 설명해 줄 수 있습니까?

나는 이것을 읽으려고 시도했지만 여전히 그것들의 가치 나 그들이 대체하는 것을 이해하지 못한다. 그리고 그들은 내 코드를 더 짧고 이해하기 쉽게 만들까요?

최신 정보

많은 사람들이 답변을 올렸지 만, 나 같은 바보라도 이해할 수있는 아주 간단한 것에 대해 트랜스 듀서가 있거나없는 예를 보는 것은 좋을 것입니다. 물론 트랜스 듀서가 어느 정도 높은 수준의 이해를 필요로하지 않는 한,이 경우 절대 이해할 수 없습니다.



답변

트랜스 듀서는 기본 시퀀스가 ​​무엇인지 (어떻게 수행하는지) 모르고 데이터 시퀀스로 무엇을해야하는지 레시피입니다. 모든 시퀀스, 비동기 채널 또는 관찰 가능할 수 있습니다.

그들은 구성 가능하고 다형성입니다.

이점은 새 데이터 소스가 추가 될 때마다 모든 표준 결합자를 구현할 필요가 없다는 것입니다. 계속해서. 결과적으로 사용자는 다른 데이터 소스에서 해당 레시피를 재사용 할 수 있습니다.

광고 업데이트

Clojure 1.7 이전 버전에서는 데이터 흐름 쿼리를 작성하는 세 가지 방법이 있습니다.

  1. 중첩 된 호출
    (reduce + (filter odd? (map #(+ 2 %) (range 0 10))))
  1. 기능성 구성
    (def xform
      (comp
        (partial filter odd?)
        (partial map #(+ 2 %))))
    (reduce + (xform (range 0 10)))
  1. 스레딩 매크로
    (defn xform [xs]
      (->> xs
           (map #(+ 2 %))
           (filter odd?)))
    (reduce + (xform (range 0 10)))

변환기를 사용하면 다음과 같이 작성합니다.

(def xform
  (comp
    (map #(+ 2 %))
    (filter odd?)))
(transduce xform + (range 0 10))

그들은 모두 똑같이합니다. 차이점은 트랜스 듀서를 직접 호출하지 않고 다른 기능으로 전달한다는 것입니다. 변환기는 무엇을해야하는지 알고 있으며 변환기를 가져 오는 기능은 방법을 알고 있습니다. 결합 자의 순서는 스레딩 매크로 (자연 순서)로 작성하는 것과 같습니다. 이제 xform채널과 함께 재사용 할 수 있습니다 .

(chan 1 xform)


답변

변환기는 효율성을 향상시키고보다 모듈화 된 방식으로 효율적인 코드를 작성할 수 있도록합니다.

이것은 괜찮은 실행 입니다.

이전에 호출을 구성에 비해 map, filter, reduce당신이 그 컬렉션을 각 단계 사이에 중간 컬렉션을 구축하고, 반복적으로 걸을 필요가 없기 때문에 등 당신은 더 나은 성능을 얻을.

reducers모든 작업을 단일 표현식으로 또는 수동으로 구성하는 것과 비교하여 추상화를 사용하기가 더 쉽고, 모듈화가 더 좋으며, 처리 함수를 재사용 할 수 있습니다.


답변

트랜스 듀서는 기능을 줄이기위한 조합 수단입니다.

예 : 축소 함수는 두 개의 인수 (지금까지의 결과와 입력)를 취하는 함수입니다. 새 결과를 반환합니다 (지금까지). 예 +: 두 개의 인수를 사용하면 첫 번째는 지금까지의 결과로, 두 번째는 입력으로 생각할 수 있습니다.

변환기는 이제 + 기능을 사용하여 두 배 더하기 기능으로 만들 수 있습니다 (추가하기 전에 모든 입력을 두 배로 늘림). 트랜스 듀서는 다음과 같은 모습입니다 (대부분의 기본 용어로).

(defn double
  [rfn]
  (fn [r i]
    (rfn r (* 2 i))))

그림으로 대체 rfn하여 두 번 이상으로 변환되는 +방법을 확인하십시오 +.

(def twice-plus ;; result of (double +)
  (fn [r i]
    (+ r (* 2 i))))

(twice-plus 1 2)  ;-> 5
(= (twice-plus 1 2) ((double +) 1 2)) ;-> true

그래서

(reduce (double +) 0 [1 2 3]) 

이제 12를 산출합니다.

변환기에 의해 반환 된 감소 함수는 결과가 어떻게 누적되는지와는 무관합니다. 그 이유는 무의식적으로 전달 된 감소 함수와 함께 축적되기 때문입니다. 여기서 우리는 conj대신 +. Conj컬렉션과 값을 취하고 해당 값이 추가 된 새 컬렉션을 반환합니다.

(reduce (double conj) [] [1 2 3]) 

[2 4 6]

또한 입력 소스의 종류와 무관합니다.

여러 변환기를 (체인 가능한) 레시피로 연결하여 감소 기능을 변환 할 수 있습니다.

업데이트 : 이제 공식 페이지가 있으므로 http://clojure.org/transducers 를 읽어 보는 것이 좋습니다 .


답변

일련의 함수를 사용하여 데이터 스트림을 변환한다고 가정 해보십시오. Unix 셸을 사용하면 파이프 연산자로 이런 종류의 작업을 수행 할 수 있습니다.

cat /etc/passwd | tr '[:lower:]' '[:upper:]' | cut -d: -f1| grep R| wc -l

(위의 명령은 사용자 이름에 대문자 또는 소문자 r이있는 사용자 수를 계산합니다.) 이것은 일련의 프로세스로 구현되며 각 프로세스는 이전 프로세스의 출력에서 ​​읽으므로 4 개의 중간 스트림이 있습니다. 5 개의 명령을 단일 집계 명령으로 구성하는 다른 구현을 상상할 수 있습니다.이 명령은 입력에서 읽고 출력을 정확히 한 번만 기록합니다. 중간 스트림이 비싸고 구성이 저렴하다면 좋은 절충안이 될 수 있습니다.

Clojure도 마찬가지입니다. 변환 파이프 라인을 표현하는 방법에는 여러 가지가 있지만 수행 방법에 따라 한 함수에서 다음 함수로 전달되는 중간 스트림으로 끝날 수 있습니다. 데이터가 많은 경우 해당 함수를 단일 함수로 구성하는 것이 더 빠릅니다. 변환기를 사용하면 쉽게 할 수 있습니다. 이전의 Clojure 혁신, 감속기도 그렇게 할 수 있지만 약간의 제한이 있습니다. 변환기는 이러한 제한 사항 중 일부를 제거합니다.

따라서 질문에 답하기 위해 변환기가 반드시 코드를 짧게 또는 더 이해하기 쉽게 만들지는 않지만 코드가 더 길거나 덜 이해하기 쉽지 않을 것이며 많은 데이터로 작업하는 경우 변환기가 코드를 만들 수 있습니다. 더 빨리.

이것은 변환기에 대한 꽤 좋은 개요입니다.


답변

Rich Hickey는 Strange Loop 2014 컨퍼런스 (45 분)에서 ‘트랜스 듀서’강연을했습니다.

그는 트랜스 듀서가 무엇인지 간단한 방법으로 설명합니다. 실제 사례는 공항에서 가방 처리입니다. 그는 다른 측면을 명확하게 분리하고 현재 접근 방식과 대조합니다. 끝까지 그는 그들의 존재에 대한 근거를 제시합니다.

비디오 : https://www.youtube.com/watch?v=6mTbuzafcII


답변

transducers-js 에서 예제를 읽으면 일상적인 코드에서 어떻게 사용할 수 있는지 구체적으로 이해하는 데 도움이됩니다.

예를 들어, 다음 예제를 고려하십시오 (위 링크의 README에서 가져옴).

var t = require("transducers-js");

var map    = t.map,
    filter = t.filter,
    comp   = t.comp,
    into   = t.into;

var inc    = function(n) { return n + 1; };
var isEven = function(n) { return n % 2 == 0; };
var xf     = comp(map(inc), filter(isEven));

console.log(into([], xf, [0,1,2,3,4])); // [2,4]

하나 xf는 Underscore를 사용하는 일반적인 대안보다 훨씬 깔끔하게 보입니다.

_.filter(_.map([0, 1, 2, 3, 4], inc), isEven);


답변

변환기는 하나의 감소 기능을 사용하고 다른 기능을 반환하는 기능입니다. 감소 기능은

예를 들면 :

user> (def my-transducer (comp count filter))
#'user/my-transducer
user> (my-transducer even? [0 1 2 3 4 5 6])
4
user> (my-transducer #(< 3 %) [0 1 2 3 4 5 6])
3

이 경우 my-transducer는 0에 적용되는 입력 필터링 기능을 사용하고 그 값이 짝수이면? 첫 번째 경우 필터는 해당 값을 카운터로 전달한 후 다음 값을 필터링합니다. 먼저 필터링 한 다음 해당 값을 모두 전달하는 대신 계산합니다.

두 번째 예제에서와 동일하게 한 번에 하나의 값을 확인하고 해당 값이 3보다 작 으면 1을 더할 수 있습니다.