地图功能的后置条件

时间:2014-11-25 14:51:26

标签: f# functional-programming functor

地图函数(Seq.map,List.map等)是否具有隐含的后置条件,即输出与输入具有相同数量的项目?更进一步,如果我们有某种Tree.map函数,是否假设输入和输出树的“形状”是相同的?

我问的原因是我总是做出这样的假设(我怀疑很多代码映射的代码也是如此),但后来我发现如果映射,Set.map可以返回一个较小的集合函数产生重复。因此,我的假设无效,或者Set不应被视为用于映射目的的序列。这是什么?

5 个答案:

答案 0 :(得分:4)

很多观点,这是我的:

我们可以将所有map函数/方法视为Haskell的fmap Functors的特定情况。从该定义我们可以假设结构将被保留(加上一些其他有趣的属性)。

但是在.NET中没有类型类,所以我们可以定义map来限制Functors'结果是一些Functor属性不会被保留,但是因为有没有影响影响的通用代码是有限的。

所以没有什么能阻止我们定义map

  • 设置(限制:该功能必须是' a->' b当' a和' b:比较且应该是injective
  • 字符串(限制:函数应为char-> char)
  • Nullables(限制:功能应该是' a->' b其中' b不是参考类型)

请注意,在某些情况下,在类型和值级别都存在限制,例如对于集合,类型级别的限制是两种类型' a和&b; b应该具有比较而对函数的限制值是函数应该是injective

如果语言能够表达类型级别约束,则编译器将在不满足这些要求时抛出错误。

对于函数值,没有编译时限制,但如果我们想确保它们是正确的,我们可以创建单元测试。但是如果我们不关心限制这些功能会发生什么呢?

好吧,只要我们了解某些Functor属性将不会被遵守,那么使用地图而不是限制Functor就没有错。

因此,我们可以在排序列表中定义map结构,当然我们不能假设在这些情况下map a >> map b总是等同于map (a >> b) 。这里的限制是函数应该是monotonically increasing

注意:对于Haskell,有一个package带有限制仿函数和一个集合实例

答案 1 :(得分:3)

是的,我希望map函数能够尊重输入的结构(尽管许多实现可能没有明确的测试)。

Set.map的情况下,可以认为给定(参数)map本身的实现是正确的,但参数函数必须是injective 整体映射函数是结构保留的。实际上,对于集合来说,它是两个函数的组合属性。

使用一些验证来包装Set.map很容易,这些验证在应用时测试参数函数的注入性。

答案 2 :(得分:3)

集合有点棘手,因为您只能创建一组可以测试它们是否相等的东西。实际上,F#集实际上表示为树,因此您需要能够比较它们。

这也意味着集合的map函数与列表的map函数不同:

List.map : ('a -> 'b) -> 'a list -> 'b list
Set.map  : ('a -> 'b) -> Set<'a> -> Set<'b>) when 'a : comparison and 'b : comparison

您必须能够比较 'b值的事实解释了为什么集合上的map函数可以比常规map函数做更多的事情列表和序列。所以,它不是普通的地图操作!

(当然,还有其他可能的方法可以在F#中解决这个问题 - map函数可以返回一个空列表 - 但是结果的推断类型将是'c list所以这也是是另一种地图)。

答案 3 :(得分:2)

是的,通常你会期望map保持形状(因此保持大小)。但是对于集合,这显然不能保持一般,因为集合必须遵守一些额外的法则(比如没有重复的元素 - 所以Set.map (f : X -> bool)显然不会保留集合的大小,如果它应用于具有两个以上的集合其中的元素)。

答案 4 :(得分:2)

术语地图源于数学,仅指一个函数。就其本身而言,它并未说明结果的表示方式。我认为答案取决于内容被映射的类型。

  

因此,我的假设无效,或者Set不应被视为用于映射目的的序列。这是什么?

我说这个假设一般无效,但在合理适用的情况下应该适用。例如,如果树需要依赖于包含值的结构,并且一个这样的树映射到另一个树,则无法创建维护树结构的有效结果。

但是,您可以将集合视为用于映射目的的序列,就像可转换为IEnumerable<>的任何内容一样。只需使用Seq.map代替Set.map