Replace a 3 parameter list-comprehension by using map, concat

北城余情 提交于 2019-12-30 14:10:05

问题


I have some understanding of list comprehension. I understand that the expression:

[x * x | x <- [1..10]]
should output [1,4,9,16,25,36,49,64,81,100]

and that the effect of that expression is the same as:

map power [1..10]
power x = x * x

Now, I have to find out the other method (just like the above) for the following function:

[(x,y+z) | x <- [1..10], y <- [1..x], z <- [1..y]]

I can't figure it out by myself without errors, please help me


回答1:


The Haskell Report tells us how to translate list comprehensions:

[ e | True ]         = [e]
[ e | q ]            = [ e | q, True ]
[ e | b, Q ]         = if b then [ e | Q ] else []
[ e | p <- l, Q ]    = let ok p = [ e | Q ]
                           ok _ = []
                       in concatMap ok l
[ e | let decls, Q ] = let decls in [ e | Q ]

Since your list comprehension only uses irrefutable patterns (that is, patterns that never fail), the fourth clause above simplifies somewhat:

[ e | p <- l, Q ]    = concatMap (\p -> [ e | Q ]) l

I'll use this version for concision, but a true derivation should use the definition from the Report. (Homework: try the real translation, and check that you get the "same thing" in the end.) Let's try it, shall we?

  [(x,y+z) | x <- [1..10], y <- [1..x], z <- [1..y]]
= concatMap (\x -> [(x,y+z) | y <- [1..x], z <- [1..y]] [1..10]
= concatMap (\x -> concatMap (\y -> [(x,y+z) | z <- [1..y]]) [1..x]) [1..10]
= concatMap (\x -> concatMap (\y -> [(x,y+z) | z <- [1..y], True]) [1..x]) [1..10]
= concatMap (\x -> concatMap (\y -> concatMap (\z -> [(x,y+z) | True]) [1..y]) [1..x]) [1..10]
= concatMap (\x -> concatMap (\y -> concatMap (\z -> [(x,y+z)]) [1..y]) [1..x]) [1..10]

And we're finally at a version that has no list comprehensions.

If you're comfortable with monads, then you can also gain insight into the behavior of this expression by observing that concatMap is a flipped version of the list's (>>=) function; moreover, [e] is like the list's return e. So, rewriting with monad operators:

= [1..10] >>= \x ->
  [1..x]  >>= \y ->
  [1..y]  >>= \z ->
  return (x,y+z)



回答2:


[(x,y+z) | x <- [1..10], y <- [1..x], z <- [1..y]]

is the same as

concatMap
    (\x -> [(x,y+z) | y <- [1..x], z <- [1..y]])
    [1..10]

You can extract the y and z variables out of the list comprehension similarly. (But you must do it in order from left to right: so y next and z last.)

concatMap is a function defined in the Prelude:

concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f = concat . map f



回答3:


You can transform this into do-notation:

foo = do x <- [1..10]
         y <- [1..x]
         z <- [1..y]
         return (x, y+z)

This works because list is a monad. The do-notation itself is just syntactic sugar for a monadic calculation. Following the desugaring rules (described here under "Desugaring of do blocks") you end up with:

[1..10] >>= (\x -> [1..x] >>= (\y -> [1..y] >>= (\z -> [(x,y+z)])))

The operator >>= is defined in Control.Monad, and is equivalent to a concatMap with flipped arguments for lists. return t is just [t] in case of lists.



来源:https://stackoverflow.com/questions/7819801/replace-a-3-parameter-list-comprehension-by-using-map-concat

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!