Jq语言

jq是领域特定高级的纯函数式编程语言，它采用词法作用域，其中所有JSON值都是常量。jq支持回溯并可管理JSON数据的无限长字串流。jq支持基于名字空间的模块系统，并对闭包有一定支持，尤其是它的函数和泛函表达式可以用作其他函数的参数。
jq与Icon和Haskell编程语言有关。它最初采用Haskell实现，随即移植至C语言。gojq是“纯”Go实现。jq还有用Rust实现的叫做jaq的方言，它规定了指称语义。

**历史**
jq由Stephen Dolan创建并在2012年10月发行2014 。它被设计为“针对JSON数据的sed类似者”。在jq版本1.5中增加支持了正则表达式。
针对jq的叫做yq的“包装器”，增加支持了YAML、XML和TOML。它首次发行于2017年。
用Go实现的gojq最初发行于2019年，gojq显着的扩展jq包括了支持YAML。
用Rust实现的jaq，其项目目标是更快速和更准确的jq实现，仍保持与jq在大多数情况下的兼容性。在2024年3月于其目标中排除了jq的特定高级特征，比如模块、SQL风格算子和给非常大JSON文档的串流解析器。
用jq实现的jqjq，最初发行于2022年。jqjq显着的可以运行自身，拥有REPL并支持eval。

**用法**

**命令行用法**
jq典型的用于命令行，并可以协作于其他命令行实用工具，比如curl。下面的例子展示如何将curl命令的输出通过管道接转到jq过滤器，从而确定同这个Wikipedia页面关联的范畴名字：
$ URL=$(echo 'jq语言' | jq -rR '@uri "https://zh.wikipedia.org/w/api.php?action=parse&page=\(.)&format=json"')
$ curl -s ${URL} | jq '.parse.categories."*"'
这里的流水线产生的输出，由JSON字符串的串流组成，它们是：
"小写标题"
"CS1英语来源_(en)"
"动态类型编程语言"
"函数式编程语言"
"面向文本编程语言"
"2012年建立的程式语言"
"数据查询语言"
"2012年软件"
上述curl命令对这个页面使用了MediaWiki API来产生JSON响应。管道（pipe）符号允许curl的输出由jq来访问，它是标准的Unix shell的进程间通信机制。
这里展示的jq过滤器的方法链是如下流水线（pipeline）的简写：
."parse" | ."categories" | . | ."*"
这对应于curl调用所产生的嵌套JSON结构。jq流水线的构造方式，同Unix风格流水线一样，采用管道符号。

**嵌入式用法**
C语言和Go实现二者都提供函数库，使得jq功能可以嵌入到其他应用和编程环境之中。
例如，gojq已经集成于SQLite，故而jq函数可以在其SQL语句中获得到。这些函数被标记为“确定性的”，故而可以被用在CREATE INDEX命令中。

**运算的模态**
jq缺省的充当针对JSON输入的“串流编辑器”，非常像被当作多行文本的“串流编辑器”的sed实用工具。但是jq有一些其他运算模态： 
# 它可以将来自一个或多个来源的输入当作文本的诸行；
# 它可以将来自特定来源的输入的串流收集到一个JSON阵列之中；
# 它可以使用所谓的“串流解析器”解析其输入，产生针对所有“叶子”路径的PATH, VALUE阵列的串流。
“串流解析器”（streaming parser），在一个或多个JSON输入太大无法载入内存之时特别有用，因为它需求的内存典型的相当小。例如，对于任意大的JSON对象的阵列，峰值内存需求不比处理最大顶层对象所需要的多出很多。
这些运算模态可以在特定限制下组合起来。

**语法和语义**

**类型**
所有JSON值自身是jq中的值，它们从而有在下列表格中展示的类型。gojq和jaq实现将数区分为整数和浮点数。gojq实现支持无界精度整数算术，同于jq采用Haskell的最初实现。
是通常表示空值的一个值，就像任何其他JSON标量一样；它不是空指针。lang=json（对应于NaN）和infinite（参见IEEE 754），是仅有的两个不是JSON值的jq标量。

**形式**
jq有特殊语法形式，比如：
*"csharp"：变量绑定；
*"python"和"python"：更新赋值和平凡（plain）赋值；
*if …… then …… elif …… then …… else …… end：条件构造；
*reduce EXP as $var (INIT; UPDATE)：串流归约；
*"csharp"：输出中间值的串流归约；
*"python"：函数创建；
*include MOD;和import MOD as $name;：模块的包含和导入。
jq中有两种类型的符号，可称为“变量”的值绑定和函数。二者都是词法作用域的，表达式只能提及其左侧即前面最近的定义的符号，但是函数可以提及自身来创建递归函数。

**过滤器**
jq是面向JSON的编程语言，使用|符号来连接过滤器形成流水线。例如：
$ echo '1,2' | jq 'add'
3
$ jq -n '1,2 | add'
3
这里jq内的JSON阵列1,2是求值为阵列的一个jq过滤器。
尽管类似于Unix流水线，jq流水线允许将到来数据，如同并行的发送到在|右手端的多于一个接收者。例如，程序add/length将计算阵列中数的平均，故而：
$ jq -n '1,2 | add/length'
1.5
$ jq -nc '1,2 | length, add, add/length'
2,3,1.5
单独的点号.可以充任占位符（placeholder）或通配符，例如：
$ jq -nc '1 | ., .'
1,1
$ jq -n '2 | pow(.; .)'
4
在jq中采用隐式编程风格，阶乘可以通过共递归运算recurse()定义为"python"，下面是其使用例子：
$ echo '0 1 2 6' | jq -c 'limit(.+1; 0,1 | recurse(first+1, last*(first+1)) | last)'
1
1,1
1,1,2
1,1,2,6,24,120,720
$ jq -n 'first(0,1 | recurse(first+1, last*(first+1)) | last)'
1
$ jq -n '0,1,2,6 | nth(.; 0,1 | recurse(first+1, last*(first+1)) | last)'
1
1
2
720
$ jq -nc '0,1,2,6 | map(nth(.; 0,1 | recurse(first+1, last*(first+1)) | last))'
1,1,2,720
斐波那契数列可以通过共递归运算recurse()定义为"python"，下面是其使用例子：
$ echo '0 1 2 6' | jq -c 'limit(.+1; 0,1 | recurse(last, add) | first)'
0
0,1
0,1,1
0,1,1,2,3,5,8
$ jq -n 'first(0,1 | recurse(last, add) | first)'
0
$ jq -n '0,1,2,6 | nth(.; 0,1 | recurse(last, add) | first)'
0
1
1
8
$ jq -nc '0,1,2,6 | map(nth(.; 0,1 | recurse(last, add) | first))'
0,1,1,8
这种无限列表不可以不加限定的使用，也不可以对其进行last()运算。下面将其定义为新的命名过滤器：
def fac: nth(.; 0,1 | recurse(first+1, last*(first+1)) | last);
def fib: nth(.; 0,1 | recurse(last, add) | first);
共递归运算recurse()可以设置递推条件：
$ jq -nc '0 | recurse(.+1; . 
下面的例子展示如何定义参数化的命名过滤器，它格式化从2到36含二者的任何底数的整数：
def tobase($b):
def digit: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".:.+1;
def mod: .%$b;
def div: (.-mod)/$b;
def place_values: recurse(select(. >= $b) | div) | mod;
select(2 
这里的tobase($b)充当了库函数，所以为了应对使用者将.作为实际参数传递给它的潜在情况，而对其形式参数采用了变量形式。下面将它保存入tobase.jq文件，接着使用这个函数：
$ echo '15 16' | jq 'include "./tobase"; tobase(16)'
"F"
"10"
下一个例子求解经典的覆面算数学游戏：

从M是在最高位的进位可知：M为1，S为8或9，O为0，代码展示了基于range()和.的生成器：
def send_more_money:
def choose(m; n; k): . as $used

(range(m; n+1)-$used) | $used+{(k):.};
def num: reduce . as $i (0; 10*. + $i);
{"m":1,"o":0}

choose(2; 9; "e")

choose(2; 9; "d")

choose(2; 9; "y") 

select((.s,.e,.n,.d | num) 
+    (.m,.o,.r,.e | num)

**(.m,.o,.n,.e,.y | num))

.s,.e,.n,.d,"+",.m,.o,.r,.e,"=",.m,.o,.n,.e,.y;
send_more_money
将上述代码保存入send_more_money.jq文件，接着使用这个函数得到这个谜题仅有的一个解：
$ jq -nc -f ./send_more_money.jq
9,5,6,7,"+",1,0,8,5,"=",1,0,6,5,2

**解析表达式文法**
在jq和解析表达式文法（PEG）形式化之间有密切关联。这种关联源于下列表格中展示的PEG七个基本运算与jq构造之间的等价性。

**八皇后问题例子**
回溯法求解八皇后问题，采用函数式编程风格的条件表达式和递归函数可以写为：
def queens:
def q(r): . as $pl

if r 
将这段代码保存入queens.jq文件中，下面演示其执行结果并提取其92个解中的前两个解：
$ jq -nc -f ./queens.jq | wc -l
92
$ jq -nc -f ./queens.jq | sed -n '1,2p'
"a1","b7","c5","d8","e2","f4","g6","h3"
"a1","b7","c4","d6","e8","f2","g5","h3"
在能采用可逆（reversible）赋值和静态变量的语言比如Icon的实现中，其3个位置列表共有8+15+15 = 38个元素，而这个存储传递风格实现对3个位置列表进行了zip变换，共有3*8*(1+8)/2 = 108个元素。
进一步采用隐式编程风格，上述代码可以写为：
def queens:
def q(r): . as $pl 

$pl3 as $cl | $cl | . as $c

tostring) as $k0

tostring) as $k1

def place: 
($k0 | in($pl1) | not)
and ($k1 | in($pl2) | not); 
select(place)

$pl0+$c, $pl1+{$k0:null},
$pl2+{$k1:null}, $cl-$c;
def pipeline(i; n): 
q(i) | if i+1 
它还可以采用共递归运算recurse()写为：
def queens:
def q: . as $pl 

$pl4 as $r 

$pl3 as $cl | $cl | . as $c

tostring) as $k0

tostring) as $k1

def place: 
($k0 | in($pl1) | not)
and ($k1 | in($pl2) | not); 
select(place)

$pl0+$c, $pl1+{$k0:null}, 
$pl2+{$k1:null}, $cl-$c, $r+1;
, {}, {}, range(0; 8), 0 | recurse(q) 

select(.4 == 8) | .0

map("abcdefgh".:.+1) | to_entries

sort;
queens
最后，通过旋转和反射将可行解归并为独立解：
def queens(n):
def q: . as $pl 

$pl4 as $r 

$pl3 as $cl | $cl | . as $c

tostring) as $k0

tostring) as $k1

def place: 
($k0 | in($pl1) | not)
and ($k1 | in($pl2) | not); 
select(place)

$pl0+$c, $pl1+{$k0:null},
$pl2+{$k1:null}, $cl-$c, $r+1;
def pipeline(n): 
q | if n > 1 then pipeline(n-1) end;
def toletter: 
"abcdefghijklmnopqrstuvwxyz".:.+1;
def fund_solut(f):
def inverse: . as $xl

reduce range(0; n) as $i
(; .+ index($i));
def variants:
., inverse | map(., reverse)

map(., map(n-1-.)) 

add);
foreach f as $i 
(null, {}; .1 as $ml

variants) as $nl

if all($nl; in($ml) | not) then
$i, ($ml | .$nl=null)
else
null, $ml end;
.0)

select (. != null);
fund_solut(, {}, {}, range(0; n), 0

.0) 

to_entries

sort;
queens(8)
下面演示其执行结果：
$ jq -nc -f ./queens.jq | wc -l
12
$ jq -nc -f ./queens.jq 
"a1","b7","c5","d8","e2","f4","g6","h3"
"a1","b7","c4","d6","e8","f2","g5","h3"
"a6","b1","c5","d2","e8","f3","g7","h4"
"a4","b1","c5","d8","e2","f7","g3","h6"
"a5","b1","c8","d4","e2","f7","g3","h6"
"a3","b1","c7","d5","e8","f2","g4","h6"
"a5","b1","c4","d6","e8","f2","g7","h3"
"a7","b1","c3","d8","e6","f4","g2","h5"
"a5","b1","c8","d6","e3","f7","g2","h4"
"a5","b3","c1","d7","e2","f8","g6","h4"
"a5","b7","c1","d4","e2","f8","g6","h3"
"a6","b3","c1","d8","e4","f2","g7","h5"

**注释**

**参考书目**
 Cite book |last=Janssens |first=Jeroen |url=https://books.google.com/books?id=-hg-EAAAQBAJ |title=Data Science at the Command Line |date=2021 |publisher=O'Reilly Media |isbn=9781492087885 |language=en- Cite book |last=Janssens |first=Jeroen |url=https://books.google.com/books?id=EsWeBAAAQBAJ |title=Data Science at the Command Line: Facing the Future with Time-Tested Tools |date=2014 |publisher=O'Reilly Media |isbn=9781491947807 |language=en- Cite book |last=Marrs |first=Tom |url=https://books.google.com/books?id=e6woDwAAQBAJ |title=JSON at Work: Practical Data Integration for the Web |date=2017 |publisher=O'Reilly Media |isbn=9781491982419 |language=en

**引用**

**外部链接**
  jq homepage date=20250130064115 -  gojq date=20250128111941  - the Pure Go implementation-  jaq date=20250116160055  - the Rust implementation-  jqjq date=20241203073952  - the jq implementation-  jq FAQ date=20250126182040 -  Awesome jq date=20250128110520  - curated listing of jq-related resources-  The jq Programming Language page date=20180620124849  on the Rosetta Code comparative programming tasks project site