最近我想弄清楚如何在Haskell中做一些编程。
我试图做一些简单的操作。现在我仍然坚持像这个例子中的操作:
input = [1,2,3,4]
output = [1,2,2,3,3,3,4,4,4,4]
也就是说,对于x
中的每个元素input
,在x
中生成x
的{{1}}元素。因此,对于输入中的元素output
,将1
附加到输出。然后,对于输入中的元素[1]
,将元素2
附加到输出。然后,对于元素[2,2]
,请附加3
等。算法应仅适用于标准数字。
我知道这很容易,并且在#34;正常"中执行它是微不足道的。命令式编程,但由于Haskell的功能是无状态的,我在如何处理这个问题时遇到了问题。
任何人都可以给我一些暗示,一个绝对的Haskell初学者怎么能应付这个?
答案 0 :(得分:7)
你刚刚发现了monads!
以下是您正在做的事情的一般概念:
对于输入中的每个a
- 元素(这是一个容器类型M a
,此处为[a]
),您指定一个完整的新容器M b
。但作为最终结果,您只需要一个“扁平”容器M b
。
好吧,我们来看看Monad
类型类的定义:
class (Applicative m) => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
这正是您所需要的。列表是Monad
的实例,因此您可以编写
replicates :: [Int] -> [Int]
replicates l = l >>= \n -> replicate n n
或者,这可以写成
replicates l = do
n <- l
replicate n n
知道可能更容易理解的列表理解可能会很有趣
replicates l = [ n | n <- l, _ <- [1..n] ]
正如chi所建议的,实际上只是另一个monad表达式的语法糖:
[ n | n <- l, _ <- [1..n] ] ≡ l >>= \n -> [1..n] >>= \_ -> return n
...或者至少它曾经是GHC的一些旧版本,我认为它现在使用更优化的列表推导实现。您仍然可以使用-XMonadComprehensions
标记启用该去糖变体。
答案 1 :(得分:5)
另一个解决方案,利用列表推导:
output = [ n | n <- input , m <- [1..n] ]
将上述内容与命令式Python代码进行比较:
for n in input: -- n <- input
for m in range(1,n+1): -- m <- [1..n] (in Python the second extreme is excluded, hence +1)
print n -- the n in [ n | ... ]
请注意,m
未使用 - 在Haskell中,通常可以将其称为_
来表达此信息:
output = [ n | n <- input , _ <- [1..n] ]
答案 2 :(得分:4)
作为初学者,我更容易理解这样的事情:
concat $ map(\ x - &gt;取x $ repeat x)[1,2,3,4]
对于&#34;列为monads&#34;重要的是要知道还有&#34; concat&#34;引擎盖下的操作(在绑定定义中),IMO
答案 3 :(得分:4)
一个简单的解决方案:
rep (x:xs) = replicate x x ++ rep xs
rep [] = []
提示:
replicate 5 "a"
给出["a","a","a","a","a"]
,它对第二个参数中的任何类型都有相同的作用,但第一个参数必须是Int ++
连接两个列表rep
的推断类型为[Int] -> [Int]
,如果您需要使用其他类型,则应使用转换函数