事先说明,本人刚接触 Groovy 不久,如果有可以优化的地方或者更好的想法,欢迎评论。

昨天看到国外大神写的一篇博客,内容大致是在 Python 中实现 C-style for loop,令人叹为观止。C-style for loop 本身就十分精巧,这激起了我的好奇心,还有没有其他用一种语言的特性来实现另一种语言的语法的例子呢?

于是我瞄上了 Groovy,之前学 Gradle 的时候接触过,了解了 Groovy 的函数如果只有一个参数,调用时不需要加括号。我立刻想到这应该能实现 Python-style for loop。

设想

首先 Python 的 for loop 看起来是这样的:

for a in b:
    ...

forin 是 Groovy 中的关键字,所以用 _for_in 代替。目标是让这段 Groovy 代码运行起来:

_for 's' _in 'abcde' {
    print s + ', '
}

期望输出:

a, b, c, d, e, 

但是我翻了下 Groovy 的语法文档,发现这个大括号是一个 Closure 闭包函数(没错我是现学的),而 'abcde' { println s + ', ' } 啥也不是。只能加个冒号,写成 'abcde': { println s + ', ' },把这一段作为一个映射传进 _in 方法了。

最终期望的代码:

_for 's' _in 'abcde': {
    print s + ', '
}

开搞

边翻文档边查资料边敲代码搞了一个多小时,总算搞出来了。虽然说 Groovy 有些特性非常灵活好用,但是 Java 写习惯了总想用 Java 的语法写 😆。

class _InvokeIn {
    String contextArgName

    _InvokeIn(contextArgName) {
        this.contextArgName = contextArgName
    }

    def _in(LinkedHashMap<Object, Closure> iterable) {
        Map.Entry entry = iterable.entrySet()[0]
        entry.value.setResolveStrategy(Closure.DELEGATE_FIRST)
        Map map = [:]
        entry.value.delegate = map
        for (a in entry.key) {
            map.put(contextArgName, a)
            entry.value.call()
        }
    }
}

static def _for(String contextArgName) {
    return new _InvokeIn(contextArgName)
}

_for 'a' _in 'abc': {
    println a + ' --- ' + a.getClass()
}

Gridea 用的库好像没有 Groovy 的语法高亮,所以用了 Java 的

完美运行,虽然编译器找不到变量 a 的定义一直在报警告,但这无伤大雅。真正的不足之处在于这种写法的映射的键只能是字符串,函数调用不让过编译。我写完 range 方法才发现这个坑。还有去不掉的大括号。

2023-02-08