ChatGPT 遇上 Groovy™ 一行代码笑话

作者: Paul King

发布时间: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……​


问:为什么程序员总是把圣诞节 🎄 和万圣节 🎃 搞混?

答:因为 12 月 25 日(十进制)和 10 月 31 日(八进制)是一样的。

我们可以用下面的 Groovy 脚本来验证这一点

def christmas = 25 // dec
def halloween = 031 // oct
assert christmas == halloween

问:为什么 Groovy 如此简洁优雅?

答:因为它借鉴了 Go 的一些特性,像 R 一样支持数据科学但更轻量级,融入了 Python 和 Perl 的元素,吸收了 Java 的精华并加入了 C# 的独特之处,最后还带有一点 Ruby 的风味。

我们可以用下面的 Groovy 脚本来验证这一点

assert 'Groovy' == 'Go'.take(1) + 'R'.toLowerCase() +
                   'Python'['Perl'.size()] * 2 +
                   'Java'['C#'.size()] + 'Ruby'[-1]

问:为什么开发人员破产了?

答:因为他的资金缓存(cache)、智能感知(intellisense)和空库存(null stocks)都空了 💰。

我们将 `Expando` 类与 Elvis 运算符和 Groovy truth 结合起来验证这个事实

def money = intelli = new Expando()
def rich = money.cache ?: intelli.cents ?: null?.stocks
assert !rich

问:为什么程序员觉得使用 Groovy 很酷?

答:因为创建一盒“分类”巧克力很容易。

我们创建了一个“可排序的” `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 是时钟上最好的时间,指针朝下。 🕡

让我们创建一个 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

Groovy 能帮我回答“先有鸡还是先有蛋”这个问题吗?

当然,运行这个脚本

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])

问:为什么钻石和狗是人类最好的朋友?

答:因为钻石是永恒的(forever),而狗是毛茸茸的(fur-ever)! 💎 🐕

让我们使用 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]]

问:为什么路虎汽车和路虎 Explore 智能手机相处得如此融洽?

答:因为它们都明白“探索”新领域的重要性,无论是越野还是在线!

谁知道汽车和智能手机有什么关系,但趋势似乎是公司都想涉足这两者。同样,如果你有两个类,它们显然彼此没有任何关系,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

二进制:就像 1、10、11 一样简单

println "Binary: It's as easy as ${(1..3).collect{ Integer.toBinaryString(it) }.join(', ')}"

问:我在羊场上班的第一天,被要求收集 37 只羊?

答:我说 40 只! 🐑 🐏

assert 40 == Math.round(37/10)*10

问:程序员写了什么代码来庆祝 Groovy 的 20 岁生日? 🎂

答:一个“Hip-Hip array”

String[] celebrate = ['hip', 'hip']