Update list's values - erlang

I have the following setup:
1> rd(rec, {name, value}).
rec
2> L = [#rec{name = a, value = 1}, #rec{name = b, value = 2}, #rec{name = c, value = 3}].
[#rec{name = a,value = 1},
#rec{name = b,value = 2},
#rec{name = c,value = 3}]
3> M = [#rec{name = a, value = 111}, #rec{name = c, value = 333}].
[#rec{name = a,value = 111},#rec{name = c,value = 333}]
The elements in list L are unique based on their name. I also don't know the previous values of the elements in list M. What I am trying to do is to update list L with the values in list M, while keeping the elements of L that are not present in M. I did the following:
update_values([], _M, Acc) ->
Acc;
update_attributes_from_fact([H|T], M, Acc) ->
case [X#rec.value || X <- M, X#rec.name =:= H#rec.name] of
[] ->
update_values(T, M, [H|Acc]);
[NewValue] ->
update_values(T, M, [H#rec{value = NewValue}|Acc])
end.
It does the job but I wonder if there is a simpler method that uses bifs.
Thanks a lot.

There's no existing function that does this for you, since you just want to update the value field rather than replacing the entire record in L (like lists:keyreplace() does). If both L and M can be long, I recommend that if you can, you change L from a list to a dict or gb_tree using #rec.name as key. Then you can loop over M, and for each element in M, look up the correct entry if there is one and write back the updated record. The loop can be written as a fold. Even if you convert the list L to a dict first and convert it back again after the loop, it will be more efficient than the L*M approach. But if M is always short and you don't want to keep L as a dict in the rest of the code, your current approach is good.

Pure list comprehensions solution:
[case [X||X=#rec{name=XN}<-M, XN=:=N] of [] -> Y; [#rec{value =V}|_] -> Y#rec{value=V} end || Y=#rec{name=N} <- L].
little bit more effective using lists:keyfind/3:
[case lists:keyfind(N,#rec.name,M) of false -> Y; #rec{value=V} -> Y#rec{value=V} end || Y=#rec{name=N} <- L].
even more effective for big M:
D = dict:from_list([{X#rec.name, X#rec.value} || X<-M]),
[case dict:find(N,D) of error -> Y; {ok,V} -> Y#rec{value=V} end || Y=#rec{name=N} <- L].
but for really big M this approach can be fastest:
merge_join(lists:keysort(#rec.name, L), lists:ukeysort(#rec.name, M)).
merge_join(L, []) -> L;
merge_join([], _) -> [];
merge_join([#rec{name=N}=Y|L], [#rec{name=N, value=V}|_]=M) -> [Y#rec{value=V}|merge_join(L,M)];
merge_join([#rec{name=NL}=Y|L], [#rec{name=NM}|_]=M) when NL<NM -> [Y|merge_join(L,M)];
merge_join(L, [_|M]) -> merge_join(L, M).

You could use lists:ukeymerge/3:
lists:ukeymerge(#rec.name, M, L).
Which:
returns the sorted list formed by merging TupleList1 and TupleList2.
The merge is performed on the Nth element of each tuple. Both
TupleList1 and TupleList2 must be key-sorted without duplicates prior
to evaluating this function. When two tuples compare equal, the tuple
from TupleList1 is picked and the one from TupleList2 deleted.
A record is a tuple and you can use #rec.name to return the position of the key in a transparent way. Note that I reverted the lists L and M, since the function keeps the value from the first list.

Related

Recursive function in F# that determines in a list of n elements of type int, the greater of two adjacent values

I have recently started learning f# and I have a problem with a task like the one in the subject line. I managed to solve this task but not using a recursive function. I have tried to convert my function to a recursive function but it does not work because in the function I create arrays which elements I then change. Please advise me how to convert my function to a recursive function or how else to perform this task.
let list = [8;4;3;3;5;9;-7]
let comp (a,b) = if a>b then a elif b = a then a else b
let maks (b: _ list) =
let x = b.Length
if x % 2 = 0 then
let tab = Array.create ((x/2)) 0
for i = 0 to (x/2)-1 do
tab.[i] <- (comp(b.Item(2*i),b.Item(2*i+1)))
let newlist = tab |> Array.toList
newlist
else
let tab = Array.create (((x-1)/2)+1) 0
tab.[(((x-1)/2))] <- b.Item(x-1)
for i = 0 to ((x-1)/2)-1 do
tab.[i] <- (comp(b.Item(2*i),b.Item(2*i+1)))
let newlist = tab |> Array.toList
newlist
It is worth noting that, if you were doing this not for learning purposes, there is a nice way of doing this using the chunkBySize function:
list
|> List.chunkBySize 2
|> List.map (fun l -> comp(l.[0], l.[l.Length-1]))
This splits the list into chunks of size at most 2. For each chunk, you can then compare the first element with the last element and that is the result you wanted.
If this is a homework question, I don't want to give away the answer, so consider this pseudocode solution instead:
If the list contains at least two elements:
Answer a new list consisting of:
The greater of the first two elements, followed by
Recursively applying the function to the rest of the list
Else the list contains less than two elements:
Answer the list unchanged
Hint: F#'s pattern matching ability makes this easy to implement.
Thanks to your guidance I managed to create the following function:
let rec maks2 (b: _ list,newlist: _ list,i:int) =
let x = b.Length
if x >= 2 then
if x % 2 = 0 then
if i < ((x/2)-1)+1 then
let d = (porownaj(b.Item(2*i),b.Item(2*i+1)))
let list2 = d::newlist
maks2(b,list2,i+1)
else
newlist
else
if i < ((x/2)-1)+1 then
let d = (porownaj(b.Item(2*i),b.Item(2*i+1)))
let list2 = d::newlist
maks2(b,list2,i+1)
else
let list3 = b.Item(x-1)::newlist
list3
else
b
The function works correctly, it takes as arguments list, empty list and index.
The only problem is that the returned list is reversed, i.e. values that should be at the end are at the beginning. How to add items to the end of the list?
You can use pattern matching to match and check/extract lists in one step.A typical recursive function, would look like:
let rec adjGreater xs =
match xs with
| [] -> []
| [x] -> [x]
| x::y::rest -> (if x >= y then x else y) :: adjGreater rest
It checks wether the list is empty, has one element, or has two elements and the remaining list in rest.
Then it builds a new list by either using x or y as the first element, and then compute the result of the remaing rest recursivly.
This is not tail-recursive. A tail-call optimized version would be, that instead of using the result of the recursive call. You would create a new list, and pass the computed valuke so far, to the recursive function. Usually this way, you want to create a inner recursive loop function.
As you only can add values to the top of a list, you then need to reverse the result of the recursive function like this:
let adjGreater xs =
let rec loop xs result =
match xs with
| [] -> result
| [x] -> x :: result
| x::y::rest -> loop rest ((if x >= y then x else y) :: result)
List.rev (loop xs [])

Remove a single non-unique value from a sequence in F#

I have a sequence of integers representing dice in F#.
In the game in question, the player has a pool of dice and can choose to play one (governed by certain rules) and keep the rest.
If, for example, a player rolls a 6, 6 and a 4 and decides to play one the sixes, is there a simple way to return a sequence with only one 6 removed?
Seq.filter (fun x -> x != 6) dice
removes all of the sixes, not just one.
Non-trivial operations on sequences are painful to work with, since they don't support pattern matching. I think the simplest solution is as follows:
let filterFirst f s =
seq {
let filtered = ref false
for a in s do
if filtered.Value = false && f a then
filtered := true
else yield a
}
So long as the mutable implementation is hidden from the client, it's still functional style ;)
If you're going to store data I would use ResizeArray instead of a Sequence. It has a wealth of functions built in such as the function you asked about. It's simply called Remove. Note: ResizeArray is an abbreviation for the CLI type List.
let test = seq [1; 2; 6; 6; 1; 0]
let a = new ResizeArray<int>(test)
a.Remove 6 |> ignore
Seq.toList a |> printf "%A"
// output
> [1; 2; 6; 1; 0]
Other data type options could be Array
let removeOneFromArray v a =
let i = Array.findIndex ((=)v) a
Array.append a.[..(i-1)] a.[(i+1)..]
or List
let removeOneFromList v l =
let rec remove acc = function
| x::xs when x = v -> List.rev acc # xs
| x::xs -> remove (x::acc) xs
| [] -> acc
remove [] l
the below code will work for a list (so not any seq but it sounds like the sequence your using could be a List)
let rec removeOne value list =
match list with
| head::tail when head = value -> tail
| head::tail -> head::(removeOne value tail)
| _ -> [] //you might wanna fail here since it didn't find value in
//the list
EDIT: code updated based on correct comment below. Thanks P
EDIT: After reading a different answer I thought that a warning would be in order. Don't use the above code for infite sequences but since I guess your players don't have infite dice that should not be a problem but for but for completeness here's an implementation that would work for (almost) any
finite sequence
let rec removeOne value seq acc =
match seq.Any() with
| true when s.First() = value -> seq.Skip(1)
| true -> seq.First()::(removeOne value seq.Skip(1))
| _ -> List.rev acc //you might wanna fail here since it didn't find value in
//the list
However I recommend using the first solution which Im confident will perform better than the latter even if you have to turn a sequence into a list first (at least for small sequences or large sequences with the soughtfor value in the end)
I don't think there is any function that would allow you to directly represent the idea that you want to remove just the first element matching the specified criteria from the list (e.g. something like Seq.removeOne).
You can implement the function in a relatively readable way using Seq.fold (if the sequence of numbers is finite):
let removeOne f l =
Seq.fold (fun (removed, res) v ->
if removed then true, v::res
elif f v then true, res
else false, v::res) (false, []) l
|> snd |> List.rev
> removeOne (fun x -> x = 6) [ 1; 2; 6; 6; 1 ];
val it : int list = [1; 2; 6; 1]
The fold function keeps some state - in this case of type bool * list<'a>. The Boolean flag represents whether we already removed some element and the list is used to accumulate the result (which has to be reversed at the end of processing).
If you need to do this for (possibly) infinite seq<int>, then you'll need to use GetEnumerator directly and implement the code as a recursive sequence expression. This is a bit uglier and it would look like this:
let removeOne f (s:seq<_>) =
// Get enumerator of the input sequence
let en = s.GetEnumerator()
let rec loop() = seq {
// Move to the next element
if en.MoveNext() then
// Is this the element to skip?
if f en.Current then
// Yes - return all remaining elements without filtering
while en.MoveNext() do
yield en.Current
else
// No - return this element and continue looping
yield en.Current
yield! loop() }
loop()
You can try this:
let rec removeFirstOccurrence item screened items =
items |> function
| h::tail -> if h = item
then screened # tail
else tail |> removeFirstOccurrence item (screened # [h])
| _ -> []
Usage:
let updated = products |> removeFirstOccurrence product []

Return value in F# - incomplete construct

I've trying to learn F#. I'm a complete beginner, so this might be a walkover for you guys :)
I have the following function:
let removeEven l =
let n = List.length l;
let list_ = [];
let seq_ = seq { for x in 1..n do if x % 2 <> 0 then yield List.nth l (x-1)}
for x in seq_ do
let list_ = list_ # [x];
list_;
It takes a list, and return a new list containing all the numbers, which is placed at an odd index in the original list, so removeEven [x1;x2;x3] = [x1;x3]
However, I get my already favourite error-message: Incomplete construct at or before this point in expression...
If I add a print to the end of the line, instead of list_:
...
print_any list_;
the problem is fixed. But I do not want to print the list, I want to return it!
What causes this? Why can't I return my list?
To answer your question first, the compiler complains because there is a problem inside the for loop. In F#, let serves to declare values (that are immutable and cannot be changed later in the program). It isn't a statement as in C# - let can be only used as part of another expression. For example:
let n = 10
n + n
Actually means that you want the n symbol to refer to the value 10 in the expression n + n. The problem with your code is that you're using let without any expression (probably because you want to use mutable variables):
for x in seq_ do
let list_ = list_ # [x] // This isn't assignment!
list_
The problematic line is an incomplete expression - using let in this way isn't allowed, because it doesn't contain any expression (the list_ value will not be accessed from any code). You can use mutable variable to correct your code:
let mutable list_ = [] // declared as 'mutable'
let seq_ = seq { for x in 1..n do if x % 2 <> 0 then yield List.nth l (x-1)}
for x in seq_ do
list_ <- list_ # [x] // assignment using '<-'
Now, this should work, but it isn't really functional, because you're using imperative mutation. Moreover, appending elements using # is really inefficient thing to do in functional languages. So, if you want to make your code functional, you'll probably need to use different approach. Both of the other answers show a great approach, although I prefer the example by Joel, because indexing into a list (in the solution by Chaos) also isn't very functional (there is no pointer arithmetic, so it will be also slower).
Probably the most classical functional solution would be to use the List.fold function, which aggregates all elements of the list into a single result, walking from the left to the right:
[1;2;3;4;5]
|> List.fold (fun (flag, res) el ->
if flag then (not flag, el::res) else (not flag, res)) (true, [])
|> snd |> List.rev
Here, the state used during the aggregation is a Boolean flag specifying whether to include the next element (during each step, we flip the flag by returning not flag). The second element is the list aggregated so far (we add element by el::res only when the flag is set. After fold returns, we use snd to get the second element of the tuple (the aggregated list) and reverse it using List.rev, because it was collected in the reversed order (this is more efficient than appending to the end using res#[el]).
Edit: If I understand your requirements correctly, here's a version of your function done functional rather than imperative style, that removes elements with odd indexes.
let removeEven list =
list
|> Seq.mapi (fun i x -> (i, x))
|> Seq.filter (fun (i, x) -> i % 2 = 0)
|> Seq.map snd
|> List.ofSeq
> removeEven ['a'; 'b'; 'c'; 'd'];;
val it : char list = ['a'; 'c']
I think this is what you are looking for.
let removeEven list =
let maxIndex = (List.length list) - 1;
seq { for i in 0..2..maxIndex -> list.[i] }
|> Seq.toList
Tests
val removeEven : 'a list -> 'a list
> removeEven [1;2;3;4;5;6];;
val it : int list = [1; 3; 5]
> removeEven [1;2;3;4;5];;
val it : int list = [1; 3; 5]
> removeEven [1;2;3;4];;
val it : int list = [1; 3]
> removeEven [1;2;3];;
val it : int list = [1; 3]
> removeEven [1;2];;
val it : int list = [1]
> removeEven [1];;
val it : int list = [1]
You can try a pattern-matching approach. I haven't used F# in a while and I can't test things right now, but it would be something like this:
let rec curse sofar ls =
match ls with
| even :: odd :: tl -> curse (even :: sofar) tl
| even :: [] -> curse (even :: sofar) []
| [] -> List.rev sofar
curse [] [ 1; 2; 3; 4; 5 ]
This recursively picks off the even elements. I think. I would probably use Joel Mueller's approach though. I don't remember if there is an index-based filter function, but that would probably be the ideal to use, or to make if it doesn't exist in the libraries.
But in general lists aren't really meant as index-type things. That's what arrays are for. If you consider what kind of algorithm would require a list having its even elements removed, maybe it's possible that in the steps prior to this requirement, the elements can be paired up in tuples, like this:
[ (1,2); (3,4) ]
That would make it trivial to get the even-"indexed" elements out:
thelist |> List.map fst // take first element from each tuple
There's a variety of options if the input list isn't guaranteed to have an even number of elements.
Yet another alternative, which (by my reckoning) is slightly slower than Joel's, but it's shorter :)
let removeEven list =
list
|> Seq.mapi (fun i x -> (i, x))
|> Seq.choose (fun (i,x) -> if i % 2 = 0 then Some(x) else None)
|> List.ofSeq

How to write a function that returns an array of indices of values in array x in array y?

I am trying to write an efficient algorithm that will effectively let me merge data sets (like a sql join). I think I need to use Array.tryFindIndex, but the syntax has me lost.
Based on the data below, I am calling arrX my "host" array, and want to return an int array that has its length, and tells me the positions of each of its elements in arrY (returning -1 if its not in there). (Once I know these indices I can then use them on arrays of data that of length arrY.length)
let arrX= [|"A";"B";"C";"D";"E";"F"|]
let arrY = [|"E";"A";"C"|];
let desiredIndices = [|1; -1; 2; -1; 0; -1|]
It looks like I need to use an option type somehow, and I think a mapi2 in there as well.
Does anyone know how to get this done? (I think it could be a very useful code snippet for people who are merging data sets from different sources)
Thanks!
//This code does not compile, can't figure out what to do here
let d = Array.tryFindIndex (fun x y -> x = y) arrX
The tryFindIndex function searches for a single element in the array specified as the second argument. The lambda function gets only a single parameter and it should return true if the parameter is the element you are looking for. The type signature of the tryFindIndex function shows this:
('a -> bool) -> 'a [] -> int option
(In your example, you're giving it a function that takes two parameters of type 'a -> 'a -> bool, which is incompatible with the expected type). The tryFindIndex function returns an option type, which means that it gives you None if no element matches the predicate, otherwise it gives you Some(idx) containing the index of the found element.
To get the desired array of indices, you need to run tryFindIndex for every element of the input array (arrX). This can be done using the Array.map function. If you want to get -1 if the element wasn't found, you can use pattern matching to convert None to -1 and Some(idx) to idx:
let desired =
arrX |> Array.map (fun x ->
let res = Array.tryFindIndex (fun y -> x = y) arrY
match res with
| None -> -1
| Some idx -> idx)
The same thing can be written using sequence expression (instead of map), which may be more readable:
let desired =
[| for x in arrX do
let res = Array.tryFindIndex (fun y -> x = y) arrY
match res with
| None -> yield -1
| Some idx -> yield idx |]
Anyway, if you need to implement a join-like operation, you can do it more simply using sequence expressions. In the following example, I also added some values (in addition to the string keys), so that you can better see how it works:
let arrX= [|"A",1; "B",2; "C",3; "D",4; "E",5; "F",6|]
let arrY = [|"E",10; "A",20; "C",30|]
[| for x, i in arrX do
for y, j in arrY do
if x = y then
yield x, i, j |]
// Result: [|("A", 1, 20); ("C", 3, 30); ("E", 5, 10)|]
The sequence expression simply loops over all arrX elements and for each of them, it loops over all arrY element. Then it tests whether the keys are the same and if they are, it produces a single element. This isn't particularly efficient, but in most of the cases, it should work fine.
Write a custom function that returns -1 if nothing is found, or returns the index if it's found. Next, use Array.map to map a new array using this function:
let arrX= [|"A";"B";"C";"D";"E";"F"|]
let arrY = [|"E";"A";"C"|];
let indexOrNegativeOne x =
match Array.tryFindIndex (fun y -> y = x) arrY with
| Some(y) -> y
| None -> -1
let desired = arrX |> Array.map indexOrNegativeOne
printfn "%A" desired

lists:usort for nth element in tuple

I need to sort tuples according to the second element of each tuple but apparently usort/1 only works with the first element. So I have to swap the elements, usort them and swap back.Is there an easier way?Also is there a way to sort in descending order (I know sorting and reversing can be done, but just want to know).
Have you tried keysort/2 function (or its counterpart ukeysort/2)?
> lists:reverse(lists:keysort(2, [{a,2}, {b,1}, {c, 3}])).
[{c,3},{a,2},{b,1}]
If you don't sort very big lists this is probably the most readable solution you can get.
Actually, a better answer:
There's a second version of sort that takes a sorting function:
lists:sort(Fun, List1) -> List2
Here's an example that sorts on the second element in a tuple:
lists:sort(fun(A, B) ->
{A1, A2} = A,
{B1, B2} = B,
if
A2 > B2 ->
false;
true ->
true
end
end, YourList).
An improved version of bmdhacks' solution:
lists:sort(fun(A, B) ->
{_, A2} = A,
{_, B2} = B,
A2 =< B2
end, YourList).
Underscores are better then A1 and B1, because the compiler will give warnings
for those.
To sort in descending order, just change <= to >=.
It shouldn't be too hard to write your own sort function (adapted from a common example):
qsort([]) -> [];
qsort([Pivot|Tail]) ->
{PivotFirst, PivotSecond} = Pivot,
qsort([{FirstElement, SecondElement} || {FirstElement,SecondElement} <- Tail, SecondElement < PivotSecond])
++ [Pivot] ++
qsort([{FirstElement, SecondElement} || {FirstElement,SecondElement} <- Tail, SecondElement >= PivotSecond]).

Resources