0

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.

1 Answers1

1

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:

  1. The postcondition does not assume that the array starts at 1, and uses array.lower and array.upper instead.

  2. 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
Alexander Kogtenkov
  • 5,770
  • 1
  • 27
  • 35
  • I think that it should be: old (array.twin) [array.lower] – Eric Bezault Sep 29 '19 at 05:32
  • To avoid this mistake, you could have factored out the code like that: c.item = (old array.twin) [if c.target_index < array.upper then c.target_index + 1 else array.lower end] – Eric Bezault Sep 29 '19 at 05:36
  • If bracket expressions have lower precedence than old expressions, the code should be changed as suggested. Otherwise, (e.g., if bracket expressions have precedence of qualified feature calls) the code should be OK. Anyway, the variant with a single old expression looks better indeed. – Alexander Kogtenkov Sep 29 '19 at 09:25
  • My concern was not so much on precedence, but rather on the fact that you forgot `.twin` in the else-branch. `old array` is the same object as `array`, so you should use `old (array.twin)`. – Eric Bezault Sep 30 '19 at 06:47
  • `old array [array.lower]` interpreted as `old (array [array.lower])` obtains an element from the original array, not from the updated one. Adding a call to `twin` here does not change anything because `old (array [array.lower]) = old (array.twin [array.lower])`. – Alexander Kogtenkov Sep 30 '19 at 08:34
  • Oh, I see. So I checked in the ECMA standard, and the target of a bracket expression cannot be an old expression, unless parenthesized. So you are right, `old array [array.lower]` cannot interpreted as `(old array) [array.lower]`, and as a consequence should be interpreted as `old (array [array.lower])`. – Eric Bezault Sep 30 '19 at 11:23
  • If i was to do `c.item ~ if c.target_index < array.upper then (old array.deep_twin) [c.target_index + 1] else (old array.deep_twin) [array.lower]` Would that produce the same result? – Ed Shirinian Oct 01 '19 at 16:43
  • @EdShirinian Yes, it would produce the same result. However, the postcondition with `=` rather than `~` is a bit stronger, as it ensures that all original elements are preserved. The condition with `~` says only that the elements are equal, but are not necessary the same objects. This slight difference might be essential when items are of reference type, and when they could be modified. In other words, the postcondition with `=` gives less freedom for possible implementation. In particular, with `=`, such an implementation cannot put twins of original objects into the array. – Alexander Kogtenkov Oct 01 '19 at 19:16