How to iterate array in precondition? - eiffel

I want to iterate through array in a precondition.
But It seems precondition part doesn't allow use of "from" and "across" syntax.
Is there a way to iterate through array in precondition?
insert_last (s: STRING)
require
new_is_longer_than_prevs:
-- here I want to iterate through array "arr" and if length of s is longer than all other previously stored string values in array
do
arr.force (s, arr.upper + 1)
end

The version suggested in the other reply works in most cases (it assumes the lower index of the array is 1). However, the across loop can be used directly on the array rather than on its index range:
new_is_longer_than_prevs:
across arr as c all s.count > c.item.count end
This version works for any lower index and is slightly more efficient at run-time.

You can use 'across ... as ... all ... end' or 'across ... as ... some ... end' in precondition and postcondition. The 'all' version is used to valid if a condition is True for every iteration and the 'some' version is used to valid if the condition is True for at least one iteration. You can use some thing like this in your code:
insert_last (s: STRING)
require
new_is_longer_than_prevs:
across arr.lower |..| arr.upper as la_index all s.count > arr[la_index.item].count end
do
arr.force (s, arr.upper + 1)
end

Related

How do I use an across loop in post condition to compare an old array and new array at certain indices?

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

How do you initialize an array with only one element, without hardcoding the first index?

I am trying to pass an array of Unbounded_String to a function, and I don't care about the range of the index, as the function is going to loop over each element.
The (element1, element2) syntax automatically starts at the first index value in the range, then increments for the second value given, which works fine for more than one value. However, for a single value, this cannot be used as the parentheses are considered superfluous.
This code shows the error messages for each of the attempts I have made. (1) works, but (2), the preferable syntax for passing a single-element array, does not. (3) works, and is given as an answer to this similar question. However, this hardcodes the first index of the range into the calling side; if the String_Array implementation changes, all the call-sites have to be changed, even though they don't care about the index values used.
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure Main is
function "+"(S: String) return Ada.Strings.Unbounded.Unbounded_String
renames Ada.Strings.Unbounded.To_Unbounded_String;
type String_Array is array (Positive range <>) of Unbounded_String;
procedure Foo(input : in String_Array) is
begin
null;
end Foo;
begin
Foo((+"one", +"two")); --(1)
--Foo((+"only")); --(2) positional aggregate cannot have one component
Foo((1 => +"only")); --(3)
--Foo((String_Array'First => +"only")); --(4) prefix for "First" attribute must be constrained array
--Foo((String_Array'Range => +"only")); --(5) prefix for "Range" attribute must be constrained array
--Foo((String_Array'Range'First => +"only")); --(6) range attribute cannot be used in expression
--Foo((String_Array'Range'Type_Class'First => +"only")); --(7) range attribute cannot be used in expression
end Main;
What you want (2) is indeed impossible as it could be mistaken for a parenthesized expression (see http://www.adaic.org/resources/add_content/standards/12aarm/html/AA-4-3-3.html note 10).
If you really want to avoid expression (3) for the reasons you stated, as workaround, you could define a function to handle the one-element array case:
function Singleton_String_Array (S: String) return String_Array is ((1 => + S));
-- one element call
Foo(Singleton_String_Array ("only"));
It reuse your expression (3) but the first index hardcoding is no longer done on call site.
You can also overload your foo function to handle the special one-element case:
procedure Process_String (input : in Ada.Strings.Unbounded.Unbounded_String) is
begin
null;
end Process_String;
procedure Foo(input : in String_Array) is
begin
for string of input loop
Process_String (string);
end loop;
end Foo;
procedure Foo(input : in Ada.Strings.Unbounded.Unbounded_String) is
begin
Process_String (input);
end Foo;
-- One element call
Foo(+"only");
The short answer is that all array objects must be constrained, which means callers usually have to decide on the array bounds.
However, you know the index type, and could do
Foo((Positive'First => +"only"));
which doesn't really answer your question, since someone may still fiddle with the array range, and there's not really any guard against that.
Adding a new subtype as the range may be a viable solution, though:
subtype String_Array_Range is Positive;
type String_Array is array (String_Array_Range range <>) of Unbounded_String;
...
Foo((String_Array_Range'First => +"only"));
Any fiddling can now be done on the String_Array_Range subtype without affecting any callers. But there's still no guarantee against evil programmers changing the index type of the array itself...
type String_Array is array (Positive range <>) of Unbounded_String;
Declares a type of array but doesn't provide the size.
Remember that an array has a static size.
So String_Array'First and String_Array'Range don't match to anything.
If you declared
type my_String_Array is String_Array(1 .. 35);
my_arr : my_String_Array
Then my_arr'First denotes 1 and my_arr'Range denotes 1..35.
As long as you don't put a contraint on the type, you won't have access to these attributes.

Post-Condition Violation with Feature in Eiffel

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).

Lua table.getn() returns 0?

I've embedded Lua into my C application, and am trying to figure out why a table created in my C code via:
lua_createtable(L, 0, numObjects);
and returned to Lua, will produce a result of zero when I call the following:
print("Num entries", table.getn(data))
(Where "data" is the table created by lua_createtable above)
There's clearly data in the table, as I can walk over each entry (string : userdata) pair via:
for key, val in pairs(data) do
...
end
But why does table.getn(data) return zero? Do I need to insert something into the meta of the table when I create it with lua_createtable? I've been looking at examples of lua_createtable use, and I haven't seen this done anywhere....
table.getn (which you shouldn't be using in Lua 5.1+. Use the length operator #) returns the number of elements in the array part of the table.
The array part is every key that starts with the number 1 and increases up until the first value that is nil (not present). If all of your keys are strings, then the size of the array part of your table is 0.
Although it's a costly (O(n) vs O(1) for simple lists), you can also add a method to count the elements of your map :
>> function table.map_length(t)
local c = 0
for k,v in pairs(t) do
c = c+1
end
return c
end
>> a = {spam="data1",egg='data2'}
>> table.map_length(a)
2
If you have such requirements, and if your environment allows you to do so think about using penlight that provides that kind of features and much more.
the # operator (and table.getn) effectivly return the size of the array section (though when you have a holey table the semantics are more complex)
It does not count anything in the hash part of the table (eg, string keys)
for k,v in pairs(tbl) do count = count + 1 end

Largest word in an Array

Hey i'm having problems creating a simple button for a programme which finds the largest word in an array and puts it into a textbox. I've done most of the coding (I hope) was wondering if somebody could help me actually with the code that finds the largest text in the array as I am struggling the most with that.
Private Sub btnLongName_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLongName.Click
Dim LongName As String
Dim LengthOfLongestName As Integer
Dim UltimateName As String
For i As Integer = 0 To 5
LongName = Members(i).Name
LengthOfLongestName = Len(LongName)
If Members(i).Name.Length > LengthOfLongestName Then
End If
Next i
txtResult.Text = "The longest name is " & UltimateName & " ."
End Sub
End Class
Thanks for your time - its for college homework, struggling big time on it :(
edit: I've edited the code
Since this is homework, I won't write the code for you; instead I'll try to give you some hints that will point you in the right direction.
Declare a variable of an appropriate type to hold the <longest value so far>, initialize it with the "shortest" value for that type.
Loop through all the values in the array (perhaps with a For or For Each loop)
Pseudo-code for the inside your loop:
If the Length of <the value being checked> exceeds _
the Length of the <longest value so far> Then
Assign <the value being checked> to the <longest value so far>
End If
When the loop finishes, the <longest value so far> will be the longest value in the array.
Notes
You can use MSDN as a reference on how to use a For loop or a For Each loop (If you haven't learned For loops yet, you can also use a Do Loop)
<the value being checked> will be different on each iteration through the loop; it should correspond to each consecutive value in your array. You can verify that this is working by setting a breakpoint.
You can get the length of a string by saying myString.Length
If you've learned about Functions, consider writing a function that takes an array as a parameter, and returns the longest value in the array.
There are certainly ways you could do this with LINQ, but I don't think that is the goal of the assignment ;-]
In response to Edit 1:
Your If statement needs to be inside of some sort of loop (For, For Each, Do, etc) I think this is the key concept that you are missing.
Instead of comparing LongName.Length to LengthOfLongestName, you need to compare the length of an entry in your array to LengthOfLongestName
You're on the right track with Members(0).Name.Length, but you can't just check element 0; you have to check every element in the array.
Given your current code, you'll probably be assigning <An entry in your array>.Name to LongName
The last index in a one-dimensional array is <array>.Length - 1 or <array>.GetUpperBound(0).
The following doesn't really address anything in your assignment, but I hope it will give you some ideas on how to go through all the items in your list:
' A For loop that does a message box for each of the numbers from 0 to 5 '
For i as Integer = 0 To 5
MessageBox.Show(i)
Next i
' Code that does a message box with the names of the 2nd, 3rd and last '
' entries in Members '
' (Remember that the first item is at 0, the second item is at 1, etc...) '
MessageBox.Show(Members(1).Name)
MessageBox.Show(Members(2).Name)
MessageBox.Show(Members(Members.GetUpperBound()).Name)
In response to Edit 2:
You're getting warmer...
You should only update LongName and LengthOfLongName if the current value is the longest you've seen so far (i.e. they should be assigned inside of the If statement)
You have to go to the last index of the array, not 5. See above (the response to your first edit) on how to get that last index.
You don't really need the UltimateName variable; you can just use LongName ;-]
You might want to use <stringVariable>.Length instead of Len(<stringVariable>) to be consistent.
What you are missing is a loop that checks each member, and putting the If statement inside it and make it compare the length of the name to the longest name that you have found so far. If the name is longer, put it in the variable for the longest found, and update the length variable.
You can either initialise the variables with the name of the first member and loop from the second member and on, or you can initialise the variables with an empty string and loop all the members. Personally I prefer the latter one.

Resources