About
RSS

Bit Focus


Pipeline: 我所希望的列表处理方法

    本文仅表现个人从事程序设计以来的感受和想法, 带有主观色彩, 可以认为结论式的句子均省略了 "我认为" 这样的字眼.

    编程语言中最基本的事情就是数据变换, 比如基本的四则运算, 或是求正弦, 或是将字符串拼接起来, 前面这三种都是针对数据个体的, 而且在不同语言中实现得大同小异. 而对于数据集体的操作, 比如对一个列表进行变换, 各种编程语言会有各自独特的方法, 甚至同一种语言在语言特性升级前后都会有不同, 比如在 C++ 中, 一直以来采用 for 来迭代, 在 C++11 新标准引入了 lambda 之后, std::for_each 就咸鱼翻身变得流行起来; 又比如在 Python 中, 列表迭代采用 for element in list 的方式, 而在引入 yield 之后的版本中, 转为了 for element in generator. 虽然语法没有变, 但是整体思想改变了不少.

    而在这些眼花缭乱迭代方式当中, 程序员究竟想要得到什么? 解决实际问题的过程中, 最常见的操作应当是
  • 映射: 给出一个函数 f 和集合 {x}, 得到新的集合 {f(x)}
  • 过滤: 给出一个函数 g 和集合 {x}, 得到新的集合 {x 当且仅当 g(x) 为真}

    将这些需求提出来之后, 最让我感到有趣的莫过于 Shell 的管道. 比如这样一句
find -type f | awk '{ print "mv", $1, "/tmp/" }'
相当与进行了一次映射, 集合为 find -type f 给出的文件列表, 函数 f 就是 awk 程序. 而这个例子
find -type f | grep wav$
则是一个过滤.
    上面的例子中, 并没有任何迭代的过程, 可以说 Shell 的管道完全抽象了集合迭代, 让对集合的操作变为了对元素个体的操作. 在语言层面上将迭代过程本身封装起来, 这确实是一件很实在的事情. 因此, 我很乐意将这个特性引进到了 Stekin 程序设计语言中.

    在当前的 dev 分支上, Stekin 实现了类似管道的列表操作方式. 具体的语法类似
  • 列表 | return 表达式
  • 列表 | if 布尔表达式
分别表示映射和过滤.
    在 awk 中, 使用 $1 等表示该行的第一个词, 而 $2 表示第二个, 等等. 而在 Stekin 中并没有这个困扰, 因为迭代的列表中元素类型是确定的, 因此直接使用 $element 来表示元素, 如下面的代码
list: [0, 1, 2, 3, 4]
sqr_list: list | return $element * $element
将每一个元素 $element 映射到其平方 $element * $element, 得到的 sqr_list 将是 [0, 1, 4, 9, 16].
    除了在管道中可以使用 $element 引用元素之外, 还可以使用 $index 引用当前元素的索引 (从 0 开始). 不过这种表示法仍然有待商榷, 我曾想过使用如 $$, $@ 这样看起来很 Shell 很 Makefile 的符号, 但那样实在太不直观, 于是使用了 $element 这样直接写单词的方法. 如果你有什么想法或建议, 欢迎在回复中提出来.

    而过滤操作则除了将管道符号 | 之后的 return 关键字换成 if 之外, 它要求接下来的表达式是布尔类型的. 当且仅当这个表达式的值为真时, 则该元素将被添加到结果列表中去, 如
list: [0, 1, 2, 3, 4]
new_list: list | if $element % 3 != 0

Permanent Link: /p/408 Load full text

Post tags:

 Stekin
 List Processing
 Pipeline


. Back to Bit Focus
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site