-7

How can I apply a function to only a single element of a list? Any suggestion?

Example:

let list = [1,2,3,4,3,6]
    function x = x * 2
in ...

I want to apply function only to the first occurance of 3 and stop there.

Output:

List = [1,2,6,4,3,6] -- [1, 2, function 3, 4, 3, 6]
chepner
  • 497,756
  • 71
  • 530
  • 681
JohnnyPire
  • 79
  • 1
  • 1
  • 5

3 Answers3

0

you need to maintain some type of state to indicate the first instance of the value, since map will apply the function to all values.

Perhaps something like this

map (\(b,x) -> if (b) then f x else x) $ markFirst 3 [1,2,3,4,3,6]

and

markFirst :: a -> [a] -> [(Boolean,a)]
markFirst a [] = []
markFirst a (x:xs) | x==a = (True,x): zip (repeat False) xs
                   | otherwise = (False,x): markFirst a xs

I'm sure there is an easier way, but that's the best I came up with at this time on the day before Thanksgiving.

Here is another approach based on the comment below

> let leftap f (x,y) = f x ++ y

leftap (map (\x -> if(x==3) then f x else x)) $ splitAt 3 [1,2,3,4,3,6]
karakfa
  • 66,216
  • 7
  • 41
  • 56
0

To map or not to map, that is the question.

Better not to map.

Why? Because map id == id anyway, and you only want to map through one element, the first one found to be equal to the argument given.

Thus, split the list in two, change the found element, and glue them all back together. Simple.

See: span :: (a -> Bool) -> [a] -> ([a], [a]).

Write: revappend (xs :: [a]) (ys :: [a]) == append (reverse xs) ys, only efficient.

Or fuse all the pieces together into one function. You can code it directly with manual recursion, or using foldr. Remember,

map f xs = foldr (\x r -> f x : r) [] xs

takeWhile p xs = foldr (\x r -> if p x then x : r else []) [] xs

takeUntil p xs = foldr (\x r -> if p x then [x] else x : r) [] xs

filter p xs = foldr (\x r -> if p x then x : r else r) [] xs

duplicate xs = foldr (\x r -> x : x : r) [] xs

mapFirstThat p f xs = -- ... your function

etc. Although, foldr won't be a direct fit, as you need the combining function of the (\x xs r -> ...) variety. That is known as paramorphism, and can be faked by feeding tails xs to the foldr, instead.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
0

You can just create a simple function which multiples a number by two:

times_two :: (Num a) => a -> a
times_two x = x * 2

Then simply search for the specified element in the list, and apply times_two to it. Something like this could work:

map_one_element :: (Eq a, Num a) => a -> (a -> a) -> [a] -> [a]

-- base case
map_one_element _ _ [] = []

-- recursive case
map_one_element x f (y:ys)

    -- ff element is found, apply f to it and add rest of the list normally
    | x == y = f y : ys

    -- first occurence hasnt been found, keep recursing
    | otherwise = y : map_one_element x f ys

Which works as follows:

*Main> map_one_element 3 times_two [1,2,3,4,3,6]
[1,2,6,4,3,6]
RoadRunner
  • 25,803
  • 6
  • 42
  • 75