我正在努力学习#34;了解你的Haskell"我现在正在关于monad的章节。在其他与Haskell相关的帖子中,我看到很多人推荐sigfpe在monad上的博客文章;强烈建议Haskell学生通过他的各种练习,让读者发明"发明" /"发现" monads的概念为他/她自己。
我在容器练习的最后一步遇到了麻烦。我认为链接到他的网站会违反论坛规则,所以我会尽力在这里描述它。 (如果我在这方面做错了,请注意。)作为一个抬头,我对练习的描述可能不完全一致,所以一个简单的谷歌搜索可能是最好的:)
概述:
每组练习都会引导读者完成构建不同类型monad的步骤。这些步骤的概述如下:
f'
和g'
- 它们具有相同的类型声明bind
),它允许我们组合f
和g
,使输出有意义(一旦你看到下面的具体练习,可能会更有意义)。直接组合不起作用,因为f
和g
的返回类型与其参数类型不匹配。unit
),以便满足以下条件:(bind unit . f') == (bind f' . unit)
lift
函数,使lift f = unit . f
bind (lift f) (lift g) == lift . bind f g
以下是我正在进行的一系列练习:
sqrt'
和cbrt'
,它们计算复数的平方根和立方根(即a + bi形式的数字,其中a和b是实数,而i是负面的平方根)。基础数学并不重要。重要的是,可能的第n个根的数量是n。换句话说,复数(即a + bi形式)具有复数的两个平方根,复数的三个立方根等。鉴于复杂根的性质,sqrt'
和cbrt'
都采用Complex
类型的参数并返回类型[Complex]
。我们被要求构造一个bind
函数,它允许我们计算复数的第六个根,同时利用我们已经有sqrt'和cbrt'。 (直接作文obv赢了工作)
bind :: (Complex -> [Complex) -> [Complex] -> [Complex]
bind f = (concat . map f)
接下来,我们构建unit
和lift
:
unit :: Complex -> [Complex]
unit x = [x]
lift :: (Complex -> [Complex]) -> Complex -> [Complex]
lift f = unit . f
在最后一步(这就是我遇到问题),我们要求显示以下内容:
bind (lift f) (lift g) == lift . bind 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)
答案 0 :(得分:2)
你的第一个误解是lift
的类型。在问题描述中,您将其列为以下第一个,但在您的代码中,您将获得第二个。
lift :: (Complex -> [Complex]) -> Complex -> [Complex]
lift :: (a -> b ) -> a -> [b]
注意第二个定义如何在第一个参数的返回类型周围包含[]
。第二个是正确的。 lift
将采用普通函数Complex -> Complex
并生成一个"多值"从Complex -> [Complex]
计算,多个值只返回从普通函数返回的单个值。
您的第二个误解与sigfpe's article中*
和.
的使用方式有关。 *
用于撰写"多值"计算在一起; 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
无法选择列表[]
和函数->
具有相同的类型
相反,您需要显示以下内容。请注意不同的符号*
和.
。我们将*
替换为bind
和.
。
lift f * lift g == lift (f . g)
bind (lift f) . (lift g) == lift (f . g)
我将为您留下余下的练习。
unit
和lift
有用的原因是它们允许您重复使用已有的普通类型的东西。 lift
将普通函数转换为"多值"计算和unit
将普通值转换为"多值"的结果。计算