Matlab 中 Data-driven 风格的 API 设计

设计

所谓 data-driven API,指的是用户可以把“操作”作为参数,传入函数,像下面这种:

stream = dataStream('load', 'example.csv');
image = dataStream('get', stream, 1);
newStream = processStream('map', @(im)(sobel(im)));

这是我最近在写的图像数据库读取的工具函数,我把视频或图片文件夹抽象成一个 dataStream,并基于这个 dataStream 完成一系列读写以及图像处理操作(processStream)。

代码中的 “load”(载入),“get”(读取),“map”(...就是你所知道的 map),都代表不同类型的操作,外层的 “dataStream”和 “processStream”仅起到一个入口的作用,它们的内部包含了每个操作的实现。设计 data-driven API 的方法很容易,例如上面的流处理函数:

function varargout = processStream(action, varargin)
    varargout = repmat({[]}, [1 nargout]);
    [varargout{:}] = feval(action, varargin{:});
end

function newstream = map(stream, action, varargin)
    % ......map 的实现(略)
end

通过 varargin 和 varargout 传递可变的输入和输出参数,通过 feval 来调用对应的内部函数(例如map)即可。
至于可变参数的用法,可以参考下面两个文档:

  1. Variable-length output argument list
  2. Variable-length input argument list

优点

Data-driven API 解决了 Matlab 的一个缺点,即一个文件仅能放一个主函数(以及几个用户无法访问的辅助函数)。它使相同功能的函数被良好的组织到了一起,许多图像处理库都采用这种风格的设计。

脱离 Matlab 来看,Data-driven 使得开发者可以在不改变接口的情况下新增功能(例如,只要往 processStream.m 文件里加新的内部函数,就可以自动扩展 processStream 支持的操作);

此外, data-driven 还可以让 API 支持某种程度的 DSL,因为操作的类型是通过字符串描述的,那么我们可以把这些字符串组织成一个文本文件,通过读取并执行这个文件,就能让 API 执行你想要的功能,或者我们可以把所需执行的操作保存到一个数组里,让 API 读取并执行这个数组。

例如,基于 processStream,我设计了一个流水线操作的函数 pipeline.m:

% 像流水线一样处理数据:
% processStream('map',...) >>>> processStream('reduce',....)
% 操作按照 schema 来执行,schema 就像下面这个,是个嵌套的 cell array
% {
%     { 'map'   , 'sobel', [3, 3] },  % 先在流中运行 3x3 的 sobel 算子
%     { 'map'   , 'sharpen' }, % 在上一步的输出中运行锐化算子
%     ..........
% }
% 这个 schema 相当于一个小的 DSL
function result = pipeline(stream, schema)
    for i = 1 : numel(schema)
        args = makeArguments(stream, schema{i});
        result = processStream(args{:});
        stream = result;
    end
end
% 构造 processStream 的输入参数
function args = makeArguments(stream, operations)
    args = {operations{1}, stream, operations{2:end}};
end

总结

我认为 Data-driven API 是 Matlab 中最重要的“设计模式”(如果它有设计模式的话...),通过它,我把图像数据库抽象成数据流,并在数据流上实现了 map, foreach,flatmap,reduce 等等一系列 functional 风格的操作,基于这些操作,我把一个原本很冗长的机器学习训练脚本变得十分简洁。

posted @ 2015-04-05 22:56  何磊  阅读(493)  评论(0编辑  收藏  举报