ChatGPT 遇上 Groovy™ 一行代码笑话
发布时间:2023-10-19 06:00PM
我问 ChatGPT 能否将一些与编程相关的简短/一行代码的“老爸笑话”表示为简短/一行代码的 Groovy 脚本。它最初的回答有些平淡,但在经过一番“劝说”和“后期调整”后,这篇博文包含了最终结果。请注意,双关语的质量参差不齐。
但在开始之前,Groovy 是一种出色的语言,可用于与基于云计算的生成式 AI 系统交互。您可以查看以下 GitHub 仓库,其中展示了如何使用 Groovy 和 Micronaut 与 ChatGPT 对话。这个例子又基于 Ken Kousen 在 Java 中的类似例子。查看他关于该主题的精彩 视频。另请查看 Guillaume Laforge 最近关于使用 Groovy 通过其 PaLM 2 API 与 Google 的 Bard 对话以及使用 LangChain4J 的帖子。
话虽如此,我们在这篇博文中只使用了 Web UI。现在,让我们来看看一些 PUNch line……
我们可以用下面的 Groovy 脚本来验证这一点
def christmas = 25 // dec
def halloween = 031 // oct
assert christmas == halloween
我们可以用下面的 Groovy 脚本来验证这一点
assert 'Groovy' == 'Go'.take(1) + 'R'.toLowerCase() +
'Python'['Perl'.size()] * 2 +
'Java'['C#'.size()] + 'Ruby'[-1]
我们将 `Expando` 类与 Elvis 运算符和 Groovy truth 结合起来验证这个事实
def money = intelli = new Expando()
def rich = money.cache ?: intelli.cents ?: null?.stocks
assert !rich
我们创建了一个“可排序的” `Chocolate` 记录,利用了默认参数和声明式增强。然后我们创建了一个 `Box` 类,它委托给 `List`。接着我们创建了一盒巧克力,并使用 `toString()` 方法检查它是否“分类”好了。
@Sortable @ToString(ignoreNulls = true)
record Chocolate(int cocoa, String filling = null) {}
class Box { @Delegate List<Chocolate> contents }
var box = new Box(contents: [
new Chocolate(80),
new Chocolate(50, 'Caramel')
].sort())
assert box.toString() == '[Chocolate(50, Caramel), Chocolate(80)]'
让我们把“melons”(甜瓜)这个词用 Groovy 灵活的索引方式稍微打乱一下,看看能不能得到“lemons”(柠檬)
def dyslexic = 'melons'[2..0,3..-1] == 'lemons'
assert dyslexic
让我们创建一个 6:30 的 `LocalTime` 并检查指针是否朝下。分针在 60 分钟内移动 360 度,因此每分钟移动 6 度。我们可以检查在 6:30 时它是否正好朝下(180°)。时针在 720 分钟(半天)内移动 360 度,所以我们将总分钟数除以 2。总分钟数是小时数乘以 60 加上当前小时的分钟数。在 6:30,我们知道时针将移动到 6 点和 7 点标记之间的一半(15°),但为了我们的测试,我们只需确保它指向下方(我们会在直下方向的 20° 范围内,我们将此指定为一个范围)。这是结果
def (h, m) = LocalTime.of(6, 30)[HOUR_OF_DAY, MINUTE_OF_HOUR]
assert 6 * m == 180L
assert (h * 60 + m).intdiv(2) in 180L..200L
要在 Groovy 中展示这一点,我们应该首先声明我们的无知和冷漠。然后我们计算差异,最后检查我们的断言。
int ignorance, apathy
int know = care = ignorance - apathy
assert !know && !care
当然,运行这个脚本
def (first, second) = ['egg', 'chicken'].shuffled()
println "Which came first, the $first or the $second? The eternal debate!"
它只是以两种不同的顺序提出问题,但我相信你明白我的意思。
既然死亡和税收是确定的,我们应该将关于它们的引述存储在一个不可变结构中。记录提供了浅层不变性,并且非常适合此目的。对于深层不变性,请考虑使用 `@Immutable` AST 转换,因此即使我们的引述存储在一个可变的 ArrayList 中,也无法更改它。这是一个例子
record Quote(String text, String author, int year) { }
var q1 = new Quote('Perfection is immutable. But for things imperfect, change is the way to perfect them.', 'Owen Feltham', 1840)
var q2 = new Quote('Nothing is certain except death and taxes.', 'Benjamin Franklin', 1789)
@Immutable
class FavoriteQuotes { List<Quote> list }
var favorites = new FavoriteQuotes([q1, q2])
让我们使用 traits 来检查钻石和狗作为最好的朋友
trait HasBestFriend {
abstract String friend()
boolean isBestFriend(String candidate) {
friend() == candidate
}
}
class Diamond implements HasBestFriend {
String friend() { 'girl' }
}
class Dog implements HasBestFriend {
String friend() { 'man' }
}
assert ['man', 'girl'].collect{
[new Diamond().isBestFriend(it),
new Dog().isBestFriend(it)]
} == [[false, true], [true, false]]
谁知道汽车和智能手机有什么关系,但趋势似乎是公司都想涉足这两者。同样,如果你有两个类,它们显然彼此没有任何关系,Groovy 的鸭子类型,或者在这种情况下是属性处理,可能会让你比你想象的更容易地将它们一起使用。
import groovy.transform.*
record Smartphone(String make, String model, String color, int year) { }
record Car(String make, String model, String color, int year) { }
def s = new Smartphone('Landrover', 'Explore', 'Black', 2018)
def c = new Car(s.toMap())
assert c.toString() == 'Car[make=Landrover, model=Explore, color=Black, year=2018]'
我们首先展示 Groovy 如何帮助我们找到我们正在寻找的机器人。当有几个克隆体看起来一模一样时,无人机最难找到。我们不会提及克隆人战争!
我们将创建一个打乱顺序的机器人及其克隆体列表,然后搜索我们想要的那些
@AutoClone class Droid { String name }
def r2d2 = new Droid(name: 'R2-D2')
def c3po = new Droid(name: 'C-3PO')
def droids = [r2d2, c3po]
3.times {
droids << r2d2.clone()
droids << c3po.clone()
}
droids.shuffle()
droids.eachWithIndex { droid, index ->
if (droid == r2d2) println "Droid $index is $r2d2.name"
if (droid == c3po) println "Droid $index is $c3po.name"
}
如果我们不想输出找到机器人的索引,我们可以使用另一种表达式来表明我们正在寻找的机器人已经找到
assert droids.any{ it.is(r2d2) }
assert droids.any{ it.is(c3po) }
当然,欧比旺使用了绝地精神控制,我们可以用 Groovy 的元编程来展示,这里是一个类别类。
class JediMindTrick {
static boolean is(Droid d, Droid other) { false }
}
use(JediMindTrick) {
assert !droids.any{ it.is(r2d2) }
assert !droids.any{ it.is(c3po) }
}
如图所示
try {
pet.say('Pieces of 7')
} catch(ParrotyError e) {
var plank = e.stackTrace
plank.walk()
}
根据定义,纯函数对于相同的输入总是返回相同的结果。因此,纯函数的一种潜在优化是缓存给定输入值集的结果。Groovy 提供了 `@Memoized` AST 转换来实现此功能(以及用于闭包的 `.memoized()` 方法调用)。
我们来编写一个 `StringUtil` 类,它有一个*几乎*纯的函数。`bothCases` 方法的返回值就其输入而言是一个纯函数。它返回一个列表,其中包含输入字符串的小写和大写值。它还有一个副作用,即每次调用时都会增加一个计数器;这只是为了让我们了解正在发生什么。
class StringUtil {
static int count = 0
@Memoized
static List<String> bothCases(String s) {
count++
[s.toLowerCase(), s.toUpperCase()]
}
}
我们对 `bothCases` 应用 `@Memoized` 注解,这可以实现自动缓存。该注解具有许多可选的注解属性,用于配置缓存行为,但我们只使用默认值。
我们可以看到,对于常量参数(在我们的例子中,“Foo”被调用了两次),第二次调用时没有必要再次调用 `bothCases` 方法,因为我们可以使用前一次调用的缓存值。
assert StringUtil.count == 0
assert StringUtil.bothCases('Foo') == ['foo', 'FOO']
assert StringUtil.count == 1
assert StringUtil.bothCases('Foo') == ['foo', 'FOO']
assert StringUtil.count == 1
println "Binary: It's as easy as ${(1..3).collect{ Integer.toBinaryString(it) }.join(', ')}"
assert 40 == Math.round(37/10)*10
String[] celebrate = ['hip', 'hip']