Kotlin :强行 Point-Free

2017/04/26 Kotlin

昨天在学习 Haskell 的过程中了解了什么是 Point-Free 风格,然后瞬间中毒。于是我就打算在 Kotlin 里面模拟一下。

这是 Haskell 中不 Point-Free 的版本:

generate :: (Num a, Enum a) => [a] -> [a]
-- not point free
generate ls = zipWith (*) [0..] ls

引用大魔头在他的 Haskell 教程里面说过的一句话,

两边约掉个参数

于是我们可以写出 Point-Free 的版本:

generate :: (Num a, Enum a) => [a] -> [a]
-- point free
generate = zipWith (*) [0..]

然后我就尝试把它翻译为 Kotlin。

解释

首先向各位看不懂 Haskell 的看官解释一下什么是 Point-Free。

柯里化

先假设 Kotlin 自带柯里化,也就是说,我定义函数

fun a(a: Int,b: Int) = a + b

之后,我可以这样调用

val x = a(1)
val y = x(2)
print(y) // 3

Point-Free

然后我可以这样:

fun plus1(x: Int) = a(1)(x)

对吧,就是一个返回参数+ 1的函数。

这是不 Point-Free 的写法。

fun plus1() = a(1)

这是 Point-Free 的写法。很简单吧。

Kotlin 强行模仿

因为标准库没有zipWith这个函数,所以我们先写一个,然后手动柯里化了:

fun <A, B, C : Any> zipWith(op: (A, B) -> C) = { x: Sequence<A> ->
  { y: Sequence<B> ->
    val iX = x.iterator()
    val iY = y.iterator()
    generateSequence {
      if (iX.hasNext() and iY.hasNext()) op(iX.next(), iY.next())
      else null
    }
  }
}

此处使用了惰性序列Sequence来模拟 Haskell 的惰性求值,然后使用返回带参数的 Lambda 的方式模拟柯里化。

然后写个那个破函数:

fun generate() = zipWith { x: Int, y: Int -> x * y } (
    generateSequence(0, Int::inc)
)

对比 Haskell 的两行:

generate :: (Num a, Enum a) => [a] -> [a]
generate = zipWith (*) [0..]

很简单吧?调用就是这样:

fun main(args: Array<String>) {
  generate()(sequenceOf(1, 1, 2, 3, 5, 8, 13, 21))
      // .forEach(::println)
}

把那行注释取消了就是输出。

对比 Haskell 的调用:

generate [1, 1, 2, 3, 5, 8, 13, 21]

Easy~

完整的代码

我把完整的代码贴到了我的 gist上。

试下调用 gist 的 API (很可能需要翻墙才能看到):


Search

    Post Directory



    如果觉得这篇文章给您带来了收获或者说它值得您这么做,您可以选择对我进行捐助。
    下面是微信支付哟


    本作品由
    Kotlin :强行 Point-Free采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。
    基于 http://ice1000.org/2017/04/26/HaskellPintFree2Kotlin/上的作品创作。
    This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
    知识共享许可协议