我想解析一个大的json文件(3GB)并返回此文件中每一行的哈希映射.我的直觉是使用换能器逐行处理文件并构造一个带有一些选定字段的矢量(文件中> 5%的字节).
但是,以下代码抛出OutOfMemory异常:
file.json
{"experiments": {"results": ...}}
{"experiments": {"results": ...}}
{"experiments": {"results": ...}}
Run Code Online (Sandbox Code Playgroud)
parser.clj
(defn load-with!
"Load a file using a parser, a structure and a transducer."
[parser structure xform path]
(with-open [r (clojure.java.io/reader path)]
(into structure xform (parser r))))
(def xf (map #(get-in % ["experiments" "results"])))
(def parser (comp (partial map cheshire.core/parse-string) line-seq))
(load-with! parser (vector) xf "file.json")
Run Code Online (Sandbox Code Playgroud)
当我使用JVisualVM可视化进程时,堆会随着时间的推移而增长,并且在进程崩溃之前会超过25 GB.
传感器在这种情况下是否合适?还有更好的选择吗?
我要求在函数结束时返回新结构之一.因此,我无法使用doseq来就地处理文件.
而且,我需要根据文件格式更改解析器和传感器.
谢谢 !
虽然从解决问题Hackkerank(https://www.hackerrank.com/challenges/string-compression/problem)我已经写了2个实现与不换能器.
我期望传感器实现比功能链操作符更快->>
.不幸的是,根据我的迷你基准测试,链接操作符的性能比传感器好2.5倍.
我在想,我应尽可能使用传感器.或者我没有正确理解传感器的概念?
时间:
"经过的时间:0.844459毫秒"
"经过的时间:2.697836毫秒"
码:
(defn string-compression-2
[s]
(->> s
(partition-by identity)
(mapcat #(if (> (count %) 1)
(list (first %) (count %))
(list (first %))))
(apply str)))
(def xform-str-compr
(comp (partition-by identity)
(mapcat #(if (> (count %) 1)
(list (first %) (count %))
(list (first %))))))
(defn string-compression-3
[s]
(transduce xform-str-compr str s))
(time (string-compression-2 "aaabccdddd"))
(time (string-compression-3 "aaabccdddd"))
Run Code Online (Sandbox Code Playgroud) RxJS v4 曾经有一种Observable.transduce
方法可以使用转换器。这允许使用在过去具有重大性能优势的独立于库的传感器运算符。
来源
RxJS v5.5 和 v6 具有可管道操作符和 v6 删除了方法链。因此,我假设 RxJS 操作符是标准的转换器。查看源代码,情况似乎并非如此。
RxJS v6 操作符的功能就像一个转换器,其中每个值在下一个值通过之前完全通过链传递,但 RxJS v6 操作符没有使用我在其他库中看到的标准转换器方法,这意味着,我不认为它们是便携式的。
关于传感器的整个事情是他们对集合本身一无所知。您可以编写 100 个普遍适用于任何集合或流类型的运算符,而不是专门为 observable 编写 100 个运算符。
在 RxJS v5 中是否.pipe
一致.transduce
或完全删除了这种方法?
在高层次上,我理解使用转换器不会创建任何中间数据结构,而通过一长串操作创建->>
,因此转换器方法的性能更高。这在我下面的一个例子中被证明是正确的。但是,当我添加clojure.core.async/chan
到组合中时,我并没有获得预期的性能改进。显然有些东西我不明白,我希望得到解释。
(ns dev
(:require [clojure.core.async :as async]
[criterium.core :as crit]))
;; Setup some toy data.
(def n 1e6)
(def data (repeat n "1"))
;; Reusable thread-last operation (the "slower" one).
(defn tx [x]
(->> x
(map #(Integer. %))
(map inc) (map inc) (map inc) (map inc) (map inc) (map inc)
(map inc) (map inc) (map inc) (map inc) (map inc)))
;; Reusable transducer (the "faster" one).
(def xf (comp
(map #(Integer. %))
(map inc) (map inc) …
Run Code Online (Sandbox Code Playgroud) 我遇到completing
了clojuredocs的功能,但目前还没有doc.
你能提供一些例子吗?
是什么区别:
(transduce (comp fn-1 fn-2 fn-3) conj vector-collection)
Run Code Online (Sandbox Code Playgroud)
和
(eduction fn-1 fn-2 fn-3 vector-collection)
Run Code Online (Sandbox Code Playgroud)
我读过教育文档,但不明白教育的目的.
我想在转换器中使用 pmap,如果我使用常规地图编写代码,它工作正常。但是,如果我使用 pmap,则会出现 arity 异常。是否有可能在 clojure 中实现这一点,或者我做错了什么?如果这是不可能的,有人可以指出为什么会发生这种情况的文档吗?只是出于好奇。下面粘贴的是我的代码和 arity 异常的开头。
(def get-in-map (comp
(pmap read-csv)
))
clojure.lang.Compiler$CompilerException: clojure.lang.ArityException: Wrong number of args (1) passed to: core/pmap, compiling:
Run Code Online (Sandbox Code Playgroud) 我想知道是否有一种方法可以使用换能器来展平列表并过滤唯一值?
通过链接,这很容易:
import {uniq, flattenDeep} from 'lodash';|
const arr = [1, 2, [2, 3], [1, [4, 5]]];
uniq(flattendDeep(arr)); // -> [1, 2, 3, 4, 5]
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.min.js"></script>
Run Code Online (Sandbox Code Playgroud)
但是这里我们在列表上循环了两次(深度层+n)。不理想。
我想要实现的是在这种情况下使用换能器。我已经阅读了关于它的 Ramda 文档https://ramdajs.com/docs/#transduce,但我仍然找不到正确编写它的方法。
目前,我使用了一个带有递归函数的 reduce 函数:
import {isArray} from 'lodash';
const arr = [1, 2, [2, 3], [1, [4, 5]]];
const flattenDeepUniq = (p, c) => {
if (isArray(c)) {
c.forEach(o => p = flattenDeepUniq(p, o));
}
else {
p = !p.includes(c) ? [...p, c] : p;
}
return p; …
Run Code Online (Sandbox Code Playgroud)我最近花了一些时间来研究转换器(函数式编程中的工具,可以在不失去代码可读性/灵活性的情况下提高性能),当我开始测试它们的实际速度时,我得到了一些非常令人失望的结果。考虑:
const inc = x => x + 1;
const isEven = x => x % 2 === 0;
// simplest, shortest way I would be comfortable with if performance wasn't an issue
const mapFilter = xs => xs.filter(isEven).map(inc);
// transducers way
// function composition
const compose = (...fns) => x => fns.reduceRight((y, f) => f(y), x);
const map = f => step => (a, c) => step(a, f(c));
const filter = p => step => (a, c) => (p(c) …
Run Code Online (Sandbox Code Playgroud)使用数组时,通常需要中间表示形式-尤其是在函数编程中,在这种编程中,数据通常被视为不可变的:
const square = x => x * x;
const odd = x => (x & 1) === 1;
let xs = [1,2,3,4,5,6,7,8,9];
// unnecessary intermediate array:
xs.map(square).filter(odd); // [1,4,9,16,25,36,49,64,81] => [1,9,25,49,81]
// even worse:
xs.map(square).filter(odd).slice(0, 2); // [1,9]
Run Code Online (Sandbox Code Playgroud)
如何在Javascript / Ecmascript 2015中避免这种行为,以获得更有效的迭代算法?
transducer ×11
clojure ×6
javascript ×3
reduce ×3
arrays ×1
automata ×1
channels ×1
nfa ×1
performance ×1
pipelining ×1
pmap ×1
ramda.js ×1
recursion ×1
rxjs ×1