Editing tuples in a list of sml - list

I want to make a list which is of specification :(string*int) list and the tuples can be edited. For example, suppose
val gamma = [("a",20),("b",30),("c",40)] :(string*int) list
Now, how can I change the value 30 in the tuple ("b",30) to , let's say, 70.

You need to map over the list and build a new tuple:
let
fun change key value (k, v) =
if k = key
then (k, value)
else (k, v)
val list = [("a",20),("b",30),("c",40)]
in
List.map (change "b" 70) list
end

Related

Updating a list of 2-tuples in SML

I'm trying to update a running list of 2-tuples in SML. This is the same problem as: How can I update lists in SML using functions?
Unfortunately, the answer doesn't help. First, here is my code:
fun member(a, []) = false
| member((a, b), (c, d)::cds) = a = c orelse member((a, b), cds);
fun update([], (loc, v)) = [(loc, v)]
| update((y, z)::yzs, (loc, v)) =
if member((loc, v), (y, z)::yzs) then
if loc = y then (loc, v)::yzs
else (y, z)::update(yzs, (loc, v))
else (y, z)::yzs#[(loc, v)];
I am able to call the update function on FLR to create new values, but can only update the last value. In addition, when the last value is added and I call the update function again, it has not appended to FLR. Here's my example:
- val FLR = [(1,1),(2,4),(3,9),(4,16),(5,25)];
val FLR = [(1,1),(2,4),(3,9),(4,16),(5,25)] : (int * int) list
- update(FLR, (6,36));
val it = [(1,1),(2,4),(3,9),(4,16),(5,25),(6,36)] : (int * int) list
- update(FLR, (7,42));
val it = [(1,1),(2,4),(3,9),(4,16),(5,25),(7,42)] : (int * int) list
Instead of assigning the value to "it," is there a way for me to assign the value of the new list to FLR? In addition, I'm not able to add new values to an empty list? Any suggestions on that?
As Yawar pointed out, your code seems to work, but the idea of mutating variables to see updates must be replaced with shadowing bindings with newly created values that slightly different.
Your code can be simplified:
fun member ((x,_), xs) = List.exists (fn (y,_) => x = y) xs
fun update ([], (x,v)) = [(x,v)]
| update ((y,w)::pairs, (x,v)) =
if x = y
then (x,v) :: pairs (* replace (y,w) with (x,v), stop *)
else (y,w) :: update (pairs, (x,v)) (* keep (y,w), continue *)
That is, you don't need memberinside update to tell you whether the list needs updating or not, since that will require member to recurse through the list and return true or false with not much else to show for where the insert/replace should occur.
It seems a little asymmetric that member takes the pair first and the list second, while update takes the list first and the pair second. When designing APIs, try and make things consistent.
Doing yzs#[(loc, v)] is bad and probably, in your case, unnecessary. This insertion of a single element will loop through the entire list with the effect of the new pair ending at the end of the list. Instead of (y, z)::yzs#[(loc, v)] you could do (loc, v)::(y, z)::yzs to reduce this to a constant-time operation.

SML - Finding an element in the list and changing it's value

I'm new to SML. I'm writing a function which accepts 2 int and a list of tuples as input:
fun moveBoxL(Xw,Yw,boxes:(int * int)list) =
The length of my list(boxes) can change. I want to find a tuple in the list whose elements are equal to Xw,Yw. For example I want a tuple like box1 whose elements are like this: box1=(Xw,Yw) and if there was such a tuple, I want to change it's value => box1(Xw-1,Yw-1) .
Is there a way?
fun moveBox (x, y, boxes) = List.map (fn p => if p = (x,y) then (x-1,y-1) else p) boxes

SML - changing the value of multiple tuples in the list

I'm new to SML. I'm writing a function which accepts 2 int (x,y) and a list of tuples (named boxes) as input. The length of my list can change. I want to find 2 tuples in the list whose elements depends on x and y. for example I want to check whether there is a tuple like box1=(x,y) and another tuple like box2=(x-2,y-3) and if both of them are available in the list then their values should be changed and returned simultaneously. I know how to find a tuple and change it's value using List.map . but how about updating multiple tuples?
fun move(x,y,boxes:(int * int)list) =
if List.exists (fn s => s = (x,y)) boxes andalso
List.exists (fn p => p = (x-1,y-2)) boxes
then ... (then for example how to change their value to box1=(x-1,y-2)
and box2=(x-3,y-4) at the same time and update them in the list)
List.map (fn p =>
if p = (x, y) then (x-1, y-2)
else if p = (x-2, y-3) then (x-3, y-4)
else p
) boxes

creating a list of lists based on another list content

I have 2 lists. They will always be the same length with respect to each other and might look like this toy example. The actual content is not predictable.
val original = [1, 2, 0, 1, 1, 2]
val elements = ["a","b","c","d","e","f"]
I want to create the following list:
val mappedList = [["c"],["a","d","e"],["b","f"]]
0 1 2
So the pattern is to group elements in the elements list, based on the value of the same-position element in original list. Any idea how can I achieve this in SML? I am not looking for a hard coded solution for this exact data, but a general one.
One way is to first write a function which takes an ordered pair such as (2,"c") and a list of ordered pairs such as
[(3,["a"]),(2,["b"]),(1,["a","e"])]
and returns a modified list with the element tacked onto the appropriate list (or creates a new (key,list) pair if none exists) so that the result would look like:
[(3,["a"]),(2,["c","b"]),(1,["a","e"])]
The following function does the trick:
fun store ((k,v), []) = [(k,[v])]
| store ((k,v), (m,vs)::items) = if k = m
then (m,v::vs)::items
else (m,vs)::store ((k,v) ,items);
Given a list of keys and a corresponding list of values, you could fold this last function over the corresponding zip of the keys and values:
fun group ks vs = foldl store [] (ListPair.zip(ks,vs));
For example, if
val original = [1, 2, 0, 1, 1, 2];
val elements = ["a","b","c","d","e","f"];
- group original elements;
val it = [(1,["e","d","a"]),(2,["f","b"]),(0,["c"])] : (int * string list) list
Note that you could sort this list according to the keys if so desired.
Finally -- if you just want the groups (reversed to match their original order in the list) the following works:
fun groups ks vs = map rev (#2 (ListPair.unzip (group ks vs)));
For example,
- groups original elements;
val it = [["a","d","e"],["b","f"],["c"]] : string list list
On Edit: if you want the final answer to be sorted according to the keys (as opposed to the order in which they appear) you could use #SimonShine 's idea and store the data in sorted order, or you could sort the output of the group function. Somewhat oddly, the SML Standard Basis Library lacks a built-in sort, but the standard implementations have their own sorts (and it is easy enough to write your own). For example, using SML/NJ's sort you could write:
fun sortedGroups ks vs =
let
val g = group ks vs
val s = ListMergeSort.sort (fn ((i,_),(j,_)) => i>j) g
in
map rev (#2 (ListPair.unzip s))
end;
Leading to the expected:
- sortedGroups original elements;
val it = [["c"],["a","d","e"],["b","f"]] : string list list
With the general strategy to first form a list of pairs (k, vs) where k is the value they are grouped by and vs is the elements, one could then extract the elements alone. Since John did this, I'll add two other things you can do:
Assume that original : int list, insert the pairs in sorted order:
fun group ks vs =
let fun insert ((k, v), []) = [(k, [v])]
| insert (k1v as (k1, v), items as ((k2vs as (k2, vs))::rest)) =
case Int.compare (k1, k2) of
LESS => (k1, [v]) :: items
| EQUAL => (k2, v::vs) :: rest
| GREATER => k2vs :: insert (k1v, rest)
fun extract (k, vs) = rev vs
in
map extract (List.foldl insert [] (ListPair.zip (ks, vs)))
end
This produces the same result as your example:
- val mappedList = group original elements;
> val mappedList = [["c"], ["a", "d", "e"], ["b", "f"]] : string list list
I'm a bit unsure if by "The actual content is not predictable." you also mean "The types of original and elements are not known." So:
Assume that original : 'a list and that some cmp : 'a * 'a -> order exists:
fun group cmp ks vs =
let fun insert ((k, v), []) = [(k, [v])]
| insert (k1v as (k1, v), items as ((k2vs as (k2, vs))::rest)) =
case cmp (k1, k2) of
LESS => (k1, [v]) :: items
| EQUAL => (k2, v::vs) :: rest
| GREATER => k2vs :: insert (k1v, rest)
fun extract (k, vs) = rev vs
in
map extract (List.foldl insert [] (ListPair.zip (ks, vs)))
end
Use a tree for storing pairs:
datatype 'a bintree = Empty | Node of 'a bintree * 'a * 'a bintree
(* post-order tree folding *)
fun fold f e Empty = e
| fold f e0 (Node (left, x, right)) =
let val e1 = fold f e0 right
val e2 = f (x, e1)
val e3 = fold f e2 left
in e3 end
fun group cmp ks vs =
let fun insert ((k, v), Empty) = Node (Empty, (k, [v]), Empty)
| insert (k1v as (k1, v), Node (left, k2vs as (k2, vs), right)) =
case cmp (k1, k2) of
LESS => Node (insert (k1v, left), k2vs, right)
| EQUAL => Node (left, (k2, v::vs), right)
| GREATER => Node (left, k2vs, insert (k1v, right))
fun extract ((k, vs), result) = rev vs :: result
in
fold extract [] (List.foldl insert Empty (ListPair.zip (ks, vs)))
end

Zipping on more than 2 lists in sml

I am having a problem with tail in my function.I would like to get rid of the tail but I cannot think of a way to do it.I will be happy if someone may assist me figure a way out.
fun Rollists (x::nil) = [x]
| Rollists (xs) =(map hd xs)::Rollists( map tl xs);
this function,is supposed to output elements from a given list list as pairs from each list,an larger version of ListPair.zip
Generates pairs from a list of lists based upon the first list:
fun generateTuples (lol) =
let
(* Treat the ListOfLists as a database
table, with the first
list being a key value column *)
val keys = hd(lol)
(* and the remaining columns being
additional fields in its tuple *)
val records = tl(lol)
(* Pairs the key with each column
in its record *)
fun formPairs (aKey,listOfRecords) =
if null listOfRecords
then []
else [aKey,hd(hd listOfRecords)]::
(formPairs(aKey,tl(listOfRecords)))
(* Pops a row's data fields from the record *)
fun chopLists (listOfRecords)=
if null listOfRecords
then []
else tl(hd(listOfRecords))::
(chopLists(tl(listOfRecords)))
in
(* Pass the first key value to formPairs
along with all the records. Then pop
the first row off the database and call
generateTuples on the remain *)
if null keys
then []
else generateTuples(tl(keys)::(chopLists(records)))
# formPairs(hd(keys),records)
end
Example:
val list1 = [0,1,2,3,4]
val list2 = [55,66,77,88,99]
val list3 = [10,11,12,13,14]
val list4 = [555,666,777,888,999]
val lols = [list1,list2,list3,list4]
- generateTuples(lols);
val it =
[[4,99],[4,14],[4,999],[3,88],[3,13],[3,888],[2,77],[2,12],[2,777],[1,66],
[1,11],[1,666],...] : int list list