How to use map with two lists? - sml

In SML, how can you use a map function that takes two lists and returns a list of each element in one list multiplied with its corresponding element in the other list?
I tried
fun mult_list v1 v2 = map (fn (x, y) => x * y) (v1, v2);
but it's not working...

You want ListPair.map:
fun mult_list v1 v2 = ListPair.map (fn (x, y) => x * y) (v1, v2)
which, btw, can be abbreviated to
fun mult_list v1 v2 = ListPair.map op* (v1, v2)
You can also use ListPair.mapEq instead, if you want to force an exception when the lists are not of the same length.

Related

Create List by multiplying object with [1..n]

I am still not into Haskell and need a hint for the following function.
I want to create a list by adding each multiplication of a pair:
all :: Int -> (Int,Int) -> [(Int, Int)]
all n, pair = ...
E.g. n = 3, pair (1,2) => [(1,2), (2,4), (3,6)]
which expands to [(1*(1,2)), ((2*(1,2)), (3*(1,2))]
I know its something with x <- [1..n] and x*pair but I don't know which built-in function is right to put it together!
You need to do the multiplication separately on the elements in the tuples, and then recreate a tuple. You can use fst and snd to get the elements.
all n pair = [(x*(fst pair), x*(snd pair)) | x <- [1..n]]
Pattern matching is another common way to access tuple elements.
all n (a, b) = [(x*a, x*b) | x <- [1..n]]

SML: Comparing every element of two lists without storing state info?

I have an SML assignment, and the general idea is that I shouldn't store state info, should not used built in library functions, etc and just solve in a functional way. Not sure how to do it:
The question requires comparing every element of two lists together:
input
list1: [(3,3,5),(5,4,7),(2,3,4)];
list2: [3, 6];
output
newList: [(3,3,5), (2,3,5)]
Essentially, when the second element in list1's tuple arg matches an item in list 2, then I should add the list1 item to the new output list.
The way I went about implementing it:
fun thing(x, y) =
if null x then []
else if #2 (hd x) = (hd y) then hd x # thing(tl x, y)
else thing(tl x, y);
Obviously the issue with this is that I lose state information: how would you match every element in list1 against every element in list2?
when the second element in list1's tuple arg matches an item in list 2,
then I should add the list1 item to the new output list.
fun thing(x, y) =
if null x then []
else if #2 (hd x) = (hd y) then hd x # thing(tl x, y)
else thing(tl x, y);
Instead of if null x then ..., hd x and tl x, use pattern matching:
fun thing ([], _) = ...
| thing ((x,y,z)::haystack, needles) = ...
To find out if y is a member of needles, build a membership function:
fun elem (p, []) = false
| elem (p, q::qs) = p = q orelse elem (p, qs)
Check if elem (y, needles) to determine whether to include (x,y,z) as part of your result.
Apply thing recursively to haystack and needles.
Compose the recursive result using the :: (cons) operator rather than # (append).
Here's how one could solve this exercise with library functions:
fun curry f x y = f (x, y)
fun thing (haystack, needles) =
List.filter (fn (x,y,z) =>
List.exists (curry op= y) needles) haystack

Using map with function that has multiple arguments

Is it possible to use map with a function that takes multiple arguments?
I want to use map's second and third arguments repeatedly as the function's arguments.
As in
mapF x y z = map (f y z) [1, 2, 3]
So it'll evaluate f with the same y and z values, but with x = 1, x = 2, x = 3 and so on.
You should use a lambda function, to see this works lets start by using a helper function to map f over some list.
map helper [1, 2, 3] where
helper x = f x y z
In Haskell there are two syntax for functions so lets use the lambda syntax to define our helper function:
map helper [1, 2, 3] where
helper = \x -> f x y z
using the lambda syntax we don't need to give our helper function an explicit name, it can just be an anonymous function we map over the input
map (\x -> f x y z) [1, 2, 3]
So now you can say
mapF y z = map (\x -> f x y z) [1,2,3]
But presumably you don't want x to be 1, 2 and 3, you want it to be a list
you pass as an argument to mapF. So you need to give that a different name:
mapF xs y z = map (\x -> f x y z) xs
It is Haskell convention to use s as a suffix for variables that hold lists or other containers. So if one value is x then a list of them is xs
There are guaranteed to be better ways to do this (still learning) but you can:
f' = map f [1,2,3]
f' is now a list of partially applied f
g y z= map (\h -> h y z) f'
will take each of those functions and run it on the arguments y z.
You can hack that all in one line.

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

SML - Find occurences in lists to form ordered pairs

I'm trying to write a function in SML that takes in a list of ints and will output a list of ordered pairs of ints. The ordered pairs first int is the int that occurred in the input list and the second int in the ordered pair is the number of times it occurred in the input list. Also the list returned should be in ascending order according to the first int in the ordered pairs.
For example input list [1, 1, 1, 2, 3, 3, 5] would output as [(1,3), (2, 1), (3, 2), (5, 1)].
So far I have a function that uses foldl
UPDATED the code since original post.
fun turnIntoPairs l = foldl (fn (e, a) => if List.exists (fn (x, _) => x = e) a then x + 1 else a # [(e, 1)]) [] l;
I'm having trouble updating the list where I find the ordered pair that is already in the list - I want to add one to the second int in the ordered pair that was found while it's still in the list.
Any help would be greatly appreciated!
C:\Program Files (x86)\SMLNJ\\bin\.run\run.x86-win32.exe: Fatal error -- Uncaught exception Error with 0
raised at ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
[autoloading done]
C:\Users\Localadmin\Desktop\CS 671\Program 3\commonFactors.sml:1.87 Error: unbound variable or constructor: x
C:\Users\Localadmin\Desktop\CS 671\Program 3\commonFactors.sml:1.44-1.110 Error: types of if branches do not agree [literal]
then branch: int
else branch: (''Z * int) list
in expression:
if (List.exists (fn <pat> => <exp>)) a
then <errorvar> + 1
else a # (e,1) :: nil
[Finished in 0.5s with exit code 1]
Not really sure how to fix your current program, but you can solve this problem by splitting it in two: grouping equal elements and then ordering the list.
(* Groups successive equal elements into a tuples (value, count) *)
fun group (l as (x :: _)) =
let val (firstGroup, rest) = List.partition (fn y => x = y) l
in
(x, List.length firstGroup) :: (group rest)
end
| group [] = []
(* Now that we have our elements grouped, what's left is to order
them as required. *)
fun turnIntoPairs xs =
ListMergeSort.sort (fn ((x, _), (y, _)) => x >= y) (group xs)
Let's just look at the function you're passing to foldl:
(fn (e, a) => if List.exists (fn (x, _) => x = e) a then x + 1 else a # [(e, 1)])
The first problem (which the type-checker is complaining about) is that your if expression returns either x + 1, or a # [(e, 1)], which seems problematic on account of the former being a value of type int and the latter being of type (int * int) list.
Let's rewrite your code using some helper functions that I won't define and see if it gets clearer:
(fn (e, a) => if List.exists (fn (x, _) => x = e) a then increment a e else a # [(e, 1)])
Where increment has the type (int * int) list -> int -> (int * int) list.
Can you implement increment?
Like Gian, I would prefer to divide this into two functions: One that folds and one helper function that inserts. Incidentally, the insert function would take an element and an existing (int * int) list just as the accumulator function that fold accepts these two arguments.
Normally I would write an insert function curried (i.e. insert x xs) but if I write it uncurried (i.e. insert (x, xs)), I can pass it directly to foldl:
fun insert (x, []) = [(x,1)]
| insert (x, ((y,c)::xs)) =
if x = y then (y,c+1)::xs else (y,c)::insert (x, xs)
fun turnIntoPairs xs = foldl insert [] xs