Related
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
I wrote a function that is supposed to receive a list of tuples. I access the components of the tuples with # and the code compiles:
fun recheck ([], n) = []
| recheck (h::t, n) =
if ((#1 h) * (#1 h)) + ((#2 h) * (#2 h)) = n then
h::recheck(t, n)
else
recheck(t, n)
But another function that basically does the same thing, namely receiving a list of tuples and accessing those, causes an error.
fun validate ([]) = true
| validate (h::t) =
if 1 = (#1 h) then
true
else
false
Can't find a fixed record type. Found near #1
What is the difference here and why does the latter cause an error?
Edit
The first function actually does not compile on its own.
But this entire snippet does:
fun drop ([], n) = []
| drop (h::t, 0) = h::t
| drop (h::t, n) =
drop(t, n-1)
fun sts_linear (y, n) =
if y < (Math.sqrt(n)+1.0) then
let
(* x^2 + y^2 = n => x = sqrt(n-y^2) *)
val x = Math.sqrt(n - (y * y));
val xr = Real.realRound(x);
in
if (abs(x - xr) < 0.000000001) then
[(Real.trunc xr, Real.trunc y)]#sts_linear (y+1.0, n)
else
(
[]#sts_linear (y+1.0, n)
)
end
else []
fun recheck ([], n) = []
| recheck (h::t, n) =
if ((#1 h) * (#1 h)) + ((#2 h) * (#2 h)) = n then
h::recheck(t, n)
else
recheck(t, n)
fun sts (n) =
(
let
val pairs = sts_linear(0.0, Real.fromInt n);
in
recheck(drop(pairs, Real.ceil( Real.fromInt (length(pairs))/2.0 ) ), n)
end
)
Your first code doesn't compile, at least with SML/NJ:
If you got it to compile then it must have been in a nonstandard extension of SML.
The problem with both of your definitions is that there is no polymorphic idea of a tuple of arbitrary arity in SML. You can write functions to work on lists of pairs. You can write functions to work on lists of triples. But -- you can't write functions to work simultaneously on lists of pairs and lists of triples (at least if your function tries to do things with these pairs/triples as tuples).
One solution is to get rid of # and use pattern-matching to extract the components:
fun validate [] = true
| validate ((x,y)::t) =
if x = 1 then
true
else
false
But, if you really want to write a function which can polymorphically apply to either lists of pairs or list of triples (or quadruples,...), the easiest thing to do is to represent the pairs, triples, etc. as lists rather than tuples. Lists which contains lists of nonspecified size are not a problem in SML.
Trying to minimize this down, as I have seen the following work in SML/NJ
and i'm not aware of it actually being a compiler extension
val p1 = {x=0, y=0};
val p2 = {x=1, y=1};
val p3 = {x=1, y=1, z=1};
There is an awkward construct from a compiler error perspective
not many languages have errors that work in this fashion,
because the function is valid, but produces a type error
unless an invocation of the function exists to resolve the
type of 'record', thus to resolve the error more code must be added.
fun getFoo(field) = fn record => field record;
Without the following actual calling of the getX
the compiler cannot determine the type of record
of which the complete type information of ALL fields
of the record must be known to the compiler, not just the #x field.
let val getX = getFoo(#x);
val x1 = getX(p1);
val x2 = getX(p2);
val x3 = getFoo(#x)(p3);
in () end;
while the following commented out snippet results in an error because the types of
p1 and p3 are different, and so different invocations of getFoo
are required
(*
let val getX = getFoo(#x);
val x1 = getX(p1);
val x3 = getX(p3);
in () end;
*)
and the following is insufficient since it never resolves the record.
let val getX = getFoo(#x) in () end;
I tried to use Map.map to convert a map into a List of Tuples.
However this fails. I did the following experiments:
val m = Map(("a" -> 1), ("b" -> 2))
//> m : scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)
val r1 = m.map{ case (k,v) => v} //> r1 : scala.collection.immutable.Iterable[Int] = List(1, 2)
def toTuple[A,B](a:A,b:B) = (a,b) //> toTuple: [A, B](a: A, b: B)(A, B)
//val r2: List[Tuple2[_,_]] = m.map(e => (e._1,e._2))
val r3 = m.map(e => toTuple(e._1,e._2)) //> r3 : scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)
val r4 = m.toSeq //> r4 : Seq[(String, Int)] = ArrayBuffer((a,1), (b,2))
Notice how a List is generated for single elements (r1) but a Map is produced for tuples (r3). Not even forcing the type worked (r2). Only an explicit call to Seq did it (r4) So my question is, why/how does Map.map "automagically" create a new Map and not a list for example? In fact how is the return type determined (Seq, List, etc.)
A Map is a collection of tuples already.
scala> "b" -> 2
res0: (String, Int) = (b,2) // Implicitly converted to a Tuple
When you're mapping a Map, you're mapping the (key, value) pairs that it contains. This can't work, because you're stripping away the keys, and retaining only the values. So what you have is no longer a Map, but a step or two up the collection hierarchy, an Iterable:
val r1 = m.map{ case (k,v) => v}
Forcing the type cannot work, because a Map[A, B] is not a List[(A, B)]. This is the equivalent of m.map(identity). Notice how you're even accessing e with tuple accessors:
val r2: List[Tuple2[_,_]] = m.map(e => (e._1,e._2))
val r3 = m.map(e => toTuple(e._1,e._2))
Here, Seq is more generalized than List:
val r4 = m.toSeq
The simple solution as stated by #EndeNeu is to just use toList. When you map a collection, it should return the original collection type if it can. So mapping a Map should return another Map, unless the underlying structure has made it no longer a Map (like removing keys entirely) in r1.
Why even bother with a map? Wouldn't it be more appropriate for your use case to just have your data structure be a List[(String,Int)]? It's basically the same.. you can even write it in map notation:
val myMapAsList: List[(String, Int)] = List[(String,Int)](
"a" -> 1,
"b" -> 2
) // yields val myMapAsList: List[(String, Int)] = List((a,1), (b,2))
I just start working with standard ml and really have some trouble understanding the list in this language. So my question is how to shorten a list in ml ? For example, if I have a list [1,2,3,4,5,6], I want to shorten it to [1,2]. What I'm having so far is:
fun shorten(i, l) = let val newlen = i in newlen = length l//in correct
what I want is a function that will take i as a location that user want to shorten the list and l is the list. In this case, the input should look like shorten(2, [1,2,3,4,5,6] and the output should look like [1,2]
This function should do it:
fun shorten(_, nil) = nil
| shorten(0, _) = nil
| shorten(i, x::xs) = x::shorten(i - 1, xs)
As you noticed, this function doesn't throw any exceptions when i is larger than the length of the list. An approach that uses exceptions would be this:
exception IllegalArgument
fun shorten(_, nil) = nil
| shorten(0, _) = nil
| shorten(i, x::xs) =
if i > length(x::xs) then raise IllegalArgument
else x::shorten(i - 1, xs)
In SML you need to declare any exception type with exception before it is raised using raise. Otherwise, the exception type is not bound in the environment and the interpreter will complain about the symbol being unknown.
The SML Basis Library contains the function List.take to perform the required task as part of the List Structure.
- fun shorten ( toHowMany, myList ) = List.take ( myList, toHowMany ) ;
val shorten = fn : int * 'a list -> 'a list
- shorten ( 2, [1,2,3,4,5,6] ) ;
val it = [1,2] : int list
If the order of the arguments doesn't matter, then List.take can be used directly:
- List.take ( [1,2,3,4], 2 ) ;
val it = [1,2] : int list
i'm trying to learn ocaml right now and wanted to start with a little program, generating all bit-combinations:
["0","0","0"]
["0","0","1"]
["0","1","0"]
... and so on
My idea is the following code:
let rec bitstr length list =
if length = 0 then
list
else begin
bitstr (length-1)("0"::list);
bitstr (length-1)("1"::list);
end;;
But i get the following error:
Warning S: this expression should have type unit.
val bitstr : int -> string list -> string list = <fun>
# bitstr 3 [];;
- : string list = ["1"; "1"; "1"]
I did not understand what to change, can you help me?
Best regards
Philipp
begin foo; bar end executes foo and throws the result away, then it executes bar. Since this makes only sense if foo has side-effects and no meaningful return value ocaml emits a warning if foo has a return value other than unit, since everything else is likely to be a programmer error (i.e. the programmer does not actually intend for the result to be discarded) - as is the case here.
In this case it really does make no sense to calculate the list with "0" and then throw it away. Presumably you want to concatenate the two lists instead. You can do this using the # operator:
let rec bitstr length list =
if length = 0 then
[list]
else
bitstr (length-1)("0"::list) # bitstr (length-1)("1"::list);;
Note that I also made the length = 0 case return [list] instead of just list so the result is a list of lists instead of a flat list.
Although sepp2k's answer is spot on, I would like to add the following alternative (which doesn't match the signature you proposed, but actually does what you want) :
let rec bitstr = function
0 -> [[]]
| n -> let f e = List.map (fun x -> e :: x) and l = bitstr (n-1) in
(f "0" l)#(f "1" l);;
The first difference is that you do not need to pass an empty list to call the function bitsr 2 returns [["0"; "0"]; ["0"; "1"]; ["1"; "0"]; ["1"; "1"]]. Second, it returns a list of ordered binary values. But more importantly, in my opinion, it is closer to the spirit of ocaml.
I like to get other ideas!
So here it is...
let rec gen_x acc e1 e2 n = match n with
| 0 -> acc
| n -> (
let l = List.map (fun x -> e1 :: x) acc in
let r = List.map (fun x -> e2 :: x) acc in
gen_x (l # r) e1 e2 (n - 1)
);;
let rec gen_string = gen_x [[]] "0" "1"
let rec gen_int = gen_x [[]] 0 1
gen_string 2
gen_int 2
Result:
[["0"; "0"]; ["0"; "1"]; ["1"; "0"]; ["1"; "1"]]
[[0; 0]; [0; 1]; [1; 0]; [1; 1]]