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