Damaon
Damaon

Reputation: 612

Nested loop equivalent

I want to do a list of concatenations in Haskell. I have [1,2,3] and [4,5,6] and i want to produce [14,15,16,24,25,26,34,35,36]. I know I can use zipWith or sth, but how to do equivalent of: foreach in first_array foreach in second_array

I guess I have to use map and half curried functions, but can't really make it alone :S

Upvotes: 8

Views: 5943

Answers (6)

Alberto Capitani
Alberto Capitani

Reputation: 1049

The general solution of the concatenation of two lists of integers is this:

concatInt [] xs = xs
concatInt xs [] = xs
concatInt xs ys = [join x y | x <- xs , y <- ys ]
    where
    join x y = firstPart + secondPart
      where
      firstPart = x *  10 ^ lengthSecondPart
      lengthSecondPart = 1 + (truncate $ logBase 10 (fromIntegral y))
      secondPart = y

Example: concatInt [1,2,3] [4,5,6] == [14,15,16,24,25,26,34,35,36]

More complex example: concatInt [0,2,10,1,100,200] [24,2,999,44,3] == [24,2,999,44,3,224,22,2999,244,23,1024,102,10999,1044,103,124,12,1999,144,13,10024,1002,100999,10044,1003,20024,2002,200999,20044,2003]

Upvotes: 0

Nikita Volkov
Nikita Volkov

Reputation: 43310

You can exploit the fact that lists are monads and use the do notation:

do
  a <- [1, 2, 3]
  b <- [4, 5, 6]
  return $ a * 10 + b

You can also exploit the fact that lists are applicative functors (assuming you have Control.Applicative imported):

(+) <$> (*10) <$> [1,2,3] <*> [4,5,6]

Both result in the following:

[14,15,16,24,25,26,34,35,36]

Upvotes: 17

mhitza
mhitza

Reputation: 5715

Because do notation and the list comprehension have been said already. The only other option I know is via the liftM2 combinator from Control.Monad. Which is the exact same thing as the previous two.

liftM2 (\a b -> a * 10 + b) [1..3] [4..6]

Upvotes: 1

Luis Casillas
Luis Casillas

Reputation: 30227

Nested loops correspond to nested uses of map or similar functions. First approximation:

notThereYet :: [[Integer]]
notThereYet = map (\x -> map (\y -> x*10 + y) [4, 5, 6]) [1, 2, 3]

That gives you nested lists, which you can eliminate in two ways. One is to use the concat :: [[a]] -> [a] function:

solution1 :: [Integer]
solution1 = concat (map (\x -> map (\y -> x*10 + y) [4, 5, 6]) [1, 2, 3])

Another is to use this built-in function:

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

Using that:

solution2 :: [Integer]
solution2 = concatMap (\x -> map (\y -> x*10 + y) [4, 5, 6]) [1, 2, 3]

Other people have mentioned list comprehensions and the list monad, but those really bottom down to nested uses of concatMap.

Upvotes: 2

cheecheeo
cheecheeo

Reputation: 682

If you really like seeing for in your code you can also do something like this:

for :: [a] -> (a -> b) -> [b]
for = flip map

nested :: [Integer]
nested = concat nested_list
  where nested_list =
          for [1, 2, 3] (\i ->
            for [4, 5, 6] (\j ->
              i * 10 + j
            )
          )

You could also look into for and Identity for a more idiomatic approach.

Upvotes: 5

zw324
zw324

Reputation: 27190

You could use list comprehension to do it:

[x * 10 + y | x <- [1..3], y <- [4..6]]

In fact this is a direct translation of a nested loop, since the first one is the outer / slower index, and the second one is the faster / inner index.

Upvotes: 22

Related Questions