Related
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.
I am trying to create a function which interleaves a pair of triples such as ((6, 3, 2), ( 4, 5 ,1)) and create a 6-tuple out of this interleaving.
I made some research but could understand how interleaving is supposed to work so I tried something on my own end ended up with a code that is creating a 6-tuple but not in the right interleaved way. This is my code
let interleave ((a, b, c), (a', b', c')) =
let sort2 (a, b) = if a > b then (a, b) else (b, a) in
let sort3 (a, b, c) =
let (a, b) = sort2 (a, b) in
let (b, c) = sort2 (b, c) in
let (a, b) = sort2 (a, b) in
(a, b, c) in
let touch ((x), (y)) =
let (x) = sort3 (x) in
let (y) = sort3 (y) in
((x),(y)) in
let ((a, b, c), (a', b', c')) = touch ((a, b, c), (a', b', c')) in
(a, b', a', b, c, c');;
Can someone please explain to me how with what functions I can achieve a proper form of interleaving. I haven't learned about recursions and lists in case you would ask why I am trying to do it this way.
Thank you already.
The problem statement uses the word "max" without defining it. If you use the built-in compare function of OCaml as your definition, it uses lexicographic order. So you want the largest value (of the 6 values) in the first position in the 6-tuple, the second largest value next, and so on.
This should be pretty easy given your previously established skill with the sorting of tuples.
For what it's worth, there doesn't seem to be much value in preserving the identities of the two 3-tuples. Once inside the outermost function you can just work with the 6 values as a 6-tuple. Or so it would seem to me.
Update
From your example (should probably have given it at the beginning :-) it's pretty clear what you're being asked to do. You want to end up with a sequence in which the elements of the original tuples are in their original order, but they can be interleaved arbitrarily. This is often called a "shuffle" (or a merge). You have to find the shuffle that has the maximum value lexicographically.
If you reason this out, it amounts to taking whichever value is largest from the front of the two tuples and putting it next in the output.
This is much easier to do with lists.
Now that I understand what your end-goal is . . .
Since tuples of n elements are different types for different n's, you need to define helper functions for manipulating different sizes of tuples.
One approach, that basically mimics a recursive function over lists (but requires many extra functions because of tuples all having different types), is to have two sets of helper functions:
functions that prepend a value to an existing tuple: prepend_to_2, up through prepend_to_5. For example,
let prepend_to_3 (a, (b, c, d)) = (a, b, c, d)
functions that interleave two tuples of each possible size up to 3: interleave_1_1, interleave_1_2, interleave_1_3, interleave_2_2, interleave_2_3, and interleave_3_3. (Note that we don't need e.g. interleave_2_1, because we can just call interleave_1_2 with the arguments in the opposite order.) For example,
let interleave_2_2 ((a, b), (a', b')) =
if a > a'
then prepend_to_3 (a, interleave_1_2 (b, (a', b')))
else prepend_to_3 (a', interleave_1_2 (b', (a, b)))
(Do you see how that works?)
Then interleave is just interleave_3_3.
With lists and recursion this would be much simpler, since a single function can operate on lists of any length, so you don't need multiple different copies of the same logic.
I have several places in my code which look like this:
let (a,b,c) = (f "a", f "b", f "c")
It would be nice if I can write something like:
let (a,b,c) = map f ("a", "b", "c")
If there is way to do something like this in OCaml?
You can easily write map for triples of one type of element:
let map_triple f (a, b, c) = (f a, f b, f c)
let a, b, c = map_triple String.length ("foo", "bar", "quux")
It will only work for one length of tuple, however.
It would be possible to write a GADTified tuple type and write a map over that type that is polymorphic in the length of the tuple, but that kind of trickery is best avoided unless the advantage is large, which does not seem to be the case here.
The best answer is that you can't do this, if you want it to work for tuples of different sizes. Each tuple size is a different type in OCaml. So there's no OCaml type representing the idea of "a tuple of any size whose elements are strings."
But in fact this sounds like a list more than a tuple. If you can use lists instead of tuples, you can use the plain old List.map function.
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.