地图,过滤器,折叠等?我们真的需要在Erlang中使用这些吗?

时间:2014-09-19 11:35:49

标签: erlang

地图,过滤器,折叠等:http://learnyousomeerlang.com/higher-order-functions#maps-filters-folds

我读的越多,我就越困惑。

任何正文可以帮助 简化 这些概念吗?

我无法理解这些概念的重要性。 用例 需要这些吗?

我认为这主要是因为语法,diff来查找流程。

2 个答案:

答案 0 :(得分:6)

函数式编程中普遍存在的映射,过滤和折叠的概念实际上是对数据集合执行的不同操作的简化或刻板印象。在命令式语言中,您通常使用循环执行这些操作。

我们以map为例。这三个循环都采用一系列元素并返回元素的正方形序列:

// C - a lot of bookkeeping
int data[] = {1,2,3,4,5};
int squares_1_to_5[sizeof(data) / sizeof(data[0])];
for (int i = 0; i < sizeof(data) / sizeof(data[0]); ++i)
    squares_1_to_5[i] = data[i] * data[i];

// C++11 - less bookkeeping, still not obvious
std::vec<int> data{1,2,3,4,5};
std::vec<int> squares_1_to_5;
for (auto i = begin(data); i < end(data); i++)
    squares_1_to_5.push_back((*i) * (*i));

// Python - quite readable, though still not obvious
data = [1,2,3,4,5]
squares_1_to_5 = []
for x in data:
    squares_1_to_5.append(x * x)

map的属性是它采用元素的集合并返回相同数量的以某种方式修改的元素。不多也不少。在上面的片段中乍一看是否很明显?不,至少在我们阅读循环体之前。如果循环中有一些if怎么办?让我们拿最后一个例子并稍微修改一下:

data = [1,2,3,4,5]
squares_1_to_5 = []
for x in data:
    if x % 2 == 0:
        squares_1_to_5.append(x * x)

这不再是map,虽然在阅读循环体之前并不明显。结果集合可能比输入集合的元素少(可能没有?),这一点并不清楚。

我们filter编辑了输入集合,仅对输入中的某些元素执行操作。此循环实际上是mapfilter相结合。

由于分配细节(为输出数组分配多少空间?),在C中解决这个问题会更加嘈杂 - 数据操作的核心思想会在所有簿记中被淹没。

fold是最通用的,其中结果不必包含任何输入元素,但不知何故取决于它们(可能只是其中一些)。

让我们重写Erlang中的第一个Python循环:

lists:map(fun (E) -> E * E end, [1,2,3,4,5]).

这是明确的。我们看到一张地图,所以我们知道只要输入,这个调用就会返回一个列表。 第二个:

lists:map(fun (E) -> E * E end,
          lists:filter(fun (E) when E rem 2 == 0 -> true;
                           (_) -> false end,
                       [1,2,3,4,5])).

同样,只要输入filter以某种方式修改每个元素,map最多将返回一个列表。

后者的Erlang示例还显示了另一个有用的属性 - 组合map s,filterfold以表达更复杂的数据转换的能力。使用命令式循环是不可能的。

答案 1 :(得分:2)

它们几乎用在每个应用程序中,因为它们在列表上抽象了不同类型的迭代。

map用于将一个列表转换为另一个列表。可以说,你有关键值元组的列表,你只需要键。你可以写:

keys([]) -> [];
keys([{Key, _Value} | T]) ->
    [Key | keys(T)].

然后你想拥有价值观:

values([]) -> [];
values([{_Key, Value} | T}]) ->
    [Value | values(T)].

或仅列出元组的第三个元素:

third([]) -> [];
third([{_First, _Second, Third} | T]) ->
    [Third | third(T)].

你能看到这种模式吗?唯一的区别在于您从元素中获取的内容,因此您可以简单地编写为一个元素执行的操作而不是重复代码,并使用map

Third = fun({_First, _Second, Third}) -> Third end,
map(Third, List).

这个更短,代码越短,错误就越少。就那么简单。 您不必考虑极端情况(如果列表为空,该怎么办?)对于有经验的开发人员来说,它更容易阅读。

filter搜索列表。你赋予它函数,它接受元素,如果它返回true,元素将在返回的列表上,如果它返回false,元素将不在那里。例如,过滤器从列表中登录用户。

当你在迭代列表时必须进行额外的簿记时,使用

foldlfoldr - 例如对所有元素进行求和或计算某些内容。

关于这些函数的最佳解释是关于Lisp的书:"Structure and Interpretation of Computer Programs""On Lisp" Chapter 4.