user2548080
user2548080

Reputation: 187

Repeatedly call a function: Haskell

Basically, I want to create a function that takes a list of integers and another list (this list can be of any type) and produce another list that has the elements in it from the "other list" at intervals specified by the list of integers. If I input:

ixs [2,3,1] [3,2,1]
[2,1,3]

So far I have:

ix :: Int -> [a] -> a
ix a [] = error "Empty list"
ix 1 (x:xs) = x
ix a (x:xs) = ix (a-1) xs

ixs :: [Int] -> [a] -> [a]
ixs [] _ = [] 
ixs _ [] = []
ixs (x:xs) (y) = ix x y: []

With this code I only get one value returned like so:

ixs [1,2] [2,1]
[2]

How can I call the ix function repeatedly on (x:xs) so that it returns all the values I want?

Edit: I want to do this without using any standard prelude functions. I just want to use recursion.

Upvotes: 2

Views: 683

Answers (3)

J. Abrahamson
J. Abrahamson

Reputation: 74334

This is (almost) a map of an indexing ("getting the value at") of the first list over the second list

import Data.List ((!!))
-- (!!) :: [a] -> Int -> a

ixs :: [Int] -> [b] -> [b]
ixs ary ixes = map (ary !!) ixes

But you also have wraparound when you index a 3-element list by (3 mod 3 = 0), so we ought to just map mod over the indexes

ixs ary ixes = map (ary !!) (map (`mod` length ary) ixes)

And then we can simplify to "pointless style"

ixs ary = map (ary !!) . map (`mod` length ary)

which reads nicely as "map the indices modulo the array length then map the array indexing over the resultant indices". And it gives the right result

> ixs [2,3,1] [3,2,1] 
[2,1,3]

To break down the Prelude function and Data.List function, we have

(!!) :: [b] -> Int -> b
(x:_)  !! 0  = x
(_:xs) !! n
 | n > 0     = xs !! (n-1)
 | otherwise = error "List.(!!): negative argument."
_      !! _  = error "List.(!!): index too large."

and

map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs

Upvotes: 3

sabauma
sabauma

Reputation: 4253

Perhaps something like this

ixs :: [Int] -> [a] -> [a]
ixs idx a = map (`ix` a) idx

What you want to do is map your index function across all the values in the list of indices to index the second list. Note that your ix function is just !! function, but starts indexing from 1 instead of 0.

Upvotes: 0

AndrewC
AndrewC

Reputation: 32455

You could reverse the order of the arguments

ix' :: [a] -> Int -> a
ix' [] a = error "Empty list"
ix' (x:xs) 1 = x
ix' (x:xs) a = ix' xs (a-1)

to make it easier to map ix over a list of indeces:

ixs' :: [a] -> [Int] -> [a]
ixs' xs is = map (ix' xs) is

Like this:

> ixs' "Hello Mum" [1,5,6,1,5,6,1,5]
"Ho Ho Ho"

but it would be nicer to use flip to swap the arguments - ix' is just flip ix, so you could do

ixs :: [Int] -> [a] -> [a]
ixs is xs = map (flip ix xs) is

which you then call the way round you'd planned:

> ixs [1,5,6,1,5,6,1,5] "Hello Mum"
"Ho Ho Ho"

Upvotes: 0

Related Questions