创建映射操作,以便每个输入元素生成一个或多个输出元素?

时间:2014-12-21 13:04:21

标签: haskell

最近我想弄清楚如何在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初学者怎么能应付这个?

4 个答案:

答案 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],如果您需要使用其他类型,则应使用转换函数