Is there a standard Elixir function that can insert an element at every nth index of a list?
With a function call and return like:
iex> List.insert_at_every([1,2,3,4,5], 2, Enum.random(["apple","banana"]))
[1, 2, "apple", 3, 4, "apple", 5]
NB the solution proposed by #Dogbert is better by all means, I am posting this for the sake of diversity.
To intersperse the list with a constant value, one might use Enum.chunk_every/2 and Enum.intersperse/2:
iex(1)> [1,2,3,4,5]
...(1)> |> Enum.chunk_every(2)
...(1)> |> Enum.intersperse("banana")
...(1)> |> List.flatten
#⇒ [1, 2, "banana", 3, 4, "banana", 5]
This won’t work if you want to use a function to retrieve an element to intersperse on each iteration. In that case you are to implement the interspersion yourself:
iex(2)> [1,2,3,4,5]
...(2)> |> Enum.chunk_every(2)
...(2)> |> Enum.map(& &1 ++ [Enum.random(~w|banana apple|)])
...(2)> |> List.flatten
#⇒ [1, 2, "banana", 3, 4, "apple", 5, "apple"]
The result above always contains a redundant trailing element, it should be shaved off afterwards:
iex(3) > with [_ | result] <- :lists.reverse([1, 2, "banana", 3, 4, "apple", 5, "apple"]) do
...(3)> :lists.reverse(result)
...(3)> end
#⇒ [1, 2, "banana", 3, 4, "banana", 5]
There isn't a built in function for this as far as I know, but this can be done using Enum.with_index and Enum.flat_map. When the remainder of the index and the second argument is the second argument minus 1, we insert the element + the callback into the resulting list, and just the element otherwise. I think it makes more sense that passing 1 as the every argument results in your example list instead of 2. You can simply change every with every - 1 if you want though.
defmodule A do
def insert_at_every(list, every, fun) do
list
|> Enum.with_index
|> Enum.flat_map(fn {x, i} ->
if rem(i, every) == every - 1 do
[x, fun.()]
else
[x]
end
end)
end
end
IO.inspect A.insert_at_every([1, 2, 3, 4, 5], 1, fn -> Enum.random(["apple", "banana"]) end)
IO.inspect A.insert_at_every([1, 2, 3, 4, 5], 2, fn -> Enum.random(["apple", "banana"]) end)
IO.inspect A.insert_at_every([1, 2, 3, 4, 5], 3, fn -> Enum.random(["apple", "banana"]) end)
Output:
[1, "apple", 2, "apple", 3, "banana", 4, "banana", 5, "apple"]
[1, 2, "apple", 3, 4, "apple", 5]
[1, 2, 3, "apple", 4, 5]
Related
I'd like to create a predicate arrayLEQ(L,N) that is true when L = [1,2,3,...,N].
I tried to do it recursively:
arrayLEQ(L,N) :- arrayLEQ([],L,1,N).
arrayLEQ(L1,L2,N,N) :- append(L1,[N],L2).
arrayLEQ(L1,L2,F,N) :- Fnext is F+1, append(L1,[F],L1n), arrayLEQ(L1n,L2,Fnext,N).
At first I thought that it will work, but sadly it doesn't.
When I do:
?- arrayLEQ(L,5) I get L = [1,2,3,4,5] which is the right answer, but Prolog is ready to look for another answer, which is not wanted.
Would you mind explaining to me what I did wrong and why Prolog tries to look for another answer to this predicate, even if it doesn't exist.
Let's have a look at tracer after the first query succeed:
?- arrayLEQ(L,5).
L = [1,2,3,4,5].
more
Redo:arrayLEQ([1, 2, 3, 4], _5040, 5, 5)
Call:_5844 is 5+1
Exit:6 is 5+1
Call:lists:append([1, 2, 3, 4], [5], _5854)
Exit:lists:append([1, 2, 3, 4], [5], [1, 2, 3, 4, 5])
Call:...
Call:_5880 is 6+1
Exit:7 is 6+1
Call:lists:append([1, 2, 3, 4, 5], [6], _5890)
Exit:lists:append([1, 2, 3, 4, 5], [6], [1, 2, 3, 4, 5, 6])
Call:arrayLEQ([1, 2, 3, 4, 5, 6], _5040, 7, 5)
Call:_5922 is 7+1
Exit:8 is 7+1
Call:lists:append([1, 2, 3, 4, 5, 6], [7], _5932)
Exit:lists:append([1, 2, 3, 4, 5, 6], [7], [1, 2, 3, 4, 5, 6, 7])
Call:arrayLEQ([1, 2, 3, 4, 5, 6, 7], _5040, 8, 5)
Call:_5970 is 8+1
Exit:9 is 8+1
and so on...
You can see that your program keeps adding element into the list, without stopping. So there are two solutions:
Adding a cut (!): arrayLEQ(L1,L2,N,N):- !, append(L1,[N],L2). It works but maybe (in my opinion) there is a better solution.
When adding an element, you don't check if you have already passed the threshod you set (in this case 5). So you just have to add F < N before doing Fnext is F+1. So: arrayLEQ(L1,L2,F,N) :- F < N, Fnext is F+1, append(L1,[F],L1n), arrayLEQ(L1n,L2,Fnext,N). (personally i prefer this solution)
So the query now (with second solution):
?- arrayLEQ(L,5).
L = [1, 2, 3, 4, 5].
more.
false.
I suggest you to not use append/3 because in this case is not necessary at all and write someting like this:
orderedList(L,N):-
orderedList(L,1,N).
orderedList([N],N,N). %you can add a cut ! here, to avoid further search
orderedList([H|T],C,N):-
C < N,
H is C,
C1 is C+1,
orderedList(T,C1,N).
?- orderedList(L,5).
L = [1, 2, 3, 4, 5]
more
false
Then if you need to return an empty list, wou can add a predicate to handle this case easily... BTW check also the question linked in the comments by #repeat
Hello I am working with some lists I have the following list:
a = [1,2,3,4,5,6]
I would like to get the these two lists from a:
b = [2,3,4,5,6]
c = [1,2,3,4,5]
I would like to get the firt one removing the first element of a and the second one removing the last element of a, I tried:
b = a
c = a
b.pop(0)
c.pop(len(a)-1)
print(b)
print(c)
print(a)
However the output is:
[2, 3, 4, 5]
[2, 3, 4, 5]
[2, 3, 4, 5]
that is affecting my fist list, I am not sure about what I am doing, I would like to appreciate support with this.
You should not modify the original list - it's simpler than you think, just slice the input list passing the right indexes. Try this:
a = [1, 2, 3, 4, 5, 6]
b = a[1:]
c = a[:-1]
a
=> [1, 2, 3, 4, 5, 6]
b
=> [2, 3, 4, 5, 6]
c
=> [1, 2, 3, 4, 5]
I think slicing would be ok:
b =a[1:];
c =a[:-1]
For example:
Given list: [1, 2, 3, 4, 4, 8, 4]
Choose element, which last duplicate will be removed: 4
Output list: [1, 2, 3, 4, 4, 8]
I tried that way, but it just removes first needed element from list:
def list = [1, 2, 3, 4, 4, 8, 4]
def fruit = list.find { item -> item.equals(4)}
list.remove(fruit) //removes first matching item (one)
println list
I guess init(), last() or maybe toUnique() functions may be useful, but don't know, how to do this..
not sure if this is what you mean, but:
def list = [1, 2, 3, 4, 4, 8, 4]
def toremove = 4
list.remove(list.lastIndexOf(toremove))
assert list == [1, 2, 3, 4, 4, 8]
It will need to get the next imput and output:
pospair [1, 3, 9, 2, 5, 7, 1, 11]
[1, 9, 5, 1]
posimpair [1, 3, 9, 2, 5, 7, 1, 11]
[3, 2, 7, 11]
This is the way to obtain the element on the specified index:
show_in_index::Ord a=>[a]->Int->a
show_in_index l n = l!!n
It shows a result like this:
*Main> show_in_index [1,4,2,7,9] 3
7
The most simple way to do this is using recursion:
pospair :: [a] -> [a]
pospair xs = aux xs [] True
where
aux [] acc _ = acc
aux (y:ys) acc True = aux ys (acc ++ [y]) False
aux (y:ys) acc False = aux ys acc True
Note how I use True or False value to keep track of what value to eliminate. If it's False, I don't include the value in acc (accumulator). If it's True, I include the value. Using the same idea, you can implement posimpair.
You could map the function for indexing
For pospair the following works:
map ([1, 3, 9, 2, 5, 7, 1, 11] !! ) [0,2..length [1, 3, 9, 2, 5, 7, 1, 11]-1]
For posimpair we only have to change the second list that is the one that holds the indexing numbers, previously we had the series of pairs and now we want the series of impairs, so instead of having 0,2,.. until the length of the list -1 we have to do it with 1,3,..until the length of the list-1.
map ([1, 3, 9, 2, 5, 7, 1, 11] !! ) [1,3..length [1, 3, 9, 2, 5, 7, 1, 11]-1]
The general implementation is
pospairs = map (list !!) [0,2..length list - 1]
posimpairs = map (list !!) [1,3..length list - 1]
I tested your example and works.
I want a function which takes the product of the inits of a list and duplicates its elements.
For example the list is: [2, 3, 4, 5].
The product of its inits : [1, 2, 6, 24, 120].
At the end the list should look like this: [1, 1, 2, 2, 2, 6, 6, 6, 6, 24, 24, 24, 24, 24].
My problem is that the [1, 2, 6, 24, 120] should not vary, but i can't solve it, I'm pretty new to haskell. You don't need to modify this code, you can make a new one.
makeSystem :: Integral a => [a] -> [a]
makeSystem l= replicate (l !! 0) ((map product(inits l))!!0) ++ asd (tail l) where
inits [] = [[]]
inits (x:xs) = [[]] ++ map (x:) (inits xs)
An other example: makeSystem [5,2,5,2,5,2]
The result: [1, 1, 1, 1, 1, 5, 5, 10, 10, 10, 10, 10, 50, 50, 100, 100, 100, 100, 100, 500, 500]
For the first part, you can use the standard function scanl:
> scanl (*) 1 [2, 3, 4, 5]
[1,2,6,24,120]
For the second part, zipWith with replicate gets us most of the way there:
> zipWith replicate [2, 3, 4, 5] [1, 2, 6, 24, 120]
[[1,1],[2,2,2],[6,6,6,6],[24,24,24,24,24]]
then we just need to concat these lists.
Putting it all together:
> let makeSystem xs = concat $ zipWith replicate xs (scanl (*) 1 xs)
> makeSystem [2, 3, 4, 5]
[1,1,2,2,2,6,6,6,6,24,24,24,24,24]
> makeSystem [5, 2, 5, 2, 5, 2]
[1,1,1,1,1,5,5,10,10,10,10,10,50,50,100,100,100,100,100,500,500]