My Implementation Attempt
I'm attempting to implement tuple unpacking in my interpreter. To represent a tuple, I'm using a class Tuple with a vector members that can store any value Value in the language:
typedef std::variant<int, float, std::string, Tuple> Value; //Note - ignore the "Tuple" circular dependency - this code is just for illustration purposes
class Tuple {
public:
const std::vector<Value> members;
Tuple(std::vector<Type> _members) : members(_members) {}
};
The unpacking of a tuple is python-like in syntax with a variable list on the LHS and the tuple on the right.
(a, (b, c), d) = (1, (2, 3), 4)
The RHS of the above code is represented by the tuple class above as Tuple example({1, Tuple({2, 3}), 4});
For some background, in my interpreter I'm using a AST with the visitor pattern to construct Value objects and assign them to variables when a assign statement is visited. The above RHS tuple object example is generated when a TupleNode representing the expression ((1, (2, 3), 4)) is visited. The entire statement ((a, (b, c), d) = (1, (2, 3), 4)) is represented by a UnpackNode with left side pointing to a vector of identifiers ((a, (b, c), d)) and right side pointing to the TupleNode previously discussed.
When a UnpackNode is visited, it first works out what the Tuple Object (example) and then iterates over every identifier on the LHS of the statement, assigning it to a members value of the Tuple object at the same index of the identifier on the LHS.
Concerns
Using the approach described above has caused significant performance hits when using tuples. The two code snippets below yields the same results but using the tuples and unpacking is approx ~20x slower.
(a, b, c) = (1, 2, 3) //approx 20x slower then equivalent code below
_______
a = 1
b = 2
c = 3
Is representing the contents of a Tuple with a vector of vectors a good idea (performance wise) considering that it will be iterated over quite frequently? Would it instead be a better idea to flatten the vector and use some other system to work out how to index the tuple (for situations where there is tuples of tuples)? What would be an efficient way to iterate over a tuple in order to extract certain values?
If anyone has any general advice or tips on tuple implementations of resources they could link that would very much be appreciated. Or if anyone has any criticisms with my approach or can suggest a better way of doing so please feel free to let me know. There's not a lot of material on tuple unpacking in relation to compiler/interpreter design so I would be very appreciative of any information that could be provided in the context of their implementation.
Related
In OCaml, suppose I have a string list as follows:
let ls : string list = ["A"; "A"; "B"; "B"; "A"; "Y"; "Y"; "Y"] ;;
I'm having trouble writing a function that calculates how many times an element occurs consecutively and also pairs up that element with its frequency. For instance, given the above list as input, the function should return [("A", 2); ("B", 2), ("A", 1), ("Y", 3)].
I've tried looking for some hints elsewhere but almost all other similar operations are done using int lists, where it is easy to simply add numbers up. But here, we cannot add strings.
My intuition was to use something like fold_left in some similar fashion as below:
let lis : int list = [1;2;3;4;5]
let count (lis : int list) = List.fold_left (fun x y -> x + y) (0) (lis)
which is essentially summing all the elements cumulatively from left to right. But, in my case, I don't want to cumulatively sum all the elements, I just need to count how many times an element occurs consecutively. Some advice would be appreciated!
This is obviously a homework assignment, so I will just give a couple of hints.
When you get your code working, it won't be adding strings (or any other type) together. It will be adding ints together. So you might want to look back at those examples on the net again :-)
You can definitely use fold_left to get an answer. First, note that the resultl is a list of pairs. The first element of each pair can be any type, depending on the type of the original list. The second element in each pair is an int. So you have a basic type that you're working with: ('a * int) list.
Imagine that you have a way to "increment" such a list:
let increment (list: ('a * int) list) value =
(* This is one way to think of your problem *)
This function looks for the pair in the list whose first element is equal to value. If it finds it, it returns a new list where the associated int is one larger than before. If it doesn't find it, it returns a new list with an extra element (value, 1).
This is the basic operation you want to fold over your list, rather than the + operation of your example code.
I have a tuple that is (a, b, c). I want to get a common value to use as a key from this tuple, and I thought of something like hashing. For example, (a, b, c) and (b, a, c) should both give me the same hash value. However, I tried to hash (1, 2, 3) and (2, 1, 3) and ended up with different hash values.
How do I do this?
How about sort the tuple first? All permutations would become the same tuple after sorting and thus give the same hash value.
I would have thought that a list of tuples could easily be flattened:
scala> val p = "abcde".toList
p: List[Char] = List(a, b, c, d, e)
scala> val q = "pqrst".toList
q: List[Char] = List(p, q, r, s, t)
scala> val pq = p zip q
pq: List[(Char, Char)] = List((a,p), (b,q), (c,r), (d,s), (e,t))
scala> pq.flatten
But instead, this happens:
<console>:15: error: No implicit view available from (Char, Char) => scala.collection.GenTraversableOnce[B].
pq.flatten
^
I can get the job done with:
scala> (for (x <- pq) yield List(x._1, x._2)).flatten
res1: List[Char] = List(a, p, b, q, c, r, d, s, e, t)
But I'm not understanding the error message. And my alternative solution seems convoluted and inefficient.
What does that error message mean and why can't I simply flatten a List of tuples?
If the implicit conversion can't be found you can supply it explicitly.
pq.flatten {case (a,b) => List(a,b)}
If this is done multiple times throughout the code then you can save some boilerplate by making it implicit.
scala> import scala.language.implicitConversions
import scala.language.implicitConversions
scala> implicit def flatTup[T](t:(T,T)): List[T]= t match {case (a,b)=>List(a,b)}
flatTup: [T](t: (T, T))List[T]
scala> pq.flatten
res179: List[Char] = List(a, p, b, q, c, r, d, s, e, t)
jwvh's answer covers the "coding" solution to your problem perfectly well, so I am not going to go into any more detail about that. The only thing I wanted to add was clarifying why the solution that both you and jwvh found is needed.
As stated in the Scala library, Tuple2 (which (,) translates to) is:
A tuple of 2 elements; the canonical representation of a Product2.
And following up on that:
Product2 is a cartesian product of 2 components.
...which means that Tuple2[T1,T2] represents:
The set of all possible pairs of elements whose components are members of two sets (all elements in T1 and T2 respectively).
A List[T], on the other hand, represents an ordered collections of T elements.
What all this means practically is that there is no absolute way to translate any possible Tuple2[T1,T2] to a List[T], simply because T1 and T2 could be different. For example, take the following tuple:
val tuple = ("hi", 5)
How could such tuple be flattened? Should the 5 be made a String? Or maybe just flatten to a List[Any]? While both of these solutions could be used, they are working around the type system, so they are not encoded in the Tuple API by design.
All this comes down to the fact that there is no default implicit view for this case and you have to supply one yourself, as both jwvh and you already figured out.
We needed to do this recently. Allow me to explain the use case briefly before noting our solution.
Use case
Given a pool of items (which I'll call type T), we want to do an evaluation of each one against all others in the pool. The result of these comparisons is a Set of failed evaluations, which we represent as a tuple of the left item and the right item in said evaluation: (T, T).
Once these evaluations are complete, it becomes useful for us to flatten the Set[(T, T)] into another Set[T] that highlights all the items that have failed any comparisons.
Solution
Our solution for this was a fold:
val flattenedSet =
set.foldLeft(Set[T]())
{ case (acc, (x, y)) => acc + x + y }
This starts with an empty set (the initial parameter to foldLeft) as the accumulator.
Then, for each element in the consumed Set[(T, T)] (named set) here, the fold function is passed:
the last value of the accumulator (acc), and
the (T, T) tuple for that element, which the case deconstructs into x and y.
Our fold function then returns acc + x + y, which returns a set containing all the elements in the accumulator in addition to x and y. That result is passed to the next iteration as the accumulator—thus, it accumulates all the values inside each of the tuples.
Why not Lists?
I appreciated this solution in particular since it avoided creating intermediate Lists while doing the flattening—instead, it directly deconstructs each tuple while building the new Set[T].
We could also have changed our evaluation code to return List[T]s containing the left and right items in each failed evaluation—then flatten would Just Work™. But we thought the tuple more accurately represented what we were going for with the evaluation—specifically one item against another, rather than an open-ended type which could conceivably represent any number of items.
I am studying prolog and was wondering if anybody give me guidance on how to go about doing this question, It's the first of many in this area and knowing how to do this question will really help me progress. Thank-you in advance.
Using Prolog define a predicate mapof(K, M, V) such that, when invoked with K instantiated to a key, and M instantiated to a mapping, mapof will instantiate the variable V to the value (or one of the values) associated with K in mapping M. The predicate should fail if K does not appear as a key in mapping M.
It really depends how you want to represent your "mapping". In Prolog, a table of facts is the most obvious approach. For two mappings m and n:
m(a, 1).
m(b, 2).
m(c, 3). % and so on
n(a, foo).
n(b, bar).
n(c, baz). % and so on
Then, your mapof would be something along the lines of:
mapof(K, m, V) :- m(K, V).
mapof(K, n, V) :- n(K, V).
or maybe:
mapof(K, M, V) :- call(M, K, V).
A list can be used to represent a mapping, as shown by #Yasel, but a list [a, b, c] in Prolog is a nested term like .(a, .(b, .(c, []))). You don't usually represent an associative array as a singly linked list, right?
In SWI-Prolog there is a library that is better than using a simple list for a backtrackable associative array represented as a Prolog term: library(assoc). With it, you can do:
mapof(K, M, V) :- gen_assoc(K, M, V).
This library represents the associative array as an AVL tree. You can find in the SWI-Prolog code source two more associative array implementations: one using RB-trees, and one that uses non-backtrackable RB-trees.
All three libraries mentioned here are probably more efficient than a simple list of key-value pairs [k1-v1, k2-v2...] if your associative array has more than say around 100 key-value pairs in it. This doesn't mean that using a list of pairs and doing member(Key-Value, List_of_pairs) is wrong; it is the cheapest solution for simple cases.
Using the built-in predicate member/2 you can build your predicate mapof/3 like this:
mapof(K, M, V):- member((K,V), M).
Consult:
?- mapof(k1, [(k, a),(k1,b),(k2,c),(k1,d)], V).
V = b ;
V = d.
This is a followup to an answer to a question about sorting on a particular argument of a term, without creating a new list for a keysort (if I understood the original question correctly).
Say we wanted predsort/3 to behave exactly as sort/2: if I understand correctly, this would mean calling it as:
?- predsort(compare, List, Sorted).
Now say that we wanted to use predsort/3 to sort as implemented by msort/2 (see also this question). One way to do it would be to define a comparison predicate Pred(-Delta, +A, +B) that does not unify Delta with = when the elements are actually equal:
mcompare(Delta, A, B) :-
compare(Delta0, A, B),
( Delta0 == (=)
-> Delta = (<)
; Delta = Delta0
).
?- predsort(mcompare, List, Sorted).
Question: does that really simply sort without removing duplicates, as msort/2 does? It seems like it should.
Moving on: say we wanted to sort terms with arity > n, on the standard order of the nth argument in the term. The clean way to do it would be:
sort_argn(N, List, Sorted) :-
map_list_to_pairs(arg(N), List, Pairs),
keysort(Pairs, Sorted_pairs),
pairs_values(Sorted_pairs, Sorted).
If we wanted to use predsort/3 to achieve the same effect, we could try using a comparison predicate as follows:
compare_argn(N, Delta, A, B) :-
arg(N, A, AN),
arg(N, B, BN),
compare(Delta, AN-A, BN-B).
And to sort on the second argument:
?- predsort(compare_argn(2), List, Sorted).
However, this is not the same as sort_argn/3 above that uses keysort/2. It will remove duplicates, and it will order compound terms according to the standard order of the original full term if the second arguments of two terms happen to be equal:
?- predsort(compare_argn(2), [f(b,2), f(a,1), f(a,1), f(a,2)], Sorted).
Sorted = [f(a, 1), f(a, 2), f(b, 2)].
?- sort_argn(2, [f(b,2), f(a,1), f(a,1), f(a,2)], Sorted).
Sorted = [f(a, 1), f(a, 1), f(b, 2), f(a, 2)].
Making the assumption that for every pair of A and B passed to the comparison predicate Pred(Delta, A, B), A comes before B in the original list. Can we define a comparison:
compare_argn_stable(N, Delta, A, B) :-
arg(N, A, AN),
arg(N, B, BN),
compare(Delta0, AN, BN),
( Delta0 == (=)
-> Delta = (<)
; Delta = Delta0
).
At this point, if and only if any two elements A and B are always passed to the comparison predicate in the same order as they were in the original list, this should behave identically to sort_argn/3 above:
?- predsort(compare_argn_stable(N), List, Sorted).
Now of course it is important that compare_argn_stable/4 unifies Delta with < when the two "keys" are equal. Furthermore, the behavior is implementation dependent, and only identical to the keysort example iff predsort/3 keeps the original order of elements when passing them to the comparison predicate.
Question Is that correct?
Question Is there any standard that covers this aspect of predsort/3?
Since no one has answered, and since I am quite certain about it now:
Yes, you could use predsort/3 to emulate any of the other sorts. The question describes in some detail how.
However: this is a bad idea for several reasons.
The "stability" depends on the implementation of predsort/3 (see the question)
The predsort/3 itself is not part of any standard (as far as I can tell)
The chances are, your Prolog implementation provides an msort/2 or keysort/2 that is far more efficient than predsort/3
There might be rare cases where the size of the elements of the list is much bigger than the length of the list we are sorting, and this little dance:
list_to_keyval_pairs(List, Pairs), % defined by the user as necessary
keysort(Pairs, Sorted_pairs),
pairs_values(Sorted_pairs, Sorted)
(see here) is actually more expensive (slower) than using predsort(keycmp, List, Sorted), with keycmp/3 defined by the user. Even then, the order of results with equivalent keys depends not only on the (user) definition of keycmp/3, but also on the implementation of predsort/3.
In other words, a "stable" sort with predsort/3 is a bad idea.