]> granicus.if.org Git - jq/commitdiff
Apply TCO to recurse/1, add recurse/2; tweak docs
authorpkoppstein <pkoppstein@gmail.com>
Sun, 3 Aug 2014 23:49:02 +0000 (19:49 -0400)
committerNicolas Williams <nico@cryptonector.com>
Tue, 5 Aug 2014 02:11:01 +0000 (21:11 -0500)
Signed-off-by: Nicolas Williams <nico@cryptonector.com>
builtin.c
docs/content/3.manual/manual.yml

index b6da4be8b8ba5819296984ca754fdb7c4bad9b0d..4fb496cd05bfd5e71aaabaf15c8a46e9d79c7d11 100644 (file)
--- a/builtin.c
+++ b/builtin.c
@@ -928,9 +928,13 @@ static const char* const jq_builtins[] = {
   "def del(f): delpaths([path(f)]);",
   "def _assign(paths; value): value as $v | reduce path(paths) as $p (.; setpath($p; $v));",
   "def _modify(paths; update): reduce path(paths) as $p (.; setpath($p; getpath($p) | update));",
-  "def recurse(f): ., (f | select(. != null) | recurse(f));",
+
+  // recurse
+  "def recurse(f): def r: ., (f | select(. != null) | r); r;",
+  "def recurse(f; cond): def r: ., (f | select(cond) | r); r;",
   "def recurse: recurse(.[]?);",
   "def recurse_down: recurse;",
+
   "def to_entries: [keys[] as $k | {key: $k, value: .[$k]}];",
   "def from_entries: map({(.key): .value}) | add | .//={};",
   "def with_entries(f): to_entries | map(f) | from_entries;",
index 6522a6a07d1167600923b059747be8d9366cd4fe..8fa7ec0cde019652fcced028791a4b6f67c3928f 100644 (file)
@@ -22,7 +22,7 @@ body: |
   combine two filters, like addition, generally feed the same input to
   both and combine the results. So, you can implement an averaging
   filter as `add / length` - feeding the input array both to the `add`
-  filter and the `length` filter and dividing the results.
+  filter and the `length` filter and then performing the division.
 
   But that's getting ahead of ourselves. :) Let's start with something
   simpler:
@@ -40,14 +40,16 @@ manpage_intro: |
   running the command `jq 'map(.price) | add'` will take an array of
   JSON objects as input and return the sum of their "price" fields.
 
-  By default, `jq` reads a stream of JSON objects (whitespace
-  separated) from `stdin`. One or more <files> may be specified, in
+  `jq` can accept text input as well, but by default, `jq` reads a
+  stream of JSON entities (including numbers and other literals) from
+  `stdin`. Whitespace is only needed to separate entities such as 1
+  and 2, and true and false.  One or more <files> may be specified, in
   which case `jq` will read input from those instead.
 
-  The <options> are described in the [INVOKING JQ] section, they
+  The <options> are described in the [INVOKING JQ] section; they
   mostly concern input and output formatting. The <filter> is written
   in the jq language and specifies how to transform the input
-  document.
+  file or document.
 
   ## FILTERS
 
@@ -1250,7 +1252,7 @@ sections:
             output: ['[1,2,4,8,16,32,64]']
 
 
-      - title: "`recurse(f)`, `recurse`, `recurse_down`"
+      - title: "`recurse(f)`, `recurse`, `recurse(f; condition), `recurse_down`"
         body: |
 
           The `recurse(f)` function allows you to search through a
@@ -1275,10 +1277,24 @@ sections:
           When called without an argument, `recurse` is equivalent to
           `recurse(.[]?)`.
 
+          `recurse(f) is identical to `recurse(f; . != null)` and can be
+          used without concerns about recursion depth.
+
+          `recurse(f; condition)` is a generator which begins by
+          emitting . and then emits in turn .|f, .|f|f, .|f|f|f, ...  so long
+          as the computed value satisfies the condition. For example,
+          to generate all the integers, at least in principle, one
+          could write `recurse(.+1; true)`.
+
           For legacy reasons, `recurse_down` exists as an alias to
           calling `recurse` without arguments. This alias is considered
           *deprecated* and will be removed in the next major release.
 
+          These `recurse` filters are all written to take advantage of
+          jq's optimizations of certain cases of tail recursion.  In
+          particular, there is no memory overhead due to the
+          recursion.
+
         examples:
           - program: 'recurse(.foo[])'
             input: '{"foo":[{"foo": []}, {"foo":[{"foo":[]}]}]}'
@@ -1287,6 +1303,7 @@ sections:
               - '{"foo":[]}'
               - '{"foo":[{"foo":[]}]}'
               - '{"foo":[]}'
+
           - program: 'recurse'
             input: '{"a":0,"b":[1]}'
             output:
@@ -1294,6 +1311,13 @@ sections:
               - '[1]'
               - '1'
 
+          - program: 'recurse(. * .; . < 20)'
+            input: 2
+            output:
+                - 2
+                - 4
+                - 16
+
       - title: "`..`"
         body: |