1
0
mirror of https://github.com/stedolan/jq.git synced 2024-05-11 05:55:39 +00:00

Fold operation (code/docs/test)

This commit is contained in:
Stephen Dolan
2012-12-28 15:04:16 +00:00
parent 9302b16247
commit 417899f9a0
6 changed files with 61 additions and 1 deletions

View File

@@ -306,6 +306,17 @@ 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 loop = BLOCK(fold, gen_op_var_unbound(STOREV, varname), gen_op_simple(BACKTRACK));
return BLOCK(gen_op_simple(DUP),
init,
block_bind(gen_op_var_unbound(STOREV, varname),
BLOCK(gen_op_target(FORK, loop),
loop,
gen_op_var_unbound(LOADV, varname)),
OP_HAS_VARIABLE));
}
block gen_assign(block expr) { block gen_assign(block expr) {
block result_var = block_bind(gen_op_var_unbound(STOREV, "result"), block result_var = block_bind(gen_op_var_unbound(STOREV, "result"),
gen_noop(), OP_HAS_VARIABLE); gen_noop(), OP_HAS_VARIABLE);

View File

@@ -33,6 +33,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_assign(block expr); block gen_assign(block expr);
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);

View File

@@ -841,7 +841,7 @@ sections:
input: '{}' input: '{}'
output: [42] output: [42]
- title: Variables and Functions - title: Advanced features
body: | body: |
Variables are an absolute necessity in most programming languages, but Variables are an absolute necessity in most programming languages, but
they're relegated to an "advanced feature" in jq. they're relegated to an "advanced feature" in jq.
@@ -858,6 +858,10 @@ 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
bit tricky. Again, it's mostly used internally, to define some
useful bits of jq's standard library.
entries: entries:
- title: Variables - title: Variables
body: | body: |
@@ -962,6 +966,39 @@ sections:
- program: 'def addvalue(f): f as $x | map(. + $x); addvalue(.[0])' - program: 'def addvalue(f): f as $x | map(. + $x); addvalue(.[0])'
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
body: |
The `fold` syntax in jq allows you to combine all of the
results of an expression by accumulating them into a single
answer. As an example, we'll pass `[3,2,1]` to this expression:
fold 0 as $sum (.[] | $sum + .)
The variable `$sum` is first given the value `0`. The body
of the fold (i.e. `.[] | $sum + .`) is evaluated. `.[]`
produces three results, `3`, `2`, and `1`. For the first
one, `$sum + .` gives `3`.
Having produced this answer, jq backtracks to find the next
result as per usual. However, this time, `$sum` is set to
the previous value of the body, so `$sum + .` gives
`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:
- program: 'fold 0 as $sum (.[] | $sum + .)'
input: '[10,2,5,3]'
output: ['20']
- title: Assignment - title: Assignment

View File

@@ -49,6 +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; }
"//" { return DEFINEDOR; } "//" { return DEFINEDOR; }
"|=" { return SETPIPE; } "|=" { return SETPIPE; }
"+=" { return SETPLUS; } "+=" { return SETPLUS; }

View File

@@ -58,6 +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 END "end" %token END "end"
%token AND "and" %token AND "and"
%token OR "or" %token OR "or"
@@ -201,6 +202,11 @@ Term "as" '$' IDENT '|' Exp {
jv_free($4); jv_free($4);
} | } |
"fold" Term "as" '$' IDENT '(' Exp ')' {
$$ = gen_fold(jv_string_value($5), $2, $7);
jv_free($5);
} |
"if" Exp "then" Exp ElseBody { "if" Exp "then" Exp ElseBody {
$$ = gen_cond($2, $4, $5); $$ = gen_cond($2, $4, $5);
} | } |

View File

@@ -331,6 +331,10 @@ def fac: if . == 1 then 1 else . * (. - 1 | fac) end; [.[] | fac]
# [] # []
# 1001 # 1001
fold 0 as $s (.[] | $s + .)
[1,2,4]
7
# #
# Assignment # Assignment
# #