- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
给定任何容器类型,我们可以形成(以元素为中心的) zipper ,并且知道这个结构是一个 Comonad。最近在 another Stack Overflow question 中详细探讨了以下类型:
data Bin a = Branch (Bin a) a (Bin a) | Leaf a deriving Functor
data Dir = L | R
data Step a = Step a Dir (Bin a) deriving Functor
data Zip a = Zip [Step a] (Bin a) deriving Functor
instance Comonad Zip where ...
Zip
是一个
Comonad
的情况,尽管其实例的构造有点麻烦。也就是说,
Zip
可以完全机械地从
Tree
派生出来,并且(我相信)以这种方式派生的任何类型都是
Comonad
,所以我觉得我们应该是通用的并且可以自动构造这些类型和它们
data Zipper t a = Zipper { diff :: D t a, here :: a }
deriving instance Diff t => Functor (Zipper t)
class (Functor t, Functor (D t)) => Diff t where
data D t :: * -> *
inTo :: t a -> t (Zipper t a)
outOf :: Zipper t a -> t a
instance Diff t => Comonad (Zipper t) where ...
instance Diff Bin where
data D Bin a = DBin { context :: [Step a], descend :: Maybe (Bin a, Bin a) }
...
inTo
/
outOf
签名是否足够?是否还需要其他东西来限制类型?这个例子甚至可能吗?
最佳答案
就像 Chitty-Chitty-Bang-Bang 中的 child 捕手用糖果和玩具引诱 children 被囚禁一样,本科物理的招聘人员喜欢用肥皂泡和回旋镖来愚弄,但是当门砰地关上时,是“对了, children ,是时候学习了关于偏微分!”。我也是。别说我没有警告你。
这是另一个警告:以下代码需要 {-# LANGUAGE KitchenSink #-}
,或者更确切地说
{-# LANGUAGE TypeFamilies, FlexibleContexts, TupleSections, GADTs, DataKinds,
TypeOperators, FlexibleInstances, RankNTypes, ScopedTypeVariables,
StandaloneDeriving, UndecidableInstances #-}
class (Functor f, Functor (DF f)) => Diff1 f where
type DF f :: * -> *
upF :: ZF f x -> f x
downF :: f x -> f (ZF f x)
aroundF :: ZF f x -> ZF f (ZF f x)
data ZF f x = (:<-:) {cxF :: DF f x, elF :: x}
ZF f x
表示一对单孔上下文和孔中的元素。
Diff1
的操作描述了我们可以在 zipper 上进行的导航类型(没有任何“向左”和“向右”的概念,请参见我的
Clowns and Jokers 论文)。我们可以“向上”,通过将元件插入孔中来重新组装结构。我们可以“向下”,找到访问给定结构中元素的各种方法:我们用它的上下文装饰每个元素。我们可以“四处走走”,
aroundF
的类型可能会让你们中的一些人想起
class Functor c => Comonad c where
extract :: c x -> x
duplicate :: c x -> c (c x)
instance Diff1 f => Functor (ZF f) where
fmap f (df :<-: x) = fmap f df :<-: f x
instance Diff1 f => Comonad (ZF f) where
extract = elF
duplicate = aroundF
extract . duplicate == id
fmap extract . duplicate == id
duplicate . duplicate == fmap duplicate . duplicate
fmap extract (downF xs) == xs -- downF decorates the element in position
fmap upF (downF xs) = fmap (const xs) xs -- downF gives the correct context
data KF a x = KF a
instance Functor (KF a) where
fmap f (KF a) = KF a
instance Diff1 (KF a) where
type DF (KF a) = KF Void
upF (KF w :<-: _) = absurd w
downF (KF a) = KF a
aroundF (KF w :<-: _) = absurd w
upF
或
downF
无处可去,我们很容易找到所有的方法都无法到达
downF
。
data IF x = IF x
instance Functor IF where
fmap f (IF x) = IF (f x)
instance Diff1 IF where
type DF IF = KF ()
upF (KF () :<-: x) = IF x
downF (IF x) = IF (KF () :<-: x)
aroundF z@(KF () :<-: x) = KF () :<-: z
downF
找到它,
upF
重新打包它,而
aroundF
只能留在原地。
data (f :+: g) x = LF (f x) | RF (g x)
instance (Functor f, Functor g) => Functor (f :+: g) where
fmap h (LF f) = LF (fmap h f)
fmap h (RF g) = RF (fmap h g)
instance (Diff1 f, Diff1 g) => Diff1 (f :+: g) where
type DF (f :+: g) = DF f :+: DF g
upF (LF f' :<-: x) = LF (upF (f' :<-: x))
upF (RF g' :<-: x) = RF (upF (g' :<-: x))
downF
,我们必须进入标记组件内部的
downF
,然后修复生成的 zipper 以在上下文中显示标记。
downF (LF f) = LF (fmap (\ (f' :<-: x) -> LF f' :<-: x) (downF f))
downF (RF g) = RF (fmap (\ (g' :<-: x) -> RF g' :<-: x) (downF g))
aroundF
,我们剥离标签,弄清楚如何绕过未标记的东西,然后在所有生成的 zipper 中恢复标签。焦点元素
x
被其整个 zipper
z
替换。
aroundF z@(LF f' :<-: (x :: x)) =
LF (fmap (\ (f' :<-: x) -> LF f' :<-: x) . cxF $ aroundF (f' :<-: x :: ZF f x))
:<-: z
aroundF z@(RF g' :<-: (x :: x)) =
RF (fmap (\ (g' :<-: x) -> RF g' :<-: x) . cxF $ aroundF (g' :<-: x :: ZF g x))
:<-: z
ScopedTypeVariables
来消除对
aroundF
的递归调用的歧义。作为类型函数,
DF
不是单射的,因此
f' :: D f x
的事实不足以强制
f' :<-: x :: Z f x
。
data (f :*: g) x = f x :*: g x
instance (Functor f, Functor g) => Functor (f :*: g) where
fmap h (f :*: g) = fmap h f :*: fmap h g
instance (Diff1 f, Diff1 g) => Diff1 (f :*: g) where
type DF (f :*: g) = (DF f :*: g) :+: (f :*: DF g)
upF (LF (f' :*: g) :<-: x) = upF (f' :<-: x) :*: g
upF (RF (f :*: g') :<-: x) = f :*: upF (g' :<-: x)
downF
的工作方式与它对总和的处理方式类似,除了我们必须修复 zipper 上下文,不仅要使用标签(以显示我们走的方向),还要使用未触及的其他组件。
downF (f :*: g)
= fmap (\ (f' :<-: x) -> LF (f' :*: g) :<-: x) (downF f)
:*: fmap (\ (g' :<-: x) -> RF (f :*: g') :<-: x) (downF g)
aroundF
是一大堆笑声。无论我们目前访问哪一边,我们有两个选择:
aroundF
移动到那一侧。 upF
移出该侧,将 downF
移至另一侧。 aroundF z@(LF (f' :*: g) :<-: (x :: x)) =
LF (fmap (\ (f' :<-: x) -> LF (f' :*: g) :<-: x)
(cxF $ aroundF (f' :<-: x :: ZF f x))
:*: fmap (\ (g' :<-: x) -> RF (f :*: g') :<-: x) (downF g))
:<-: z
where f = upF (f' :<-: x)
aroundF z@(RF (f :*: g') :<-: (x :: x)) =
RF (fmap (\ (f' :<-: x) -> LF (f' :*: g) :<-: x) (downF f) :*:
fmap (\ (g' :<-: x) -> RF (f :*: g') :<-: x)
(cxF $ aroundF (g' :<-: x :: ZF g x)))
:<-: z
where g = upF (g' :<-: x)
deriving Show
,然后扔进去
deriving instance (Show (DF f x), Show x) => Show (ZF f x)
> downF (IF 1 :*: IF 2)
IF (LF (KF () :*: IF 2) :<-: 1) :*: IF (RF (IF 1 :*: KF ()) :<-: 2)
> fmap aroundF it
IF (LF (KF () :*: IF (RF (IF 1 :*: KF ()) :<-: 2)) :<-: (LF (KF () :*: IF 2) :<-: 1))
:*:
IF (RF (IF (LF (KF () :*: IF 2) :<-: 1) :*: KF ()) :<-: (RF (IF 1 :*: KF ()) :<-: 2))
Bifunctor
,正如现有的关于数据类型泛型编程的文献(参见 Patrik Jansson 和 Johan Jeuring 的著作,或 Jeremy Gibbons 的优秀讲义)详细解释了一个具有两个参数的类型构造函数,对应于两种子结构。我们应该能够“映射”两者。
class Bifunctor b where
bimap :: (x -> x') -> (y -> y') -> b x y -> b x' y'
Bifunctor
s 来给出递归容器的节点结构。每个节点都有子节点和元素。这些可以只是两种子结构。
data Mu b y = In (b (Mu b y) y)
b
的第一个参数中“打上递归结”,并将参数
y
保留在第二个参数中。因此,我们一劳永逸地获得
instance Bifunctor b => Functor (Mu b) where
fmap f (In b) = In (bimap (fmap f) f b)
Bifunctor
实例。
newtype K a x y = K a
instance Bifunctor (K a) where
bimap f g (K a) = K a
data Var = X | Y
data V :: Var -> * -> * -> * where
XX :: x -> V X x y
YY :: y -> V Y x y
V X x y
是
x
的副本,
V Y x y
是
y
的副本。因此
instance Bifunctor (V v) where
bimap f g (XX x) = XX (f x)
bimap f g (YY y) = YY (g y)
data (:++:) f g x y = L (f x y) | R (g x y) deriving Show
instance (Bifunctor b, Bifunctor c) => Bifunctor (b :++: c) where
bimap f g (L b) = L (bimap f g b)
bimap f g (R b) = R (bimap f g b)
data (:**:) f g x y = f x y :**: g x y deriving Show
instance (Bifunctor b, Bifunctor c) => Bifunctor (b :**: c) where
bimap f g (b :**: c) = bimap f g b :**: bimap f g c
List = Mu (K () :++: (V Y :**: V X))
Bin = Mu (V Y :**: (K () :++: (V X :**: V X)))
Mu b
是可微的?我们需要证明
b
在两个变量中都是可微的。铛!是时候学习偏微分了。
data Vary :: Var -> * where
VX :: Vary X
VY :: Vary Y
class (Bifunctor b, Bifunctor (D b X), Bifunctor (D b Y)) => Diff2 b where
type D b (v :: Var) :: * -> * -> *
up :: Vary v -> Z b v x y -> b x y
down :: b x y -> b (Z b X x y) (Z b Y x y)
around :: Vary v -> Z b v x y -> Z b v (Z b X x y) (Z b Y x y)
data Z b v x y = (:<-) {cxZ :: D b v x y, elZ :: V v x y}
D
操作需要知道要针对哪个变量。对应的 zipper
Z b v
告诉我们哪个变量
v
必须在焦点上。当我们“用上下文装饰”时,我们必须用
x
-contexts 和
X
-elements 装饰
y
-elements 和 0x251812231314312431343433433434313431343243134313431343141-elements但除此之外,这是同一个故事。
Y
允许我们建立
Diff2 b
。
instance Diff2 (K a) where
type D (K a) v = K Void
up _ (K q :<- _) = absurd q
down (K a) = K a
around _ (K q :<- _) = absurd q
instance Diff2 (V X) where
type D (V X) X = K ()
type D (V X) Y = K Void
up VX (K () :<- XX x) = XX x
up VY (K q :<- _) = absurd q
down (XX x) = XX (K () :<- XX x)
around VX z@(K () :<- XX x) = K () :<- XX z
around VY (K q :<- _) = absurd q
instance Diff2 (V Y) where
type D (V Y) X = K Void
type D (V Y) Y = K ()
up VX (K q :<- _) = absurd q
up VY (K () :<- YY y) = YY y
down (YY y) = YY (K () :<- YY y)
around VX (K q :<- _) = absurd q
around VY z@(K () :<- YY y) = K () :<- YY z
vV :: Vary v -> Z b v x y -> V v (Z b X x y) (Z b Y x y)
vV VX z = XX z
vV VY z = YY z
Diff1 (Mu b)
和
down
所需的那种“重新标记”。 (当然,我在工作时看到了我需要哪些小工具。)
zimap :: (Bifunctor c) => (forall v. Vary v -> D b v x y -> D b' v x y) ->
c (Z b X x y) (Z b Y x y) -> c (Z b' X x y) (Z b' Y x y)
zimap f = bimap
(\ (d :<- XX x) -> f VX d :<- XX x)
(\ (d :<- YY y) -> f VY d :<- YY y)
dzimap :: (Bifunctor (D c X), Bifunctor (D c Y)) =>
(forall v. Vary v -> D b v x y -> D b' v x y) ->
Vary v -> Z c v (Z b X x y) (Z b Y x y) -> D c v (Z b' X x y) (Z b' Y x y)
dzimap f VX (d :<- _) = bimap
(\ (d :<- XX x) -> f VX d :<- XX x)
(\ (d :<- YY y) -> f VY d :<- YY y)
d
dzimap f VY (d :<- _) = bimap
(\ (d :<- XX x) -> f VX d :<- XX x)
(\ (d :<- YY y) -> f VY d :<- YY y)
d
instance (Diff2 b, Diff2 c) => Diff2 (b :++: c) where
type D (b :++: c) v = D b v :++: D c v
up v (L b' :<- vv) = L (up v (b' :<- vv))
down (L b) = L (zimap (const L) (down b))
down (R c) = R (zimap (const R) (down c))
around v z@(L b' :<- vv :: Z (b :++: c) v x y)
= L (dzimap (const L) v ba) :<- vV v z
where ba = around v (b' :<- vv :: Z b v x y)
around v z@(R c' :<- vv :: Z (b :++: c) v x y)
= R (dzimap (const R) v ca) :<- vV v z
where ca = around v (c' :<- vv :: Z c v x y)
instance (Diff2 b, Diff2 c) => Diff2 (b :**: c) where
type D (b :**: c) v = (D b v :**: c) :++: (b :**: D c v)
up v (L (b' :**: c) :<- vv) = up v (b' :<- vv) :**: c
up v (R (b :**: c') :<- vv) = b :**: up v (c' :<- vv)
down (b :**: c) =
zimap (const (L . (:**: c))) (down b) :**: zimap (const (R . (b :**:))) (down c)
around v z@(L (b' :**: c) :<- vv :: Z (b :**: c) v x y)
= L (dzimap (const (L . (:**: c))) v ba :**:
zimap (const (R . (b :**:))) (down c))
:<- vV v z where
b = up v (b' :<- vv :: Z b v x y)
ba = around v (b' :<- vv :: Z b v x y)
around v z@(R (b :**: c') :<- vv :: Z (b :**: c) v x y)
= R (zimap (const (L . (:**: c))) (down b):**:
dzimap (const (R . (b :**:))) v ca)
:<- vV v z where
c = up v (c' :<- vv :: Z c v x y)
ca = around v (c' :<- vv :: Z c v x y)
around
作为 stub ,并在一个地方(在任何给定的时间)引入了一个故意的类型错误,我想要从类型检查器。即使在 Haskell 中,您也可以将类型检查作为视频游戏体验。
undefined
相对于
b
的偏导数告诉我们如何在节点内一步找到子节点,因此我们得到了 zipper 的传统概念。
data MuZpr b y = MuZpr
{ aboveMu :: [D b X (Mu b y) y]
, hereMu :: Mu b y
}
X
位置,我们可以一直放大到根。
muUp :: Diff2 b => MuZpr b y -> Mu b y
muUp (MuZpr {aboveMu = [], hereMu = t}) = t
muUp (MuZpr {aboveMu = (dX : dXs), hereMu = t}) =
muUp (MuZpr {aboveMu = dXs, hereMu = In (up VX (dX :<- XX t))})
X
衍生物的堆栈下。但是该节点中元素的位置由
X
衍生物给出。我们得到
data MuCx b y = MuCx
{ aboveY :: [D b X (Mu b y) y]
, belowY :: D b Y (Mu b y) y
}
instance Diff2 b => Functor (MuCx b) where
fmap f (MuCx { aboveY = dXs, belowY = dY }) = MuCx
{ aboveY = map (bimap (fmap f) f) dXs
, belowY = bimap (fmap f) f dY
}
instance Diff2 b => Diff1 (Mu b) where
type DF (Mu b) = MuCx b
zAboveY :: ZF (Mu b) y -> [D b X (Mu b y) y] -- the stack of `X`-derivatives above me
zAboveY (d :<-: y) = aboveY d
zZipY :: ZF (Mu b) y -> Z b Y (Mu b y) y -- the `Y`-zipper where I am
zZipY (d :<-: y) = belowY d :<- YY y
upF z = muUp (MuZpr {aboveMu = zAboveY z, hereMu = In (up VY (zZipY z))})
downF = yOnDown []
Y
的辅助函数:
yOnDown :: Diff2 b => [D b X (Mu b y) y] -> Mu b y -> Mu b (ZF (Mu b) y)
yOnDown dXs (In b) = In (contextualize dXs (down b))
down
只带我们进入节点。我们需要的 zipper 也必须携带节点的上下文。这就是
down b
所做的:
contextualize :: (Bifunctor c, Diff2 b) =>
[D b X (Mu b y) y] ->
c (Z b X (Mu b y) y) (Z b Y (Mu b y) y) ->
c (Mu b (ZF (Mu b) y)) (ZF (Mu b) y)
contextualize dXs = bimap
(\ (dX :<- XX t) -> yOnDown (dX : dXs) t)
(\ (dY :<- YY y) -> MuCx {aboveY = dXs, belowY = dY} :<-: y)
contextualise
位置,我们必须给出一个元素 zipper ,所以我们知道整个上下文
Y
回到根,以及描述元素如何位于其节点中的
dXs
是很好的。对于每个
dY
位置,还有一个进一步的子树需要探索,所以我们增加堆栈并继续前进!
aroundF z@(MuCx {aboveY = dXs, belowY = dY} :<-: _) = MuCx
{ aboveY = yOnUp dXs (In (up VY (zZipY z)))
, belowY = contextualize dXs (cxZ $ around VY (zZipY z))
} :<-: z
X
部分,我们看看在现有节点中我们还能去哪里:我们会找到替代元素
belowY
-positions 或进一步的
Y
-subnodes 来探索,所以我们 0x23413418对于
X
部分,我们必须在重新组装我们正在访问的节点后,以自己的方式备份
contextualise
衍生物的堆栈。
yOnUp :: Diff2 b => [D b X (Mu b y) y] -> Mu b y ->
[D b X (Mu b (ZF (Mu b) y)) (ZF (Mu b) y)]
yOnUp [] t = []
yOnUp (dX : dXs) (t :: Mu b y)
= contextualize dXs (cxZ $ around VX (dX :<- XX t))
: yOnUp dXs (In (up VX (dX :<- XX t)))
aboveY
的其他地方,要么继续向上。
X
为您提供事物,而
around
探索上下文以寻找其他事物以进行语境化。如果我们有合适的节点微分结构,我们就可以开发整棵树的微分结构。
f :: (i -> *) -> (o -> *)
extract
种不同类型的结构来存储
duplicate
种不同类型的元素。这些在雅可比结构下是封闭的
J f :: (i -> *) -> ((o, i) -> *)
o
-结构都是偏导数,告诉您如何在
i
-结构中制作
(o, i)
-element-hole。但这又是依赖类型的乐趣。
关于haskell - zipper 共轭,一般来说,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25554062/
给定一种支持通过列表进行迭代的编程语言,即 for element in list do ... 如果我们有一个将动态数量的列表作为输入的程序,list[1] ... list[n](其中 n
我有一个小难题。众所周知,按类型定义变量并避免使用变体是最明显的性能技巧。问题是我正在尝试编写一个可以处理隐式类型参数(基本上是变体)的例程库。 举个例子: Sub Test(A As String)
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 8 年前。 Improve this ques
我目前正在使用 Java,所以我更想知道就效率而言,直接插入它是否更好。尽管我也很好奇这是否是一种不好的做法。 最佳答案 不需要。 API 会告诉您它是否已经存在(如果您需要知道),并且 Collec
假设我需要完成三项任务。第一个选项是这样的: void doAllStuffInOneFunc() { //code block for task 1 ... ...
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
我不确定这是最好的例子,但这里是。假设我想在 Android 应用程序中将对象绘制到 Canvas 上。我需要做一些数学运算并调用一个方法来返回屏幕大小以确定在哪里绘制它,因为我的应用程序应该支持多个
在下面的选项中,有没有正确的方法来处理使用条件变量时的虚假唤醒? 1) 使用 bool 值将 wait(unique_lock_ul) 放入无限的 while 循环中 unique_lock ul(m
我遇到了连接问题:取回太多记录。我在连接集中添加了一个表,并扩展了行数。通常发生这种情况时,我会选择连接中涉及的所有 ID 字段。这样一来,扩展发生的位置就很明显了,我可以更改连接的 ON 来修复它。
我敢肯定,这个问题不仅只有 1 个答案,而且游戏引擎是否真的会更改内存中的 vector ,或使用 gltransformations?因为一直推和弹出矩阵似乎效率低下,但如果你不断修改vertici
我对程序员在 C 和 C++ 中进行不同编码的基本方式有点模糊。特别是一件事是 C++ 中的字符串在 char 数组上的使用,反之亦然。那么,一般来说,我应该使用字符串还是字符数组,为什么? 最佳答案
当我尝试使用接受“通用迭代器”的 ctor 编写 C++ 类模板时,就会出现这个问题。我不知道这里用general这个词是否合适,但我的意思是它可以像STL容器一样接受迭代器。 换句话说,我对迭代器感
修复警告的短期烦恼是否可以从长远来看支付红利?这样做通常可以避免哪些类型的运行时错误? 最佳答案 我的观点是警告是有原因的,忽略它们会带来危险。虽然有些人真的很挑剔,但在大多数情况下,他们这样做是有充
假设第三方 javascript(不是 NodeJS)模块中有这样的函数: Api.IoAsync(parameter, function(err, message) { ... }) 我想将其转换为
给定任何容器类型,我们可以形成(以元素为中心的) zipper ,并且知道这个结构是一个 Comonad。最近在 another Stack Overflow question 中详细探讨了以下类型:
假设我正在构建一个 Python 程序,一开始我导入了两个模块 - 默认情况下包含在 python 库中的 something 和 somethingElse强>,但事实并非如此。 我有两个几乎相同的
我见过的大多数东西都只是使用最大概率,这看起来不错,但并没有给你任何信心的迹象。相对概率也应该很重要,对吧?让我解释一下: 对于二元分类器,假设您的类别是 A 和 B。 P(A) = 0.01、P(B
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 4 年前。 Improv
所以让我们考虑我们有一个父类 class Parent { }; 我有两个 child 类(class) class Child1 : public Parent { void AccessFunc
我刚刚在几个人的帮助下学习外部 CSS。我正在尝试比较两张纸并将其合并为一张,但因为是 2 个人写的,所以我需要整理出他们为 ID 和类提供的名称。一张纸变得非常广泛,但另一张纸有一些我想保留的更新信
我是一名优秀的程序员,十分优秀!