我写了一个简单的函数
someFunc list elem = do
list <- elem:elem:elem:list
return elem
现在,当我使用它时,我会得到像这样的输出
*Main> someFunc [] 'a'
"aaa"
尽管事实上,这个功能没有实际用途,为什么会发生?为什么编辑列表在elem
中有效?以及如何为list
分配新值以避免这种情况?
答案 0 :(得分:4)
请注意,您的功能将被删除:
someFunc :: [b] -> b -> [b]
someFunc list elem = (elem:elem:elem:list) >>= \list -> return elem
现在请注意list
中的\list -> return elem
与传递给函数的输入list
不同。
现在看看如何定义列表的Monad实例:
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
fail _ = []
因此,您的代码最终会转换为此表单:
someFunc list elem = concat $ map (\list -> return elem) (elem:elem:elem:list)
现在您能理解为什么要获得该输出吗?
someFunc [] 'a'
会像这样应用:
concat $ map (\list -> return 'a') ('a':'a':'a':[])
concat $ [['a'],['a'],['a']]
'a':'a':'a':[]
"aaa"
答案 1 :(得分:2)
您无法为list
分配新值,list
左侧的<-
与{{1}不同,发生了什么?在list
的右侧。如果您使用<-
打开警告,则会看到
-Wall
您没有使用<interactive>:13:19: Warning:
This binding for `elem' shadows the existing binding
imported from `Prelude' (and originally defined in `GHC.List')
<interactive>:14:7: Warning:
This binding for `list' shadows the existing binding
bound at <interactive>:13:14
<interactive>:14:7: Warning: Defined but not used: `list'
定义的名称list
,只是定义它以便碰巧隐藏现有的绑定。
list <- ...
返回someFunc [] 'a'
的原因是由于列表monad的工作原理。这种表示法等同于
"aaa"
对于列表,someFunc list e = (e:e:e:list) >>= \l -> return e
基本上是>>=
,所以你有
concatMap
所以用someFunc list e = concatMap (\l -> return e) (e:e:e:list)
代替[]
和list
代替'a'
我们得到
e
您的困惑可能来自someFunc [] 'a' = concatMap (\l -> return 'a') "aaa"
= concat $ map (\l -> return 'a') "aaa"
= concat [['a'], ['a'], ['a']]
= ['a', 'a', 'a']
= "aaa"
的使用。在大多数语言中return
是关键字,但在Haskell中它只是一个函数。它不会提前退出函数调用,它所做的就是在您所在的monad的上下文中包含一个值。对于列表return
,这是整个定义。此外,在Haskell中,您无法重新分配值,但您可以使用新定义来隐藏它们。如果您始终使用return x = [x]
和-Wall
进行编译,那么您将不会遇到此问题。
答案 2 :(得分:0)
您(显然是偶然)使用列表monad。在Python中,您的代码段将表示为:
for list in [elem, elem, elem] + list:
yield elem
也就是说,您为列表中的每个项目创建一个包含一个elem
的列表,另外还有三个。
以下是您的意图:
someFunc list elem = do
list <- return (elem:elem:elem:list)
return elem
这只会创建一个新的list
变量遮蔽旧的变量,并完全忽略它以返回elem
。