在类别理论中,filter
操作是否被视为态射?如果是,那是什么样的态射?示例(在Scala中)
val myNums: Seq[Int] = Seq(-1, 3, -4, 2)
myNums.filter(_ > 0)
// Seq[Int] = List(3, 2) // result = subset, same type
myNums.filter(_ > -99)
// Seq[Int] = List(-1, 3, -4, 2) // result = identical than original
myNums.filter(_ > 99)
// Seq[Int] = List() // result = empty, same type
答案 0 :(得分:12)
在这个答案中,我假设您在filter
上讨论Set
(其他数据类型的情况似乎更加混乱)。
让我们先解决我们所说的问题。我将具体谈谈以下函数(在Scala中):
def filter[A](p: A => Boolean): Set[A] => Set[A] =
s => s filter p
当我们以这种方式写下来时,我们清楚地看到它是一个带有类型参数A
的多态函数,它将谓词A => Boolean
映射到将Set[A]
映射到其他Set[A]
的函数。 }。为了使它成为“态射”,我们必须首先找到一些类别,其中这个东西可能是“态射”。人们可能希望它是自然变换,因此“默认环境类别 - 结构”中的endofunctor类别中的态射通常被称为“Hask
”(或“Scal
”?“{ {1}}“?)。为了表明它是自然的,我们必须检查下面的图表是否为每个Scala
通勤:
f: B => A
然而,在这里,我们立刻失败了,因为它不清楚甚至放在底部的水平箭头上,因为作业 - o f
Hom[A, Boolean] ---------------------> Hom[B, Boolean]
| |
| |
| |
| filter[A] | filter[B]
| |
V ??? V
Hom[Set[A], Set[A]] ---------------> Hom[Set[B], Set[B]]
甚至看起来都不是很有趣(出于同样的原因{{1} }不是函数,请参阅here和here)。
我在这里看到的修复类型A -> Hom[Set[A], Set[A]]
的唯一“分类”结构如下:
A -> End[A]
上的谓词可以被视为具有暗示的部分有序集合,A
如果A
暗示p LEQ q
(即p
必须q
是假的,或p(x)
必须对所有q(x)
}都是真的。x: A
上,我们可以定义一个包含Set[A] => Set[A]
的部分订单,每个集合f LEQ g
保留s: Set[A]
是f(s)
的子集然后g(s)
将是单调的,因此是poset类别之间的函子。但那有点无聊。
当然,对于每个固定的filter[A]
,它(或者更确切地说是它的eta扩展)也只是从A
到A => Boolean
的函数,所以它自动成为“态射” “Set[A] => Set[A]
- 类别”。但那更无聊。
答案 1 :(得分:10)
查看此事的一个有趣方式涉及不选择filter
作为原始概念。有一个名为Filterable
的Haskell类型类is aptly described as:
与
Functor
类似,但[包含]Maybe
效果。正式地,班级
Filterable
代表从 Kleisli可能到 Hask 的仿函数。
"仿函数从Kleisli Maybe
到 Hask "的态射映射由类的mapMaybe
方法捕获,这确实是同名Data.Maybe
函数的推广:
mapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
“阶级法”只是适当的仿函数法(请注意Just
和(<=<)
分别是 Kleisli中的身份和构成>:
mapMaybe Just = id
mapMaybe (g <=< f) = mapMaybe g . mapMaybe f
该课程也可以用catMaybes
...
catMaybes :: Filterable f => f (Maybe a) -> f a
...与mapMaybe
可以相互确定(参见sequenceA
和traverse
之间的类似关系)...
catMaybes = mapMaybe id
mapMaybe g = catMaybes . fmap g
...相当于 Hask endofunctors Compose f Maybe
和f
之间的自然转换。
所有这些与你的问题有什么关系?首先,仿函数是类别之间的态射,自然变换是仿函数之间的态射。既然如此,就可以在less boring than the "morphisms in Hask" one的意义上谈论态射。你不一定想要这样做,但无论如何它都是现有的有利位置。
其次,filter
也是Filterable
的方法,其默认定义是:
filter :: Filterable f => (a -> Bool) -> f a -> f a
filter p = mapMaybe $ \a -> if p a then Just a else Nothing
或者,使用another cute combinator拼写它:
filter p = mapMaybe (ensure p)
在这个特定的分类概念中间接地给filter
一个位置。
答案 2 :(得分:7)
filter
可以用foldRight
编写为:
filter p ys = foldRight(nil)( (x, xs) => if (p(x)) x::xs else xs ) ys
列表上的 foldRight
是T-algebras的映射(这里T是List数据类型仿函数),因此filter
是T-algebras的映射。
这里讨论的两个代数是初始列表代数
[nil, cons]: 1 + A x List(A) ----> List(A)
并且,让我们说&#34;过滤器&#34;代数,
[nil, f]: 1 + A x List(A) ----> List(A)
其中f(x, xs) = if p(x) x::xs else xs
。
在这种情况下,让filter(p, _)
从初始代数到过滤器代数的唯一映射(在一般情况下称为fold
)。它是代数映射的事实意味着满足以下等式:
filter(p, nil) = nil
filter(p, x::xs) = f(x, filter(p, xs))
答案 3 :(得分:6)
要回答这样的问题,我想首先了解过滤的本质是什么。
例如,输入是一个列表是否重要?你能过滤一棵树吗?我不明白为什么不!您将谓词应用于树的每个节点,并丢弃未通过测试的节点。
但结果的形状是什么?节点删除并不总是被定义或者它是模糊的。你可以返回一个清单。但为什么要列出?任何支持追加的数据结构都可行。您还需要数据结构的空成员来启动附加过程。所以任何单位岩浆都会这样做。如果你坚持相关性,你会得到一个幺半群。回顾filter
的定义,结果是一个列表,这确实是一个幺半群。所以我们走在正确的轨道上。
所以filter
只是一个特殊情况,称为Foldable
:一种数据结构,您可以在将结果累积到一个幺半群时折叠。特别是,您可以使用谓词输出单例列表,如果它是真的;或者是一个空列表(标识元素),如果它是假的。
如果你想要一个绝对的答案,那么折叠就是一个变形现象的例子,这是代数范畴中的态射的一个例子。您正在折叠的(递归)数据结构(列表,在filter
的情况下)是某个仿函数的初始代数(在本例中为列表仿函数),并且您的谓词用于为这个仿函数定义一个代数。