开发者

Haskell - mapping the odd placed values and the even placed values differently

开发者 https://www.devze.com 2023-01-01 09:15 出处:网络
is there an easy way. To take a list of numbers, say 123456. Then multiply the odd placed by three and the even placed by 1.

is there an easy way. To take a list of numbers, say 123456. Then multiply the odd placed by three and the even placed by 1.

i.e. (1 * 3) + (2 * 1) + (3 * 3) + (4*1) + (5*3) + (6*1)

i was thinking the map function somewhere along the lines. But i don't know how to map *3 to just the odd placed values. Oh and if you could give me the version not in prelude that would be great like the actual function or functions, as if its being imported from a开发者_如何学JAVAn external haskell file

Thanks for the help


Okay, as I wrote in a comment earlier, zipWith (*) (cycle [3,1]) xs is what you're looking for. But first, a minor nitpick: the head of the list I would call the zeroth element, that's why I have switched the 1 and 3 around :-)

Let's go through a simple example; let xs be [9,8,7,3,2]. cycle [3,1] just repeats its argument over and over so that will be an infinite list starting with [3,1,3,1,3,1,..]. What zipWith f xs ys does is take the head element of xs and the head element of ys and apply f (which should be a function of two arguments) to those elements - the result of f then goes onto the front of the result of zipWith. If one of xs or ys becomes empty, we're done; otherwise we just keep going.

So the first element of the result will be (3 * 9), then (1 * 8), (3 * 7), (1 * 3), (3 * 2) and we're done!

You can have a look at the definition of zipWith here.

If you really don't want to use the predefined functions, you could define an 'alternating map' taking two functions instead of one, applying the first of these to the head of your argument list and switching the functions around on the recursive call. I'll let you figure out the details there...


What about this, assuming xs is your list of numbers:

map (uncurry ($)) (zip (cycle [((*) 1), ((*) 3)]) xs)

Here's how this works:

[((*) 1), ((*) 3)] is a list containing two functions. The first multiplies a number by one; the second multiplies a number by three.

cycle [...] creates an infinite list of those two functions, repeating one after the other, [×1, ×3, ×1, ×3 ...]

zip (cycle [...]) xs takes your numbers and pairs them with the functions. So if xs is [1..6] then you get [(×1, 1), (×3, 2), (×1, 3), (×3, 4), (×1, 5), (×3, 6)].

The $ :: (a -> b) -> a -> b function is a combinator; it takes a function a→b and applies it to a value. So map (uncurry ($)) (zip ...) takes the list of (function, number) pairs and applies the function to the number. You need uncurry because the list contains pairs, so the signature of the function to map needs to be ((a -> b), a) -> b.

This leaves you with the resulting list; in this case [1, 6, 3, 12, 5, 18].


My first thought was to use an accumulating map, using the accumulating parameter to flag whether it's on an 'even' or 'odd' cycle. I see nobody else has done that so let's see how it goes...

import Data.List (mapAccumL)

yourFunc = snd . mapAccumL mult True -- 'True' represents an odd index, starting from 1
  where
    mult True  x = (False, 3 * x)
    mult False x = (True , x)


If you want to do this point-free style, there's no better way than using cycle and some form of zip. (Point-free means, roughly, "without naming let-bound or lambda-bound variables.") I have two questions:

  • How many different ways will you need to generalize this computation?
  • How familiar are your readers with cycle and other lesser-known list functions (there must be a hundred list functions)?

The more general your problem and the more familiar your readers, the more likely I am to recommend point-free style. For a one-off solution aimed at readers not very familiar with Haskell, I might try a recursive function:

mult31 [] = 0
mult31 [x:x':xs] = x * 3 + x' * 1 + mult31 xs
mult31 [x] = x * 3

Or if you want to get clever, you can use a pair of alternating functions:

mult31 = m3
  where m3 (x:xs) = x * 3 + m1 xs
        m3 []     = 0
        m1 (x:xs) = x * 1 + m3 xs
        m1 []     = 0

Either of these functions will seem less natural to a Haskell veteran than something that uses a zip function with cycle, but for someone just starting out, they may be easier to follow.


You can still use map, just use zip first:

let list' = zip [1..] list
in map (\(cnt,val) -> if odd cnt then val * 3 else val) list'

Above you can see how to make a result dependent on the index as well as the value at that index. This is a fairly general solution that, by replacing the lambda (first argument of map), you can make many variants. The more specialized solution using cycle and zipWith is much shorter and perfectly readable to those comfortable in Haskell.

edited and fleshed out after the statement this is not homework.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号