【问题标题】:while/until loops in NushellNushell 中的 while/until 循环
【发布时间】:2022-08-19 01:46:08
【问题描述】:
你如何在 Nushell 脚本中执行 while/until 循环?
由于 Nushell 有一个相当出色的表/JSON 解析系统,我一直在尝试通过它使用 Stack Exchange API。
第一个挑战是循环访问来自 API 调用的多个可能的pages 结果。我的(通常是程序性的,有时是 OOP)背景让我在 Nushell 中找到了一个构造,例如:
let page = 1
let re = (http (echo \"/2.3/questions?fromdate=1648771200&todate=1648944000&order=desc&sort=activity&site=askubuntu&page=\" $page) | from json)
let questions = $re.items
while ($re.has_more) {
let page = page + 1
let re = (http (echo \"/2.3/questions?fromdate=1648771200&todate=1648944000&order=desc&sort=activity&site=askubuntu&page=\" $page) | from json)
let questions = $questions | append $re.items
}
... 或等效的 until 构造。
我将如何在 Nushell 中完成此操作?
注意 - 在上面的示例中使用 httpie,因为它会自动处理 Stack API 所需的 gzip 压缩(与 wget 或 Nushell\ 的内部 fetch 命令不同)。
标签:
while-loop
until-loop
nushell
【解决方案1】:
我自己花了很长时间才弄清楚这一点,所以我正在为其他刚接触 Nushell 的人写它(没有双关语,只是一个快乐的意外)。在某些时候,我确信 Nushell 文档会涵盖这一点,但目前还没有。
简短的回答:
使用实现 while/until 循环递归“自定义命令”(又名递归函数)。
细节:
我应该知道得更好,因为我过去曾涉足函数式语言。但重要的是要明白这一点Nushell 的脚本语言是一种函数式语言。回到尝试像 Bash、Zsh、Fish 等那样编写脚本的“习惯”太容易(而且太错误了)。
正如 Nushell Book 在其介绍性 Thinking in Nushell 中所说:
变量是不可变的
在 Nushell 开发的早期,我们决定看看在语言中使用更加以数据为中心的函数式风格可以持续多久。
Nushell 的环境是作用域的
而且,考虑到这一点,程序语言的普通while/until 结构就不是预期的使用像 Nushell 这样的函数式语言。
因此 Nushell 中的基本“while”循环可能类似于:
def wloop [] {
let re = (random bool)
if ($re) {
print $re
wloop
}
}
$ wloop
$ wloop
$ wloop
true
$ wloop
true
true
true
相应的直到循环可能如下所示:
def uloop [] {
let re = (random bool)
print $re
if ($re) { uloop }
}
$ uloop
false
$ uloop
false
$ uloop
true
false
如果您需要修改变量,请记住它的范围仅限于其块,因此您需要将其传递回递归函数。例如,要使用 Stack Exchange API 并更新每个调用的页码:
$ let baseUri = "https://api.stackexchange.com/2.3/questions?fromdate=1648771200&todate=1648944000&order=asc&sort=creation&site=askubuntu&pagesize=100"
$ def getAskUbuntuQuestionPageLoop [ page? ] {
let page = if ( $page == null ) {1} else {$page}
let pageUri = ((echo $baseUri "&page=" $page) | str collect)
let re = (http $pageUri | from json )
if ($re.has_more) {
$re.items | append (getAskUbuntuQuestionPageLoop ($page + 1))
} else {
$re.items
}
}
$ let questions = (getAskUbuntuQuestionPageLoop)
$ $questions | where view_count > 100 && view_count < 110 | select view_count title link
╭───┬────────────┬──────────────────────────────────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ # │ view_count │ title │ link │
├───┼────────────┼──────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 0 │ 103 │ Find reason for "apache2.service: Failed with result 'exit-code'." and │ https://askubuntu.com/questions/1400332/find-reason-for-apache2-service-failed-with-result-exit-code │
│ │ │ "Failed to start The Apache HTTP Server." │ -and-failed-t │
│ 1 │ 103 │ Public folder is forbidden in nginx │ https://askubuntu.com/questions/1400333/public-folder-is-forbidden-in-nginx │
│ 2 │ 101 │ WSL Nano scrolling up to see terminal │ https://askubuntu.com/questions/1400431/wsl-nano-scrolling-up-to-see-terminal │
╰───┴────────────┴──────────────────────────────────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────╯
请注意,未来的每个调用都是appended 到当前结果。
是的,Nushell 实际上漂亮地打印了结果表。
还要注意返回结果必须是函数中执行的最后一条语句。
旁注:个人意见——我设想 Nushell 最终将添加一个 yield 关键字以允许生成器表达式。这将进一步简化上面的示例,允许它位于可以累积结果的reduce 中。