Haskell 单子;货柜;sigfpe和x27的练习;s博客
我正在努力完成“向你学习哈斯凯尔”,现在是关于单子的章节。在其他Haskell相关的帖子中,我看到很多人推荐sigfpe关于monads的博客帖子;强烈建议Haskell的学生进行各种练习,让读者自己“发明”/“发现”单子的概念 我在容器练习的最后一步遇到了问题。我认为链接到他的网站是违反论坛规则的,所以我会尽我所能在这里描述它。(如果我在这方面错了,请恕我直言。)首先,我对这项活动的描述可能并不完全一致,因此一个简单的谷歌搜索可能是最好的:) 概述:Haskell 单子;货柜;sigfpe和x27的练习;s博客,haskell,monads,Haskell,Monads,我正在努力完成“向你学习哈斯凯尔”,现在是关于单子的章节。在其他Haskell相关的帖子中,我看到很多人推荐sigfpe关于monads的博客帖子;强烈建议Haskell的学生进行各种练习,让读者自己“发明”/“发现”单子的概念 我在容器练习的最后一步遇到了问题。我认为链接到他的网站是违反论坛规则的,所以我会尽我所能在这里描述它。(如果我在这方面错了,请恕我直言。)首先,我对这项活动的描述可能并不完全一致,因此一个简单的谷歌搜索可能是最好的:) 概述: 每一组练习都会引导读者完成构建不同类型单
- 每一组练习都会引导读者完成构建不同类型单子的步骤。这些步骤的概述如下:
我们考虑了两个函数-<代码> f′<代码>和<代码> g′/代码>,它们具有相同的类型声明 - 我们构造了一个函数(
),它允许我们组合bind
和f
,这样输出就有意义了(一旦您看到下面的具体练习,可能会更有意义)。直接合成不起作用,因为g
和f
的返回类型与其参数类型不匹配g
- 接下来,我们定义一个标识函数(
),以确保以下内容是正确的:单元
(bind unit.f')==(bind f.unit)
- 基于上一步,我们定义了一个
函数,使得lift
lift f=unit。f
- 在最后一步中,我们被要求证明以下是正确的:
bind(提升f)(提升g)=提升。绑定f g
-
我们要求考虑两个函数:代码>SqRT 和<代码> CBRT ,它计算复数的平方根和立方根(即A+BI形式的数目,其中A和B是实数,而I是负的平方根)。基础数学并不重要。重要的是,可能的第n个根的数目是n。换句话说,复数(即a+bi形式)有两个复数的平方根,三个复数的立方根,等等
- 考虑到复数根的性质,
和sqrt'
都接受类型为cbrt'
的参数,并返回类型为complex
。我们被要求构造一个[complex]
函数,它允许我们计算复数的第六个根,同时利用我们已经有了sqrt'和cbrt'这一事实。(直接合成obv不起作用)bind
bind::(复数->[Complex)->[Complex]->[Complex] 绑定f=(concat.map f)
- 接下来,我们构造
和单元
:升降机
unit::Complex->[Complex] 单位x=[x]
lift::(复杂->[Complex])->复杂->[Complex] 提升f=单位f
- 在最后一步中(这就是我遇到的问题),我们被要求展示以下内容:
bind(提升f)(提升g)=提升。绑定f g
lift
不能接受[Complex]
类型的参数吗?退一步,我不知道我们为什么还要定义unit
和lift
。(我天真的自己认为,bind
的定义解决了眼前的问题,因此接下来的问题就来了。)如果有人能帮助我理解为什么我们定义这两个函数,然后试图证明最后的等式,我将不胜感激
作为参考,我在下面附上我的代码。请注意,
bind
、unit
和lift
函数具有通用类型声明
bind :: (a1 -> [a]) -> [a1] -> [a]
bind f' = (concat . map f')
unit :: t -> [t]
unit x = [x]
lift :: (a -> b) -> a -> [b]
lift f = unit . f
--Definitions of cbRootC and sqRootC
data Complex = Complex Float Float deriving (Show)
cbrt' = rootC 3
sqrt' = rootC 2
rootC :: Float -> Complex -> [Complex]
rootC n (Complex a b) = zipWith Complex r i
where r = map (* (mod ** (1/n) )) $ map cos $ map arg [0..n-1]
i = map (* (mod ** (1/n) )) $ map sin $ map arg [0..n-1]
arg = ( * (2*pi / n) )
mod = sqrt (a*a + b*b)
您的第一个误解是在
lift
类型中。在对问题的描述中,您将其列为以下第一项,但在代码中,您有第二项
lift :: (Complex -> [Complex]) -> Complex -> [Complex]
lift :: (a -> b ) -> a -> [b]
请注意,第二个定义如何在第一个参数的返回类型周围不包含[]
。第二个定义是正确的。lift
将获取一个普通函数Complex->Complex
,并从中生成一个“多值”计算Complex->[Complex]
,对于多个值,它只返回从普通函数返回的单个值
你的第二个误解与如何使用*
和
有关。*
用于组合“多值”计算;f*g=bind f.g
用于普通函数组合
你没有被要求展示这一点
bind (lift f) (lift g) == lift . bind f g
没有进行类型检查是正确的。让我们试着做练习。bind
应用于两个参数返回[a]
lift
应用于单个参数返回a1->[b]
bind (lift f) (lift g) == lift . bind f g
[a] ~ a1 -> [b]
无法选择a
、a1
和b
,这将使列表[]
和函数->
具有相同的类型
而是要求您显示以下内容。请注意不同的符号*
和
。我们将用绑定和
替换*
lift f * lift g == lift (f . g)
bind (lift f) . (lift g) == lift (f . g)
我把剩下的练习留给你
unit
和lift
之所以有用,是因为它们允许您重用您已有的普通类型的东西。lift
将普通函数转换为“多值”计算,unit
将普通值转换为“多值”结果计算。感谢您的详细审查和回复。我确实没有看到