Remove the insane "fold" operation, replace with saner "reduce".

This commit is contained in:
Stephen Dolan committed 2013-05-16 15:07:53 +01:00
1 parent 8fbee891b3
commit bc42812715
7 files changed
+37 -38

No files matched your search

+3 -3
View File
@@ -554,10 +554,10 @@ static const char* const jq_builtins[] = {
"def unique: group_by(.) | map(.[0]);", "def unique: group_by(.) | map(.[0]);",
"def max_by(f): _max_by_impl(map([f]));", "def max_by(f): _max_by_impl(map([f]));",
"def min_by(f): _min_by_impl(map([f]));", "def min_by(f): _min_by_impl(map([f]));",
"def add: fold null as $sum (.[] | $sum + .);", "def add: reduce .[] as $x (null; . + $x);",
"def del(f): delpaths([path(f)]);", "def del(f): delpaths([path(f)]);",
"def _assign(paths; value): value as $v | fold . as $obj (path(paths) as $p | $obj | setpath($p; $v));", "def _assign(paths; value): value as $v | reduce path(paths) as $p (.; setpath($p; $v));",
"def _modify(paths; update): fold . as $obj (path(paths) as $p | $obj | setpath($p; getpath($p) | update));", "def _modify(paths; update): reduce path(paths) as $p (.; setpath($p; getpath($p) | update));",
"def recurse(f): ., (f | select(. != null) | recurse(f));", "def recurse(f): ., (f | select(. != null) | recurse(f));",
"def to_entries: [keys[] as $k | {key: $k, value: .[$k]}];", "def to_entries: [keys[] as $k | {key: $k, value: .[$k]}];",
"def from_entries: map({(.key): .value}) | add;", "def from_entries: map({(.key): .value}) | add;",
+16 -7
View File
@@ -320,15 +320,24 @@ block gen_collect(block expr) {
gen_op_var_bound(LOADV, array_var)); gen_op_var_bound(LOADV, array_var));
} }
block gen_fold(const char* varname, block init, block fold) { block gen_reduce(const char* varname, block source, block init, block body) {
block loop = BLOCK(fold, gen_op_var_unbound(STOREV, varname), gen_op_simple(BACKTRACK)); block res_var = block_bind(gen_op_var_unbound(STOREV, "reduce"),
gen_noop(), OP_HAS_VARIABLE);
block loop = BLOCK(gen_op_simple(DUP),
source,
block_bind(gen_op_var_unbound(STOREV, varname),
BLOCK(gen_op_var_bound(LOADVN, res_var),
body,
gen_op_var_bound(STOREV, res_var)),
OP_HAS_VARIABLE),
gen_op_simple(BACKTRACK));
return BLOCK(gen_op_simple(DUP), return BLOCK(gen_op_simple(DUP),
init, init,
block_bind(gen_op_var_unbound(STOREV, varname), res_var,
BLOCK(gen_op_target(FORK, loop), gen_op_target(FORK, loop),
loop, loop,
gen_op_var_unbound(LOADV, varname)), gen_op_var_bound(LOADVN, res_var));
OP_HAS_VARIABLE));
} }
block gen_definedor(block a, block b) { block gen_definedor(block a, block b) {
+1 -1
View File
@@ -34,7 +34,7 @@ block gen_call(const char* name, block body);
block gen_subexp(block a); block gen_subexp(block a);
block gen_both(block a, block b); block gen_both(block a, block b);
block gen_collect(block expr); block gen_collect(block expr);
block gen_fold(const char* varname, block init, block body); block gen_reduce(const char* varname, block source, block init, block body);
block gen_definedor(block a, block b); block gen_definedor(block a, block b);
block gen_condbranch(block iftrue, block iffalse); block gen_condbranch(block iftrue, block iffalse);
block gen_and(block a, block b); block gen_and(block a, block b);
+12 -22
View File
@@ -1016,7 +1016,7 @@ sections:
(many jq functions such as `map` and `find` are in fact written (many jq functions such as `map` and `find` are in fact written
in jq). in jq).
Finally, jq has a `fold` operation, which is very powerful but a Finally, jq has a `reduce` operation, which is very powerful but a
bit tricky. Again, it's mostly used internally, to define some bit tricky. Again, it's mostly used internally, to define some
useful bits of jq's standard library. useful bits of jq's standard library.
@@ -1126,36 +1126,26 @@ sections:
input: '[[1,2],[10,20]]' input: '[[1,2],[10,20]]'
output: ['[[1,2,1,2], [10,20,1,2]]'] output: ['[[1,2,1,2], [10,20,1,2]]']
- title: Fold - title: Reduce
body: | body: |
The `fold` syntax in jq allows you to combine all of the The `reduce` syntax in jq allows you to combine all of the
results of an expression by accumulating them into a single results of an expression by accumulating them into a single
answer. As an example, we'll pass `[3,2,1]` to this expression: answer. As an example, we'll pass `[3,2,1]` to this expression:
fold 0 as $sum (.[] | $sum + .) reduce .[] as $item (0; . + $item)
The variable `$sum` is first given the value `0`. The body For each result that `.[]` produces, `. + $item` is run to
of the fold (i.e. `.[] | $sum + .`) is evaluated. `.[]` accumulate a running total, starting from 0. In this
produces three results, `3`, `2`, and `1`. For the first example, `.[]` produces the results 3, 2, and 1, so the
one, `$sum + .` gives `3`. effect is similar to running something like this:
Having produced this answer, jq backtracks to find the next 0 | (3 as $item | . + $item) |
result as per usual. However, this time, `$sum` is set to (2 as $item | . + $item) |
the previous value of the body, so `$sum + .` gives (1 as $item | . + $item)
`5`. After the final backtracking, `$sum + .` gives
`6`. This final value is used as the value of the entire
`fold` expression, so the above filter returns `6`.
More formally, in order to evaluate `fold INIT as $VAR
(BODY)`, jq first sets `$VAR` to the value of `INIT`. It
then runs through `BODY`. Each time `BODY` produces a value,
`$VAR` is set to that value and jq backtracks to find the
next one. When `BODY` stops producing values, the final
value of `$VAR` is the result of the entire expression.
examples: examples:
- program: 'fold 0 as $sum (.[] | $sum + .)' - program: 'reduce .[] as $item (0; . + $item)'
input: '[10,2,5,3]' input: '[10,2,5,3]'
output: ['20'] output: ['20']
+1 -1
View File
@@ -49,7 +49,7 @@ struct lexer_param;
"and" { return AND; } "and" { return AND; }
"or" { return OR; } "or" { return OR; }
"end" { return END; } "end" { return END; }
"fold" { return FOLD; } "reduce" { return REDUCE; }
"//" { return DEFINEDOR; } "//" { return DEFINEDOR; }
"|=" { return SETPIPE; } "|=" { return SETPIPE; }
"+=" { return SETPLUS; } "+=" { return SETPLUS; }
+3 -3
View File
@@ -58,7 +58,7 @@ struct lexer_param;
%token THEN "then" %token THEN "then"
%token ELSE "else" %token ELSE "else"
%token ELSE_IF "elif" %token ELSE_IF "elif"
%token FOLD "fold" %token REDUCE "reduce"
%token END "end" %token END "end"
%token AND "and" %token AND "and"
%token OR "or" %token OR "or"
@@ -226,8 +226,8 @@ Term "as" '$' IDENT '|' Exp {
jv_free($4); jv_free($4);
} | } |
"fold" Term "as" '$' IDENT '(' Exp ')' { "reduce" Term "as" '$' IDENT '(' Exp ';' Exp ')' {
$$ = gen_fold(jv_string_value($5), $2, $7); $$ = gen_reduce(jv_string_value($5), $2, $7, $9);
jv_free($5); jv_free($5);
} | } |
+1 -1
View File
@@ -379,7 +379,7 @@ def fac: if . == 1 then 1 else . * (. - 1 | fac) end; [.[] | fac]
# [] # []
# 1001 # 1001
fold 0 as $s (.[] | $s + .) reduce .[] as $x (0; . + $x)
[1,2,4] [1,2,4]
7 7