Toolbox languages

Hillel Wayne

工具箱语言

原文由 Hillel Wayne 发布,订阅该博客

工具箱语言是指无需依赖第三方包就能高效解决问题的编程语言。我默认的工具箱语言是 Python 和 shell 脚本,你大概都已经很熟悉了。下面介绍一些我用的比较冷门的选择。

AutoHotKey

必须得有它!AutoHotkey 基本上就是“面向图形界面的 shell 脚本”。它是填补那些不可编程应用空白的绝佳工具,用起来非常顺手。它只支持 Windows,不过 Mac 和 Linux 上也有类似的工具。

实用特性:

  • 可以配置仅在特定程序中生效的快捷键,或根据全局标志是否开启、屏幕上是否出现特定文本等条件来触发。
  • 能轻松调用大量基础的 Win32 功能。比如打开文件选择对话框,只需 f := FileSelect()
  • GUI 框架真的非常、非常好用。说实话,至少对于做些小工具而言,是我用过的所有语言里最好的。

示例:

Audacity 不支持自定义鼠标快捷键,所以我用 AutoHotKey 把中键映射到了一个键盘快捷键上。

#HotIf WinActive("ahk_exe audacity.exe")
  MButton::Send "^l" ; silence selection
#HotIf

我让输入 ;iso 就能自动填入当前日期。

:R:;iso:: 
{
    Send(FormatTime(,"yyyy-MM-dd"))
}

这是我用来做带时间戳笔记的小工具。

; right-ctrl + d
>^d::
{
  TimeString := FormatTime(,"MM/dd hh:mm tt")
  t_msg := InputBox(,TimeString,"w200 h100")
  if t_msg.Result = "OK" {
    timestampfile := A_WorkingDir . "\Config\timestamps.txt"
    FileAppend(TimeString . "`t" . t_msg.Value . "`r`n", timestampfile)
  }
}

其他用途:为各种工具箱语言启动 REPL。输入通关某个游戏所需的那一长串 100 次按键(懂的都懂)。

延伸阅读:

J

一种数组语言,和 APL 类似。做数组运算极其出色,处理字符串或结构化数据时却让人抓狂。我以前用得很多,但现在大多换成了其他工具,比如 Excel 和 Raku。不过在它擅长的领域里,依然非常强大。

实用特性:

  • 极度简洁。在大多数语言里需要好几行的事,在 J 里用几个字符就能搞定,所以我喜欢用它快速做大量数学计算。
  • 一等公民的多维数组。+ 既能把两个数相加,也能对两个数组逐元素相加,还能把一个数加到数组的每个元素上,或把一个数组加到高维数组的每一行(或每一列)上。
  • 有大量顶层原语专门处理各种数学特殊情况,比如把一个数分解成质因数。

示例:

求一个数的所有质因数:

    q: 2520
2 2 2 3 3 5 7

假设有两个进程,各执行一个四步算法,一共有多少种可能的交错执行方式

    ni =: !@:(+/) % */@:!
    ni 4 4
70

如果我想列出一张表,展示 1 到 3 个处理器、每种 1 到 3 步情况下的交错数量呢?

   (ni@:$)"0/~ >: i. 3
1  1    1
2  6   20
6 90 1680

延伸阅读:

Frink

可能是这份清单里最冷门的语言。Frink 专为量纲分析(带单位的数学运算)而设计,但也加入了开发者觉得有趣的各种功能——而且数量还不少!在我见过的所有编程语言里,它大概最接近“更好的计算器”:上手容易、功能强大,又没有 J 或 Raku 那样陌生的语法。

实用特性:

  • 内置了大量单位和单位修饰符。calendaryear 严格等于 365 天,tropicalyear 是 365.24 天,而 half nanocentury 大约是 1.6 秒。
  • 日期字面量表示法:# 2000-01-01 # - # 200 BC # 的结果是 2199.01 年。
  • 内置了用于处理不确定性的区间类型。虽然有点笨拙,但能用。

示例:

如果一个人出生于 1970 年 1 月 1 日午夜,他会在什么时候满十亿秒?

   # 1970 # + 1 billion seconds
AD 2001-09-09 AM 02:46:40.000 (Sun) Central Daylight Time

如果我以一定时间跑完一定距离,平均配速是多少?

   // In miles per hour
   2.5 miles / (27 minutes + 16 seconds) -> mph
5.5012224938875305623

   // In meters per hour
   2.5 miles / (27 minutes + 16 seconds) -> meters / hour
8853.3594132029339854

   // In (minutes, seconds) per mile
   1 / (4.5 miles / hour) -> [minutes/mile, seconds/mile, 0]
13, 19

(6 ± 2) * (8 ± 1) 是多少?

   x = new interval [2, -2]
   (6 + x) * (8 + x/2)
[28, 72] // range is between 28 and 72

延伸阅读:

Raku

Raku(原名 Perl 6)是一门非常古怪的语言,充满了各种黑魔法。它很强大,但也很容易出错。我还不太敢用它来跑生产环境程序。但作为个人脚本和工具箱语言,它非常出色。

实用特性

  • 你可以自定义中缀运算符!还有后缀运算符。以及环缀运算符
  • 大量、多到让我担心的语法糖。比如不用写 [1, 2],可以直接写 <1 2>。不用写 ["a", "bc"],可以直接写 <a bc>。Raku 就是懂™该怎么做。
  • 只要定义一个 MAIN 函数,它的参数就会自动变成命令行参数。
  • 基于运行时值的多方法与多分派。把它和 MAIN 结合起来,写小型命令行工具会非常轻松。
  • 许多数学运算符都有对应的 Unicode 符号(比如用 ∈ 代替 (elem)),和我的各种 AutoHotKey 热字串配合得很好。

示例

生成三个随机的 10 字符小写字符串。

> for ^3 {say ('a'..'z').roll(10).join}
fflqymxapa
znyxehaqvo
qwqxusudqw

文法解析不常见的结构化数据格式(见链接)。

把一堆 SVG 的 id 批量复制到 Inkscape 标签里。

use XML;

my $xml = from-xml-file("file.svg");

for $xml.elements(:NEST, :RECURSE<99>) -> $e {
  with $e<id> ~~ /k\w/ {
    say $_.target;
    $e.set("inkscape:label", $_.target);
  }
}
$xml.save()

编写一个带几种繁琐选项组合的命令行工具(示例)。

延伸阅读:

Picat

我最新的工具箱语言,也是让我开始思考“工具箱”这个概念本身的语言。它是逻辑编程、约束求解和命令式逃生舱的奇妙混合体。我一开始只是把它当作更强的约束求解器来用,后来却不断发现它的新用途。

实用特性:

  • 可以给变量赋值。在一门逻辑语言里,这出奇地有用。很多问题感觉几乎适合用逻辑编程来解决,但总有一小部分算法不知道该怎么用纯逻辑来表达。命令式就提供了我需要的逃生舱。
  • planner 模块。我超爱 planner 模块。它是我最好的朋友。只要给它一个目标和一组可能的动作,Picat 就会找出能达成目标的动作序列。非常酷。

示例:

如果我以 4.5 英里/小时跑 X 分钟,以 5.1 英里/小时跑 Y 分钟,要想在 38 分钟内跑完 3.1 英里,X 和 Y 各应该是多少?

import cp.
[X, Y] :: 1..60, 45*X + 51*Y #= 31*60,  X+Y #= 38, solve([X,Y]).
X = 13
Y = 25
yes

给定一堆活动、时间限制和互斥关系,规划一个度假行程

检查一个逻辑谜题是否有多解。检查谜题的线索是否冗余,或者去掉某一条线索后是否仍能保持唯一解。

快速搭建一个 Petri 网可达性求解器

延伸阅读:

什么样的语言才算是好的工具箱语言?

我见过的大多数优秀工具箱语言都是用于计算的。我觉得用于副作用和自动化的工具箱语言也是可行的(比如 AutoHotKey),只是这个领域还没被充分探索。

一门工具箱语言一定要写起来非常、非常快。至少要比 Python 快。对比一下“生成十对随机数”:

# python

from random import randint
[(randint(10), randint(10)) for _ in range(10)]

# Raku
^10 .roll(2) xx 10

# J
10 2 ?@$ 10

有几个因素决定了这一点:简洁的语法意味着少打字。内置功能多,就不用自己写基础的东西。从标准库导入虽然不算最理想,但可以接受。而需要安装第三方包则让我很烦。Raku 在这方面做得很酷;Rakudo Star Bundle 就预装了一堆实用的社区包。

如果一件事能用一行搞定,你就可以直接丢进 REPL 里跑。所以你会想要一个好用的 REPL。我用的大多数语言 REPL 都不错,不过我想我的 Lisp 和 Smalltalk 读者们对于什么叫“好用的 REPL”应该会有不同看法。

理想情况下,这门语言应该有平缓的上手曲线。Raku 虽然很复杂,但只学一点点就能派上用场,而 J 的学习曲线太陡,不太适合推荐给大多数人。不过,这一点往往会和“写起来快”相冲突。

我还想加入工具箱的其他工具

  • jq,用于处理 JSON
  • JavaScript,这样我就能通过开发者控制台修改别人的网站
  • 某种能提供 J 的好处、但又没有那些一直困扰我的问题的 APL
  • 一种 concatenative 编程语言,如果我能搞清楚 CPL 到底适合解决哪些小问题的话
  • 某种能让网页抓取和解析像计算一样简单的工具。Requests 和 bs4 都不行。

感谢 Saul Pwanson 提供的反馈。如果你喜欢这篇文章,欢迎订阅我的 newsletter!我每周都会在上面发布新文章。

我为企业提供形式化方法培训,帮助软件开发变得更快、更便宜、更安全。了解更多请点击这里

我的新书 Logic for Programmers 现已开启抢先体验!点击这里查看。

本文章由 muse-spark-1.2-contributor 进行翻译

评论