The main issue from the below picture is that when "check Result end" statement is added it automatically fails and displays "CHECK_VIOLATION" error in debugger.
Also, the HASH_TABLE doesn't store all items given to it but I fixed that by switching HASH_TABLE[G, INTEGER] instead of using the current HASH_TABLE[INTEGER, G]
My main problem is why does it throw Check_violation always and fail whenever a "check result end" statement is seen? Maybe the HAS[...] function is bad?
Currently any test case feature with "check result end" makes it false and throw CHECK_VILOATION
code:
class
MY_BAG[G -> {HASHABLE, COMPARABLE}]
inherit
ADT_BAG[G]
create
make_empty, make_from_tupled_array
convert
make_from_tupled_array ({ARRAY [TUPLE [G, INTEGER]]})
feature{NONE} -- creation
make_empty
do
create table.make(1)
end
make_from_tupled_array (a_array: ARRAY [TUPLE [x: G; y: INTEGER]])
require else
non_empty: a_array.count >= 0
nonnegative: is_nonnegative(a_array)
do
create table.make(a_array.count)
across a_array as a
loop
table.force (a.item.y, a.item.x)
end
end
feature -- attributes
table: HASH_TABLE[INTEGER, G]
counter: INTEGER
testing code:
t6: BOOLEAN
local
bag: MY_BAG [STRING]
do
comment ("t6:repeated elements in contruction")
bag := <<["foo",4], ["bar",3], ["foo",2], ["bar",0]>> -- test passes
Result := bag ["foo"] = 5 -- test passes
check Result end -- test fails (really weird but as soon as check statement comes it fails)
Result := bag ["bar"] = 3
check Result end
Result := bag ["baz"] = 0
end
Most probably ADT_BAG stands for an abstraction of a multiset (also called a bag) that allows to keep items and to tell how many items equal to the given one are there (unlike a set, where at most one item may be present). If so, it is correct to use HASH_TABLE [INTEGER, G] as a storage. Then its keys are the elements and its items are the elements numbers.
So, if we add the same element multiple times, its count should be increased. In the initialization line we add 4 elements of "foo", 3 elements of "bar", 2 elements of "foo" again, and 0 elements of "bar" again. As a result we should have a bag with 6 elements of "foo" and 3 elements of "bar". Also there are no elements "baz".
According to this analysis, either initialization is incorrect (numbers for "foo" should be different) or the comparison should be done for 6 instead of 5.
As to the implementation of the class MY_BAG the idea would be to have a feature add (or whatever name specified in the interface of ADT_BAG) that
Checks if there are items with the given key.
If there are none, adds the new element with the given count.
Otherwise, replaces the current element count with the sum of the current element count and the given element count.
For simplicity the initialization procedure would use this feature to add new items instead of storing items in the hash table directly to process repeated items correctly.
Related
The following program let the user define the order in which the key names (elements) from the_dictionary_list will be inserted into Keys_input:
the_dictionary_list = {'Color': ['Amarillo.png', 'Blanco.png', 'Rojirosado.png', 'Turquesa.png', 'Verde_oscuro.png', 'Zapote.png'], 'Cuerpo': ['Cuerpo_cangrejo.png'], 'Fondo': ['Oceano.png'], 'Ojos': ['Antenas.png', 'Pico.png', 'Verticales.png'], 'Pinzas': ['None', 'Pinzitas.png', 'Pinzotas.png', 'Pinzota_pinzita.png'], 'Puas': ['None', 'Arena.png', 'Marron.png', 'Purpura.png', 'Verde.png']}
# creating an empty list
Keys_input = []
# number of elements
n = len(the_dictionary_list)
i = 0
print('\n')
print('The following "keys" represent the name of the folders in the current path')
while True:
AllKeysNames = the_dictionary_list.keys()
print('\033[46m'+str(AllKeysNames)+'\033[0m')
ele = input("\033[0;37;40mNow It's time to define the order in which the Cartesian Products will be made, tell me which valid key you want me to set now:\033[0m ")
if ele in the_dictionary_list and ele not in Keys_input:
Keys_input.append(ele) # adding the element
i += 1
print(f'\033[0;37;42mThe array has been updated, its current storage is the following {Keys_input}\033[0m')
if i == n:
print("\u001b[45mThe array is now full, let's continue with the next step\033[0m")
break
else:
if ele not in the_dictionary_list:
print('\u001b[43mPlease, type only valid key names\033[0m')
if ele in Keys_input:
print('\u001b[43mStop, that key IS ALREADY SAVED in the array, try with a different valid one\033[0m')
print(f'\u001b[45mCurrent storage of the array is the following {Keys_input}\033[0m')
Now, assume that the user chooses the following order for the elements in Keys_input:
['Fondo', 'Cuerpo', 'Ojos', 'Color', 'Pinzas', 'Puas']
How could the program above be improved to add automatically the last element left (in this case 'Puas') to the array when there are no more key names left? I mean, right after the Keys_input array is updated as follows:
['Fondo', 'Cuerpo', 'Ojos', 'Color', 'Pinzas']
Figured it out. After adding the penultimate element to the array, the program will use a for loop to iterate over the dictionary, and it will evaluate which key is not in the array, the one that is not there (as it will always be the last one) will be added automatically by the program. After this, the program breaks the while True loop of the original code
if i == (n-1):
for key in the_dictionary_list:
if key not in Keys_input:
Keys_input.append(key)
print(f'\033[0;37;42mLet me add the last key, the final storage is the following {Keys_input}\033[0m')
print("\u001b[45mThe array is now full, let's continue with the next step\033[0m")
break
Let's say I have the following dictionary:
the_dictionary_list = {'Color': ['Amarillo.png', 'Blanco.png', 'Rojirosado.png', 'Turquesa.png', 'Verde_oscuro.png', 'Zapote.png'],
'Cuerpo': ['Cuerpo_cangrejo.png'],
'Fondo': ['Oceano.png'],
'Ojos': ['Antenas.png', 'Pico.png', 'Verticales.png'],
'Pinzas': ['None', 'Pinzitas.png', 'Pinzotas.png', 'Pinzota_pinzita.png'],
'Puas': ['None', 'Arena.png', 'Marron.png', 'Purpura.png', 'Verde.png']}
A way of printing each possible combination is by using itertools, here:
import itertools as it
AllKeysNames = the_dictionary_list.keys()
Combinations = list(it.product(*(the_dictionary_list[Name] for Name in AllKeysNames)))
print(f'{Combinations}')
This will print a list which has 360 elements in total.
However, let's say that the order matters in the previous case (i.e. permutations are needed), this order would be the following:
'Fondo'>'Cuerpo'>'Ojos'>'Color'>'Pinzas'>'Puas'
How could I make a program that always start with the value of 'Fondo' key, then adds the value of 'Cuerpo' key, then adds the first value of 'Color' key, then adds the first value of 'Pinzas' key, then adds the first value of 'Puas' key and so on...?
In the end the total amount of elements in the new list would still be 360, but this time these elements would have been created following an specific order.
Any ideas?
I finally figured it out, I even decided to let the user be the one who decides the order in which the permutations will be made.
# creating an empty list
Keys_input = []
# number of elements
n = len(the_dictionary_list)
i = 0
while True:
AllKeysNames = the_dictionary_list.keys()
print('\033[46m'+str(AllKeysNames)+'\033[0m')
ele = input("\033[0;37;40mTell me which valid key you want me to set now:\033[0m ")
if ele in the_dictionary_list and ele not in Keys_input:
Keys_input.append(ele) # adding the element
i += 1
print(f'\033[0;37;42mThe array has been updated, its current storage is the following {Keys_input}\033[0m')
if i == n:
print("\u001b[45mThe array is now full, let's continue with the next step\033[0m")
break
else:
if ele not in the_dictionary_list:
print('\u001b[43mPlease, type only valid key names\033[0m')
if ele in Keys_input:
print('\u001b[43mStop, that key IS ALREADY SAVED in the array, try with a different valid one\033[0m')
print(f'\u001b[45mCurrent storage of the array is the following {Keys_input}\033[0m')
AllKeysNames2 = Keys_input
Combinations = list(it.product(*(the_dictionary_list[Name] for Name in AllKeysNames2)))
print(f'{Combinations}')
I have a method that shifts all the items, in an array, to the left by one position. In my post condition I need to ensure that my items have shifted to the left by one. I have already compared the first element of the old array to the last element of the new array. How do i across loop through the old array from 2 until count, loop through the new array from 1 until count-1 and compare them? This is my implementation so far.
items_shifted:
old array.deep_twin[1] ~ array[array.count]
and
across 2 |..| (old array.deep_twin.count) as i_twin all
across 1 |..| (array.count-1) as i_orig all
i_twin.item ~ i_orig.item
end
end
end
I expected the result to be true but instead I get a contract violation pointing to this post condition. I have tested the method out manually by printing out the array before and after the method and I get the expected result.
In the postcondition that fails, the loop cursors i_twin and i_orig iterate over sequences 2 .. array.count and 1 .. array.count - 1 respectively, i.e. their items are indexes 2, 3, ... and 1, 2, .... So, the loop performs comparisons 2 ~ 1, 3 ~ 2, etc. (at run-time, it stops on the first inequality). However, you would like to compare elements, not indexes.
One possible solution is shown below:
items_shifted:
across array as c all
c.item =
if c.target_index < array.upper then
(old array.twin) [c.target_index + 1]
else
old array [array.lower]
end
end
The loop checks that all elements are shifted. If the cursor points to the last element, it compares it against the old first element. Otherwise, it tests whether the current element is equal to the old element at the next index.
Cosmetics:
The postcondition does not assume that the array starts at 1, and uses array.lower and array.upper instead.
The postcondition does not perform a deep twin of the original array. This allows for comparing elements using = rather than ~.
Edit: To avoid potential confusion caused by precedence rules, and to highlight that comparison is performed for all items between old and new array, a better variant suggested by Eric Bezault looks like:
items_shifted:
across array as c all
c.item =(old array.twin)
[if c.target_index < array.upper then
c.target_index + 1
else
array.lower
end]
end
Actually, I already have a partial answer!!! Conditional formatting with "Cell value is" -> "duplicate" !!!
This way a check is performed for each user's new entry in "real time".
I need to check if duplicate entries exist in 30000 rows of a column (any value, but not blanks!) . I would like to keep track of how many duplicates during the filling process.
Ok, conditional formatting is a very effective visual indication and fast anough for my needs, but as I am not able to perform a loop to check the color of the cells (found some people against this approach!! Would be so easy! ) I need to find an alternative way to count the duplicates (as a whole, no need to identify how many for each case!).
I tryed the formula:
=SUMPRODUCT((COUNTIF(F2:F30001;$F$2:$F$30001)>1))
It works, but it takes two minutes to finish.
If you want to replicate my case. My 30000 entries are formatted as: letter "A" and numbers between 100000 and 999999, e.g., A354125, A214547, etc. Copy as text the result of "=CONCATENATE("A";RANDBETWEEN(100000;999999))" to save time.
Thanks!
PS: Does anybody know the algorithm used to find the duplicates in conditional formatting (it is fast)?
A macro solution is not the best, but is acceptable! ;)
The =SUMPRODUCT((COUNTIF(F2:F30001;$F$2:$F$30001)>1)) must do following: Count if $F$2 is in F2:F30001, then count if $F$3 is in F2:F30001, ..., then count if $F$30001 is in F2:F30001. So it must fully loop over the array F2:F30001 with each single item.
The fastest way counting duplicates in an array is avoiding fully loop over the array with each single item. One way is sorting first. There are very fast quick sort methods. Or using collections which per definition can only have unique items.
The following code uses the second way. The keys of a Collection must be unique. Adding an item having a duplicate key fails.
Public Function countDuplicates(vArray As Variant, Optional inclusive As Boolean ) As Variant
On Error Goto wrong
If IsMissing(inclusive) Then inclusive = False
oDuplicatesCollection = new Collection
oUniqueCollection = new Collection
lCountAll = 0
For Each vValue In vArray
If contains(oUniqueCollection, CStr(vValue)) Then
On Error Resume Next
oDuplicatesCollection.Add 42, CStr(vValue)
On Error Goto 0
Else
oUniqueCollection.Add 42, CStr(vValue)
End If
lCountAll = lCountAll + 1
Next
countDuplicates = lCountAll - oUniqueCollection.Count + IIF(inclusive, oDuplicatesCollection.Count, 0)
Exit Function
wrong:
'xray vArray
countDuplicates = CVErr(123)
End Function
Function contains(oCollection As Collection, sKey As String)
On Error Goto notContains
oCollection.Item(sKey)
contains = True
Exit Function
notContains:
contains = False
End Function
The function can be called:
=COUNTDUPLICATES(F2:F30001, TRUE())
This should return the same result as your
=SUMPRODUCT((COUNTIF(F2:F30001,$F$2:$F$30001)>1))
The optional second parameter inclusive means the count includes all the values which are present multiple times. For example {A1, A2, A2, A2, A3} contains 3 times A2. Counting inclusive means the count result will be 3. Counting not inclusive means the count result will be 2. There is 2 times A2 as a duplicate.
As you see, the function contains much more information than only the count of the duplicates. The oDuplicatesCollection contains each duplicate item. The oUniqueCollection contains each unique item. So this code could also be used for getting all unique items or all duplicate items.
This is part of the class. This class is called BAG[G -> {HASHABLE, COMPARABLE}]
it inherits from ADT_BAG which has deferred features such as count, extend, remove, remove_all, add_all... more, and domain to be re-implemented.
domain returns ARRAY[G] which is a sorted array list of G
i always get Post-condition violation "value_semantics" which is something to do with object comparison but I checked and there is no code for object comparison which is very weird.
I tried to remake the code for domain feature several times and it ALWAYS ends up with a post-condition violation or a fail.
When I check the debugger the array "a" that is returned from domain always has count 0 but this does not make sense because i move keys from table to "a" but count is still 0.
Maybe I am transferring the keys wrong to the array?
code:
count: INTEGER
-- cardinality of the domain
do
result := domain.count -- has to be domain.count because loop invariant: consistent: count = domain.count
end
domain: ARRAY[G]
-- sorted domain of bag
local
tmp: G
a: ARRAY[G]
do
create a.make_empty
across 1 |..| (a.count) as i -- MOVING keys from table to array
loop
across table as t
loop
if not a.has (t.key) then
a.enter (t.key, i.item)
i.forth
end
end
end
across 1 |..| (a.count-1) as i -- SORTING
loop
if a[i.item] > a[i.item+1] then
tmp := a[i.item]
a[i.item] := a[i.item+1]
a[i.item+1] := tmp
end
end
Result := a
ensure then
value_semantics: Result.object_comparison -- VIOLATION THROWN HERE
correct_items: across 1 |..| Result.count as j all
has(Result[j.item]) end
sorted: across 1 |..| (Result.count-1) as j all
Result[j.item] <= Result[j.item+1] end
end
test code:
t3: BOOLEAN
local
sorted_domain: ARRAY[STRING]
do
comment("t3:test sorted domain")
sorted_domain := <<"bolts", "hammers", "nuts">>
sorted_domain.compare_objects
Result := bag2.domain ~ sorted_domain -- fails here
check Result end
end
The first loop across 1 |..| (a.count) as i is not going to make a single iteration because a is empty (has no elements) at the beginning. Indeed, it has been just created with create a.make_empty.
Also, because keys in the table are unique it is useless to check whether a key has been added to the resulting array: the test not a.has (t.key) will always succeed.
Therefore the first loop should go over keys of a table and add them into the resulting array. The feature {ARRAY}.force may be of interest in this case. The addition of the new elements should not make any "holes" in the array though. One way to achieve this is to add a new element right after the current upper bound of the array.
The sorting loop is also incorrect. Here the situation is reversed compared to the previous one: sorting cannot be done in a single loop, at least two nested loops are required. The template seems to be using Insertion sort, its algorithm can be found elsewhere.
EDIT: the original answer referred to {ARRAY}.extend instead of {ARRAY}.force. Unfortunately {ARRAY}.extend is not generally available, but a.extend (x) would have the same effect as a.force (x, a.upper + 1).