Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

syntaxic exploration

257 views
Skip to first unread message

Mehdi Saada

unread,
Dec 20, 2017, 12:28:20 PM12/20/17
to
I'm being modelizing polynomes. I've got those two things: p_poly_v1_g.adb:51:21: iterable name cannot be a discriminant-dependent component of a mutable object
Ah ? So I can't write that ?
return RESULTAT : T_POLYNOME := POLY do
for ELEMENT of RESULTAT.COEF loop
ELEMENT := - ELEMENT;
end loop;
end return;

I've got that too:
p_poly_v1_g.adb:59:86: selector name should be identifier or "others"

return RES: T_POLYNOME := (T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE),POLY_A.COEF'Range => POLY_A.COEF, others => Nulle) do

I thought X'Range was strictly equivalent to X'First..X'Last ? Of course, poly is a mutable article, with two components, Degre (derived from NATURAL), and COEF, array.

I have that too:
for IND in POLY_R.COEF'Range loop
POLY_R.COEF(IND) := POLY_R.COEF(IND)*(IND+1);
end loop;

COEF's elements of T_RATIONNEL type, themselves record types. IND is of type T_DEGRE, so why do I have that:
p_poly_v1_g.adb:73:43: expected type "Standard.Integer"
p_poly_v1_g.adb:73:43: found private type "T_Rationnel"

Thanks !

Mehdi Saada

unread,
Dec 20, 2017, 12:32:25 PM12/20/17
to
Hum, how do I write a array component with one element ? Something like (_discriminant_, (0)) (array starting from 0) doesn't work. It shows:
p_poly_v1_g.adb:40:63: positional aggregate cannot have one component
p_poly_v1_g.adb:40:63: write instead "T_Vect_Coef'First => ..."

But Array_type'First..Array_type'Last doesn't seem fine when there's just one element... Or better, none. How do I write that for an empty array ??

Niklas Holsti

unread,
Dec 20, 2017, 3:05:15 PM12/20/17
to
On 17-12-20 19:28 , Mehdi Saada wrote:
> I'm being modelizing polynomes.

It is difficult to help you if you don't show the type declarations! So
my suggestions below are not sure to work.

> I've got those two things:
> p_poly_v1_g.adb:51:21: iterable name cannot be a discriminant-dependent component of a mutable object
> Ah ? So I can't write that ?
> return RESULTAT : T_POLYNOME := POLY do
> for ELEMENT of RESULTAT.COEF loop
> ELEMENT := - ELEMENT;
> end loop;
> end return;

You are trying to iterate over RESULTAT.COEF, which seems to be an array
with an index range that depends on RESULTAT.DEGRE. The error message
tells you that this is not allowed, because RESULTAT is mutable (not
constant) and therefore RESULTAT.DEGRE and thus the index range of
RESULTAT.COEF could change during the iteration. The Annotated Ada
Reference Manual gives the following rationale for this rule:

"Reason: This is the same rule that applies to renames; it serves the
same purpose of preventing the object from disappearing while the
iterator is still using it."

It should work if you change the loop to be an ordinary loop over
RESULTAT.COEF'Range, with indexing of its elements. There will then be
(at least in principle) run-time checks to ensure that the indexing is
legal even if RESULTAT has changed. In practice these checks may be
optimised away.

> I've got that too:
> p_poly_v1_g.adb:59:86: selector name should be identifier or "others"
>
> return RES: T_POLYNOME :=
> (T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE),
> POLY_A.COEF'Range => POLY_A.COEF, others => Nulle) do

Here you have omitted the '(' ')' around the nested array aggregate. It
should be

return RES: T_POLYNOME :=
(T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE),
(POLY_A.COEF'Range => POLY_A.COEF, others => Nulle)) do

> I thought X'Range was strictly equivalent to X'First..X'Last ? Of
> course, poly is a mutable article, with two components, Degre
> (derived from NATURAL), and COEF, array.

Which is why the aggregate for COEF is nested and must have its own
parentheses.

> I have that too:
> for IND in POLY_R.COEF'Range loop
> POLY_R.COEF(IND) := POLY_R.COEF(IND)*(IND+1);
> end loop;
>
> COEF's elements of T_RATIONNEL type, themselves record types.
> IND is of type T_DEGRE, so why do I have that:
> p_poly_v1_g.adb:73:43: expected type "Standard.Integer"
> p_poly_v1_g.adb:73:43: found private type "T_Rationnel"

Again hard to be sure, as you didn't show the actual type declarations,
but what is your question, really? Is it (1) why the compiler gives you
an error, or (2) why the first message says "Standard.Integer" instead
of "T_DEGRE"?

The answer to the first question is that you have not defined an "*"
operator for these operand types, or this operator is not visible at
this point in the code.

The answer to the second question is probably that the index type of the
COEF array is a subtype of Standard.Integer, not a type of its own.
GNAT's error messages of this kind refer to the underlying type, not to
the subtype, because the operator is identified from the type and not
from the subtype.

--
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
. @ .

Niklas Holsti

unread,
Dec 20, 2017, 3:08:23 PM12/20/17
to
On 17-12-20 19:32 , Mehdi Saada wrote:
> Hum, how do I write a array component with one element ?

To write an array aggregate with one component, you must use the "named
aggrage" form and define the index as well as the value, as in

(1 => Xxx)

where "1" is the index and "Xxx" is the component value for this index.

Mehdi Saada

unread,
Dec 20, 2017, 5:18:49 PM12/20/17
to
Thanks. I should have digged and post all the definitions. I'll post more details later. I had no really use of named aggregate before... How do we denote an empty array in an aggregate ?
It couldn't be applicable here, since COEF's bounds in delimited by a determinant, I'm just curious about extreme syntax cases...
Message has been deleted
Message has been deleted

Mehdi Saada

unread,
Dec 20, 2017, 6:39:09 PM12/20/17
to
Le mercredi 20 décembre 2017 23:48:45 UTC+1, Mehdi Saada a écrit :
> Sorry. How about the empty array in an aggregate ?
> I found the rule very sensical. But I wonder why the more basic iterative construct, can have checks at run time, and not the more advanced one ?
>
> subtype T_Degre is Natural range 0..Max;
> type T_Vect_Coef is array (T_Degre range <>) of T_Rationnel;
> type T_Polynome (Degre : T_Degre := 0) is
> record
> Coef : T_Vect_Coef (0..Degre) := (others => Nulle);
> end record;
> type T_Rationnel is record
> Numerateur : T_Entier := 0;
> Denominateur: T_Entier_Pos := 1;
> end record; -- (t_entier est un type paramêtre formel du générique.
> subtype T_Entier_Pos is T_Entier range 1..T_Entier'Last;
>
> All the operations (*,-,- unaire, /, abs, relations d'ordre) are provided for T_Rationnel in the same package as the type itself, in the same package.

Mehdi Saada

unread,
Dec 20, 2017, 7:35:48 PM12/20/17
to
Done what you said, it compiled. I had to do T_ENTIER(IND+1) to make "*" works. I don't get why, for I am not sure where this type is coming from...
The formal parameter of the generic unit where it comes is : type T_Entier is range <>;
but the package in instanciated with a (<>) actual parameter... I did wrote the generic, but not the instanciation part. I can't see a default parameter, what effect might (<>) has then ?

Randy Brukardt

unread,
Dec 21, 2017, 2:18:39 AM12/21/17
to
"Mehdi Saada" <0012...@gmail.com> wrote in message
news:0d33e631-e63d-4346...@googlegroups.com...
> I'm being modelizing polynomes. I've got those two things:
> p_poly_v1_g.adb:51:21: iterable name cannot be a discriminant-dependent
> component of a mutable object
> Ah ? So I can't write that ?
> return RESULTAT : T_POLYNOME := POLY do
> for ELEMENT of RESULTAT.COEF loop

Correct. You can't write this, because the iterable item could disappear in
the body of the loop. (If someone assigned a different value to the mutable
variable.) This is the same rule as used for renames (you can't rename this
component, either, for the same reason).

...
> I've got that too:
> p_poly_v1_g.adb:59:86: selector name should be identifier or "others"
>
> return RES: T_POLYNOME :=
> (T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE),POLY_A.COEF'Range => POLY_A.COEF,
> others => Nulle) do
>
> I thought X'Range was strictly equivalent to X'First..X'Last ?

'Range is rather a wart in the language; it can only be used in a few
specific places. That's because X'Range is neither a subtype constraint nor
a value, and those are the only things with general rules.

However, an aggregate choice is one the places that 'Range can be used, so
you're obviously doing something else wrong. (Which I can't see given the
partial code here.)

Randy.



Randy Brukardt

unread,
Dec 21, 2017, 2:21:53 AM12/21/17
to
"Mehdi Saada" <0012...@gmail.com> wrote in message
news:0fa75dd5-599b-4a38...@googlegroups.com...
Ada has no good way to write an empty array. I usually use (1 .. 0 => <>)
for that, but nothing is really satisfying.

For a single element, write a single choice (a choice doesn't have to be a
range!) The compiler error message gives a suggestion.

Randy.


Randy Brukardt

unread,
Dec 21, 2017, 2:24:56 AM12/21/17
to
"Mehdi Saada" <0012...@gmail.com> wrote in message
news:baca5473-6224-481e...@googlegroups.com...
>Sorry. How about the empty array in an aggregate ?
>
>subtype T_Degre is Natural range 0..Max;
>type T_Vect_Coef is array (T_Degre range <>) of T_Rationnel;
>type T_Polynome (Degre : T_Degre := 0) is
>record
> Coef : T_Vect_Coef (0..Degre) := (others => Nulle);

The above aggregate is fine for an empty array. The bounds come from the
object being initialized.

Randy.


Niklas Holsti

unread,
Dec 21, 2017, 2:44:31 AM12/21/17
to
But (just to avoid a possible misunderstanding on Mehdi's side) the
above aggregate can never be an empty array, because Degre >= 0. An
array with index range 0 .. Degre is empty only if Degre < 0.

Jeffrey R. Carter

unread,
Dec 21, 2017, 11:24:54 AM12/21/17
to
On 12/21/2017 08:21 AM, Randy Brukardt wrote:
>
> Ada has no good way to write an empty array. I usually use (1 .. 0 => <>)
> for that, but nothing is really satisfying.

Except perhaps the empty array aggregate

""

(only for string types, alas).

--
Jeff Carter
"Death awaits you all, with nasty, big, pointy teeth!"
Monty Python & the Holy Grail
20

G. B.

unread,
Dec 21, 2017, 2:23:07 PM12/21/17
to
uRandy Brukardt <ra...@rrsoftware.com> wrote:
> "Mehdi Saada" <0012...@gmail.com> wrote:
>>
>> I thought X'Range was strictly equivalent to X'First..X'Last ?
>
> 'Range is rather a wart in the language; it can only be used in a few
> specific places.

So, *for* is a wart, too, then. ;-)
It, too, can only be used in a few specific places.
But, together with an object’s Range
attribute, we get an unequivocal statement that
something applies to everything in range.
The range does not need to be programmed by
hand; that would be verbose. I like to reserve
‘Last to one-off specification of subranges.


bj.mo...@gmail.com

unread,
Dec 21, 2017, 6:46:55 PM12/21/17
to
I've been thinking lately that its a bit of a wart that 'Range or at least range syntax cant be used in more places.

Specifically, I have been thinking of container iteration.

It is currently awkward to iterate a subset of a container, given two cursors, a start and and end cursor.

for a discrete subtype, we can express in Ada;

for I in 5 .. 10 loop
...
end loop


Where the bounds of iteration can be a subset of the values of a subtype.

It seems one ought to be able to do the same with cursors.

for Position in Cursor1 .. Cursor2 loop
list (Position) := ...
end loop

Note: This currently isn't legal in Ada.

Otherwise, one has to write a while loop, which is a bit awkward.

Position : Cursor := Cursor1;

Iteration_Loop :
loop
List (Position) := ...
exit Iteration_Loop when Position := Cursor2;
Next (Position);
end loop;

One can almost do this with the current containers, with Ada 2012
iterator syntax because they generally
have an Iterate primitive that accepts a start cursor.

eg.

for I in List.Iterate(Start => Cursor1) loop
List (I) := ...
end loop;

But we currently do not have Iterate primitives in the standard containers
that also accept an End cursor, so these calls allow one to start somewhere
in the middle of a container and iterate to the end, but not to stop
earlier. It seems like it would be relatively easy to
add such calls.

Then we could write;

for I in List.Iterate(Start => Cursor1, Finish => Cursor2) loop
List (I) := ...
end loop;

Perhaps the intent was to use exit to exit the loop earlier when you hit the
second cursor.

for I in List.Iterate(Start => Cursor1) loop
List (I) := ...
exit when I = Cursor2;
end loop;

I think that's somewhat satisfactory.

But I still think it would be nice to take this one step further and
be able to add syntactic sugar to express this with range syntax, as
shown in the example above.

I don't know how useful this would be, or how easy it would be to add to the language, but one place I would use it is when
trying to iterate through containers in parallel where workers are each given a subset of the container by providing each worker with a start and end cursor.

Brad

Robert Eachus

unread,
Dec 22, 2017, 12:01:32 AM12/22/17
to
On Thursday, December 21, 2017 at 11:24:54 AM UTC-5, Jeffrey R. Carter wrote:
> On 12/21/2017 08:21 AM, Randy Brukardt wrote:
> >
> > Ada has no good way to write an empty array. I usually use (1 .. 0 => <>)
> > for that, but nothing is really satisfying.
>
> Except perhaps the empty array aggregate
>
> ""
>
> (only for string types, alas).

Is it a reasonable language improvement request to allow "" for other empty arrays? Another possibility is the reserved word null. If that were allowed I might even use it in place of "" for strings, because that is not different enough from '"' when reading, depending on the type face used.


Mehdi Saada

unread,
Dec 22, 2017, 8:31:28 AM12/22/17
to
It compiled, but I entered the agonizing world of debugging run time erros...
What in that:
return RES: T_POLYNOME :=
(T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE),POLY_A.COEF) do
for I in POLY_B.COEF'Range loop
RES.COEF(I) := RES.COEF(I) + POLY_B.COEF(I);
end loop;
RES := POLY_REDUIT(RES);
end return;

is wrong ? Because the result is.
P1: 1 -2X
P2: -1
POLY1 + (POLY1 * POLY2) = 9 -15X +2X** 2 -- wrong.

It ends with raised CONSTRAINT_ERROR : p_poly_v1_g.adb:63 length check failed
59 function "+" (POLY_A, POLY_B: T_POLYNOME) return T_POLYNOME is
60 begin
61 return RES: T_POLYNOME :=
62 (T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE),POLY_A.COEF) do
63 for I in POLY_B.COEF'Range loop
64 RES.COEF(I) := RES.COEF(I) + POLY_B.COEF(I);
65 end loop;
66 RES := POLY_REDUIT(RES);
67 end return;

Mehdi Saada

unread,
Dec 22, 2017, 1:00:37 PM12/22/17
to
I must add too,
T_Entier is range -40 000..40 000; is the scalar type at the core of T_Rationnel, the type of elements of T_POLYNOME.COEF. But I doubt it went beyond these bounds anywhere close.

Niklas Holsti

unread,
Dec 22, 2017, 1:27:05 PM12/22/17
to
On 17-12-22 15:31 , Mehdi Saada wrote:
> It compiled, but I entered the agonizing world of debugging run time erros...
> What in that:
> return RES: T_POLYNOME :=
> (T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE),POLY_A.COEF) do
> for I in POLY_B.COEF'Range loop
> RES.COEF(I) := RES.COEF(I) + POLY_B.COEF(I);
> end loop;
> RES := POLY_REDUIT(RES);
> end return;
>
> is wrong ? Because the result is.
> P1: 1 -2X
> P2: -1
> POLY1 + (POLY1 * POLY2) = 9 -15X +2X** 2 -- wrong.

I can't help with that, because you do not show the input values, nor
the output procedures.

> It ends with raised CONSTRAINT_ERROR : p_poly_v1_g.adb:63 length check failed
> 59 function "+" (POLY_A, POLY_B: T_POLYNOME) return T_POLYNOME is
> 60 begin
> 61 return RES: T_POLYNOME :=
> 62 (T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE),POLY_A.COEF) do

RES.COEF should have a length of RES.DEGRE, but the aggregate above
supplies an array (POLY_A.COEF) with a length of POLY_A.DEGRE. If
POLY_B.DEGRE is larger than POLY_A.DEGRE, these lengths are different.

Mehdi Saada

unread,
Dec 22, 2017, 3:25:19 PM12/22/17
to
How about that, then:
return RES: T_POLYNOME :=
T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE),(0..POLY_A.COEF'Last => POLY_A.COEF, others => Nulle)) do
It says "p_poly_v1_g.adb:63:51: dynamic or empty choice in aggregate must be the only choice"
I would like to write this (POLY_A.COEF'Range => POLY_A.COEF, others => Nulle) but I know "POLY_A.COEF'Range =>" must be followed by a value of T_RATIONNEL, not a slice. But I don't know how to write "PUT POLY_A.COEF there, and as much Nulle as needed to fill the void if by chance POLY_B.DEGRE > POLY_A.DEGRE"

Simon Clubley

unread,
Dec 22, 2017, 4:15:20 PM12/22/17
to
On 2017-12-22, Robert Eachus <riea...@comcast.net> wrote:
> On Thursday, December 21, 2017 at 11:24:54 AM UTC-5, Jeffrey R. Carter wrote:
>> On 12/21/2017 08:21 AM, Randy Brukardt wrote:
>> >
>> > Ada has no good way to write an empty array. I usually use (1 .. 0 => <>)
>> > for that, but nothing is really satisfying.
>>
>> Except perhaps the empty array aggregate
>>
>> ""
>>
>> (only for string types, alas).
>
> Is it a reasonable language improvement request to allow "" for other
> empty arrays?


No. That would be too confusing IMHO because it would initially appear
to be a string type to anyone unfamiliar with that part of the code
and that just feels _way_ wrong.

> Another possibility is the reserved word null. If that were
> allowed I might even use it in place of "" for strings, because that is not
> different enough from '"' when reading, depending on the type face used.
>

null is an interesting option for the empty array. However, for your
strings idea, would people unfamiliar with the code and this new usage
of null read this new usage as a null pointer instead of an empty string ?
That could be confusing.

The 1..0 notation is ugly but anyone who sees it knows instantly
exactly what the original programmer meant. Any cleaner replacement
should instantly come across as the empty array without the
possibility of confusion because someone reused Ada syntax
for this in an ambiguous way.

Simon.

--
Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP
Microsoft: Bringing you 1980s technology to a 21st century world

Niklas Holsti

unread,
Dec 22, 2017, 5:11:52 PM12/22/17
to
On 17-12-22 23:15 , Simon Clubley wrote:
> On 2017-12-22, Robert Eachus <riea...@comcast.net> wrote:
>> On Thursday, December 21, 2017 at 11:24:54 AM UTC-5, Jeffrey R. Carter wrote:
>>> On 12/21/2017 08:21 AM, Randy Brukardt wrote:
>>>>
>>>> Ada has no good way to write an empty array. I usually use (1 .. 0 => <>)
>>>> for that, but nothing is really satisfying.
>>>
>>> Except perhaps the empty array aggregate
>>>
>>> ""
>>>
>>> (only for string types, alas).
>>
>> Is it a reasonable language improvement request to allow "" for other
>> empty arrays?
>
>
> No. That would be too confusing IMHO because it would initially appear
> to be a string type to anyone unfamiliar with that part of the code
> and that just feels _way_ wrong.

I agree.

>> Another possibility is the reserved word null.

[snip]

> null is an interesting option for the empty array. However, for your
> strings idea, would people unfamiliar with the code and this new usage
> of null read this new usage as a null pointer instead of an empty string ?
> That could be confusing.

I agree, again.

I think the aggregate form (null array) was suggested some time ago, in
analogy with the existing (null record). It looks good to me, but as I
remember, there was some objection. One problem is that if the index
type has exactly one value, then a null array with that index type
cannot exist (because then A'First = A'Last for any such array A).

> The 1..0 notation is ugly but anyone who sees it knows instantly
> exactly what the original programmer meant.

Yes, but it becomes rather more ugly if the index type is an
enumeration, or a generic formal discrete type, something like

Thingummybob'Succ (Thingummybob'First) .. Thingummybob'First

Note that Thingummybob'Last .. Thingummybob'First won't be a null range
if Thingummybob has exactly one value.

> Any cleaner replacement
> should instantly come across as the empty array without the
> possibility of confusion because someone reused Ada syntax
> for this in an ambiguous way.

I think (null array) satisfies that. I wish it could be introduced.

Niklas Holsti

unread,
Dec 22, 2017, 5:33:13 PM12/22/17
to
On 17-12-22 22:25 , Mehdi Saada wrote:
> How about that, then:
> return RES: T_POLYNOME :=
> T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE),(0..POLY_A.COEF'Last => POLY_A.COEF, others => Nulle)) do
> It says "p_poly_v1_g.adb:63:51: dynamic or empty choice in aggregate must be the only choice"

(You have omitted the opening '(', after the ":=".)

Yes, the range 0..POLY_A.COEF'Last is dynamic, so the error message is
valid.

Moreover, here, too, the expression after the index range should be a
T_RATIONNEL, not an array like POLY_A.COEF. The semantics of an
association A .. B => X, in an array aggregate, is to give the same
value, X, to all elements in the index range A .. B, so it is _not_ what
you want to do here.

> I would like to write this (POLY_A.COEF'Range => POLY_A.COEF, others => Nulle)

That is equivalent, and should give you the same error, and also be
wrong in the same way.

> but I know "POLY_A.COEF'Range =>" must be followed by a value
> of T_RATIONNEL, not a slice.

The same is true for "0..POLY_A.COEF'Last =>".

> But I don't know how to write "PUT POLY_A.COEF there, and as much
> Nulle as needed to fill the void if by chance POLY_B.DEGRE > POLY_A.DEGRE"

If you insist on using an aggregate to initialise RES, you could use
this form, with array concatenation ("&" operator) for the COEF component:

return RES : T_POLYNOME :=
(T_DEGRE'MAX (POLY_A.DEGRE, POLY_B.DEGRE),
POLY_A.COEF
& (1 .. Xxx'MAX (0, POLY_B.DEGRE - POLY_A.DEGRE) => Nulle));

where Xxx is the index type of the COEF array (I forget its name).

However, I would do it differently; I would declare RES with just its
discriminant:

return
RES : T_POLYNOME (T_DEGRE'MAX (POLY_A.DEGRE, POLY_B.DEGRE))
do
-- Assign values to RES.COEF using loops, something like:

for C in RES.COEF'Range loop
RES.COEF(C) :=
(if C in POLY_A.COEF'Range then POLY_A.COEF(C) else 0)
+ (if C in POLY_B.COEF'Range then POLY_B.COEF(C) else 0);
end loop;
end return;

Dmitry A. Kazakov

unread,
Dec 22, 2017, 5:52:03 PM12/22/17
to
On 2017-12-22 23:11, Niklas Holsti wrote:

> I think the aggregate form (null array) was suggested some time ago, in
> analogy with the existing (null record).

These two totally different things. "null record" is not a record
aggregate (a value of record type), it is a type construct (a value of a
record type's type). Correspondingly "null array" would mean a type of
arrays rather than an empty aggregate of an array type.

> I think (null array) satisfies that. I wish it could be introduced.

If "" is QK, why () isn't?

Empty : My_Array := ();

It could be an attribute as well:

Empty : My_Array := My_Array'Null;

(You are right about indices. String has the lower bound set to 1. That
gives an unambiguous range of an empty string. For general case arrays
it is ambiguous)

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

Randy Brukardt

unread,
Dec 22, 2017, 6:45:26 PM12/22/17
to
<bj.mo...@gmail.com> wrote in message
news:b81c7f6f-dda7-4cd1...@googlegroups.com...
...
> Otherwise, one has to write a while loop, which is a bit awkward.
>
> Position : Cursor := Cursor1;
>
> Iteration_Loop :
> loop
> List (Position) := ...
> exit Iteration_Loop when Position := Cursor2;
> Next (Position);
> end loop;

This works, of course.

> One can almost do this with the current containers, with Ada 2012
> iterator syntax because they generally
> have an Iterate primitive that accepts a start cursor.
>
> eg.
>
> for I in List.Iterate(Start => Cursor1) loop
> List (I) := ...
> end loop;

This is true, too. But you just need to exit the loop at the end cursor to
get the semantics you want:

for I in List.Iterate(Start => Cursor1) loop
List (I) := ...
exit when I = Cursor2;
end loop;

Ada doesn't care what kind of loop you use an exit in!

If there was to be a complaint here, it's that you can't easily use the
element form ("of") with an ending cursor (because the cursor has to be
explicit). But it seems weird to me to want to use cursors and yet hide them
at the same time.

Conclusion: There isn't a need for an explicit "ending cursor", because an
exit works fine for that in the cursor form. There still might be some sort
of consistency argument, but we didn't add it originally since the exit is
so easy to use for ending a loop "early".

Randy.


Mehdi Saada

unread,
Dec 22, 2017, 8:47:16 PM12/22/17
to
Thank you, Niklas Holsti. Your way of doing it is just beautiful, as always with conditional expressions. Also, I simply forgot about the & operator for non-strings arrays.
Still:
p_poly_v1_g.adb:64:92: expected private type "T_Rationnel" defined at p_rationnels_g.ads:11, instance at p_poly_v1_g.ads:19
p_poly_v1_g.adb:64:92: found type universal integer
p_poly_v1_g.adb:64:149: expected private type "T_Rationnel" defined at p_rationnels_g.ads:11, instance at p_poly_v1_g.ads:19
p_poly_v1_g.adb:64:149: found type universal integer
gnatmake: "p_poly_v1_g.adb" compilation error

with the same sentences as in your exemple. At least, I can't see the difference with what you wrote.
return RES: T_POLYNOME (T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE)) do
for I in RES.COEF'Range loop
RES.COEF(I) := (if I in POLY_A.COEF'Range then POLY_A.COEF(I) else 0) + (if I in POLY_B.COEF'Range then POLY_B.COEF(I) else 0);

Niklas Holsti

unread,
Dec 23, 2017, 2:15:36 AM12/23/17
to
On 17-12-23 00:51 , Dmitry A. Kazakov wrote:
> On 2017-12-22 23:11, Niklas Holsti wrote:
>
>> I think the aggregate form (null array) was suggested some time ago,
>> in analogy with the existing (null record).
>
> These two totally different things. "null record" is not a record
> aggregate (a value of record type), it is a type construct (a value of a
> record type's type).

Yes and no. Compare

type Nothing is null record; -- Last choice in RM 3.8(3).

and

N : Nothing := (null record); -- Last choice in RM 4.3.1(3).

So "null record" in a type declaration is a record_definition (RM
3.8(3)) but it can also occur in an expression as a
record_component_association_list (RM 4.3.1(3)). Thus "(null record)" is
a record aggregate.

> Correspondingly "null array" would mean a type of
> arrays rather than an empty aggregate of an array type.
>
>> I think (null array) satisfies that. I wish it could be introduced.
>
> If "" is QK, why () isn't?

Could be, but parentheses are already so overloaded (which quotes are
not) that I like (null array) better.

> It could be an attribute as well:
>
> Empty : My_Array := My_Array'Null;

Could be, but it is not much better than My_Array'(null array).

> (You are right about indices. String has the lower bound set to 1. That
> gives an unambiguous range of an empty string.

No, for example (16 .. -40 => ' ') is an empty string, with bounds 16 ..
-40.

The index range of the empty string denoted by the null string literal
"" is defined in the RM unambiguously.

> For general case arrays it is ambiguous)

Yes, the index bounds of "(null array)" would have to be specifically
defined in the RM, similarly to the case for the null string literal.

Niklas Holsti

unread,
Dec 23, 2017, 2:17:59 AM12/23/17
to
On 17-12-23 03:47 , Mehdi Saada wrote:
> Thank you, Niklas Holsti. Your way of doing it is just beautiful, as
> always with conditional expressions. Also, I simply forgot about
> the & operator for non-strings arrays.
> Still:
> p_poly_v1_g.adb:64:92: expected private type "T_Rationnel" defined at p_rationnels_g.ads:11, instance at p_poly_v1_g.ads:19
> p_poly_v1_g.adb:64:92: found type universal integer
> p_poly_v1_g.adb:64:149: expected private type "T_Rationnel" defined at p_rationnels_g.ads:11, instance at p_poly_v1_g.ads:19
> p_poly_v1_g.adb:64:149: found type universal integer

Sorry, I should have written "else Nulle" instead of "else 0". I forgot
that your coefficients are rationals and not integers.
Message has been deleted
Message has been deleted

Niklas Holsti

unread,
Dec 23, 2017, 7:09:03 AM12/23/17
to
On 17-12-23 13:39 , Mehdi Saada wrote:
> And I was no stupid enough not to see that "0" were indeed
> in the wrong place, and were the only thing close to an Integer
> here ... Damn, the brain is so limited.

That's why we have compilers to tell us where the error is ;-)

GNAT's error messages are usually very helpful and to the point.
Message has been deleted

Mehdi Saada

unread,
Dec 23, 2017, 7:16:07 AM12/23/17
to
As I see it, but I don't know the intricated rules of extended return statements, RES seems to be initialized as a constrained record object.
Does "return RES: T_POLYNOME (T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE)) do"
constrains its discrimant ?
I would explain that RES := POLY_REDUIT(RES) three lines below causes "raised CONSTRAINT_ERROR : p_poly_v1_g.adb:67 discriminant check failed"

return RES: T_POLYNOME (T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE)) do
for I in RES.COEF'Range loop
RES.COEF(I) := (if I in POLY_A.COEF'Range then POLY_A.COEF(I) else Nulle) + (if I in POLY_B.COEF'Range then POLY_B.COEF(I) else Nulle);

Niklas Holsti

unread,
Dec 23, 2017, 8:04:58 AM12/23/17
to
On 17-12-23 14:16 , Mehdi Saada wrote:
> As I see it, but I don't know the intricated rules of
> extended return statements, RES seems to be initialized as
> a constrained record object.

I believe it is. Does the DEGRE discriminant have a default value? If
not, all T_POLYNOME objects are constrained.

> Does "return RES: T_POLYNOME (T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE)) do"
> constrains its discrimant ?

Yes, just as a declaration X : String(1..4) constrains the index bounds
of X.

> It would explain that RES := POLY_REDUIT(RES) three lines below
> causes "raised CONSTRAINT_ERROR : p_poly_v1_g.adb:67 discriminant check failed"

Certainly, if POLY_REDUIT returns a value with a different value of the
DEGRE discriminant. I guess POLY_REDUIT drops any zero-valued high-order
coefficients, so it can return a poly with a smaller DEGRE than its input.

I would make RES into a local variable (not an extended return object),
declare and assign it as now, and then just

return POLY_REDUIT (RES);

at the end.

Mehdi Saada

unread,
Dec 23, 2017, 9:02:15 AM12/23/17
to
The discrimant has indeed a default value, hence my "what the heck"-like reaction.
type T_Polynome (Degre : T_Degre := 0) is
record
Coef : T_Vect_Coef (0..Degre) := (others => 0);
end record;

But I'll stop with putting "extended" everywhere. I first found it really cool, but not so much now.
Message has been deleted

Mehdi Saada

unread,
Dec 23, 2017, 10:03:26 AM12/23/17
to
the function "+" is wrong as well.

input (-2 + X, 7 - 1X) the output is 5 -2X**2, which is complete nonsense, especially since that piece of code (conditional expression) isn't mine. The chunk of test program calling "+" is:
Put_Line ("Pl + P2 = ");
Ecrire(Poly1 + Poly2);

function "+" (POLY_A, POLY_B: T_POLYNOME) return T_POLYNOME is
RES: T_POLYNOME (T_DEGRE'MAX(POLY_A.DEGRE,POLY_B.DEGRE));
begin
ADA.TEXT_IO.PUT_LINE("POLY A: ");
ECRIRE (POLY_A);
ADA.TEXT_IO.PUT_LINE("POLY B: ");
ECRIRE (POLY_B);
for IND in RES.COEF'Range loop
RES.COEF(IND) := (if IND in POLY_A.COEF'Range then POLY_A.COEF(IND) else Nulle) + (if IND in POLY_B.COEF'Range then POLY_B.COEF(IND) else Nulle);
end loop;
ADA.TEXT_IO.PUT_LINE("RES avant d'être réduit : "); ADA.TEXT_IO.NEW_LINE;
ECRIRE(RES);
return POLY_REDUIT(RES);
end;

It outputs:
Pl + P2 =
POLY A:
-2 +X
POLY B:
7 -1X
RES avant d'être réduit :

5 -2X** 2

Do you thing I should take a break from Ada and invest time more on algorithmic courses ? Because I feel really down now. I simply shouldn't be so mediocre. I didn't use to be a second-rate student for the matters I liked.

Jeffrey R. Carter

unread,
Dec 23, 2017, 11:23:32 AM12/23/17
to
On 12/22/2017 11:11 PM, Niklas Holsti wrote:
>
> I think the aggregate form (null array) was suggested some time ago, in analogy
> with the existing (null record). It looks good to me, but as I remember, there
> was some objection. One problem is that if the index type has exactly one value,
> then a null array with that index type cannot exist (because then A'First =
> A'Last for any such array A).

Actually, a null array of such a type can exist if it's a string type, because
you can use the string literal "" for such types. AIUI, 'Last is undefined for
such a value. Allowing (null array) for non-string array types with an index
type with a single value would presumably work the same.

--
Jeff Carter
"The time has come to act, and act fast. I'm leaving."
Blazing Saddles
36

Niklas Holsti

unread,
Dec 23, 2017, 5:11:42 PM12/23/17
to
On 17-12-23 17:03 , Mehdi Saada wrote:
> the function "+" is wrong as well.

Sorry, can't much help you debug this, as most of your code is still not
shown. For example, the error might be in your ECRIRE procedure, or in
your operations on T_Rationnel.

> Do you thing I should take a break from Ada and invest time more on
> algorithmic courses ? Because I feel really down now. I simply
> shouldn't be so mediocre. I didn't use to be a second-rate student
> for the matters I liked.

Have you _reviewed_ and _tested_ your code for T_Rationnel and ECRIRE?
Are you sure that they work?

How about starting with T_Rationnel replaced by Integer (for example,
subtype T_Rationnel is Integer), and then seeing if the polynomial
operations work with this simpler coefficient type?

Mehdi Saada

unread,
Dec 23, 2017, 7:55:28 PM12/23/17
to
I haven't tested them, since they are provided to the student. Hence the unlikeliness of them containing errors... also, I shouldn't, at that point, be testing anything so thoroughly.

It's ok, my stagnation is logical, since 5 directed works are lacking. The best course available ever, with a third of its exercices, absent, nowhere to be found on the internet... I just contacted the teacher. That's ridiculously detrimental to the community as a whole, at least the French Ada community.

But I'll replace rationals by integers or float, good exercice. simplifies the whole for my head too :-)

Robert Eachus

unread,
Dec 23, 2017, 10:37:42 PM12/23/17
to
On Saturday, December 23, 2017 at 11:23:32 AM UTC-5, Jeffrey R. Carter wrote:
> On 12/22/2017 11:11 PM, Niklas Holsti wrote:
> >
> > I think the aggregate form (null array) was suggested some time ago, in analogy
> > with the existing (null record). It looks good to me, but as I remember, there
> > was some objection. One problem is that if the index type has exactly one value,
> > then a null array with that index type cannot exist (because then A'First =
> > A'Last for any such array A).
>
> Actually, a null array of such a type can exist if it's a string type, because
> you can use the string literal "" for such types. AIUI, 'Last is undefined for
> such a value. Allowing (null array) for non-string array types with an index
> type with a single value would presumably work the same.

The rule that allows 1..0 as a null string range only has a problem if you have an array with an enumeration index type containing a single component. I think that is one of those cases where we say, "Don't do that!" and move on.

A more complex, and potentially ugly case is for multidimensional arrays. Defining (null array) as being empty in all dimensions with Foo'Range(n) = Bar'First..Bar'Pred(Bar'First) where Bar is the nth index SUBtype for Foo works. If the last materialized is a (real) Numeric_Error AKA Constraint_Error? Again, only an issue for smart alecks. In general, you should be surprised if Foo'Last(N) for a null array doesn't raise Constraint_Error.

Niklas Holsti

unread,
Dec 24, 2017, 8:33:00 AM12/24/17
to
On 17-12-23 18:23 , Jeffrey R. Carter wrote:
> On 12/22/2017 11:11 PM, Niklas Holsti wrote:
>>
>> I think the aggregate form (null array) was suggested some time ago,
>> in analogy with the existing (null record). It looks good to me, but
>> as I remember, there was some objection. One problem is that if the
>> index type has exactly one value, then a null array with that index
>> type cannot exist (because then A'First = A'Last for any such array A).
>
> Actually, a null array of such a type can exist if it's a string type,
> because you can use the string literal "" for such types. AIUI, 'Last is
> undefined for such a value.

RM 4.2(9) says "for a null string literal, the upper bound is the
predecessor of the lower bound". I would understand this to mean
applying 'Pred to the lower bound, which will raise Constraint_Error if
the type has only one value.

If I try this with GNAT:

type One_T is (Unique);
type Str_T is array (One_T range <>) of Character;
S : constant Str_T := "";

I get compilation errors:

nullstr.adb:11:26: null string literal not allowed for type
"Str_T" defined at line 9
nullstr.adb:11:26: static expression fails Constraint_Check

where line 11 is the one with the "" literal.

So one can try to make a null array of this type with "", but it will fail.

> Allowing (null array) for non-string array
> types with an index type with a single value would presumably work the
> same.

I agree, it should also raise Constraint_Error.

Niklas Holsti

unread,
Dec 24, 2017, 8:39:59 AM12/24/17
to
On 17-12-24 05:37 , Robert Eachus wrote:
> On Saturday, December 23, 2017 at 11:23:32 AM UTC-5, Jeffrey R.
> Carter wrote:
>> On 12/22/2017 11:11 PM, Niklas Holsti wrote:
>>>
>>> I think the aggregate form (null array) was suggested some time
>>> ago, in analogy with the existing (null record). It looks good to
>>> me, but as I remember, there was some objection. One problem is
>>> that if the index type has exactly one value, then a null array
>>> with that index type cannot exist (because then A'First = A'Last
>>> for any such array A).

[snip]

> A more complex, and potentially ugly case is for multidimensional
> arrays. Defining (null array) as being empty in all dimensions with
> Foo'Range(n) = Bar'First..Bar'Pred(Bar'First) where Bar is the nth
> index SUBtype for Foo works.

In other words, the same as the current rule for the null string literal "".

> In general, you should be surprised if Foo'Last(N) for a null
> array doesn't raise Constraint_Error.

That is not true in current Ada, as I understand it.

That would be very bad -- it would mean that any general operation that
loops over an array that is potentially null would have to first check
the 'Length. Fortunately this is not the case.

Jeffrey R. Carter

unread,
Dec 25, 2017, 8:40:20 AM12/25/17
to
On 12/24/2017 02:32 PM, Niklas Holsti wrote:
>
> If I try this with GNAT:
>
>    type One_T is (Unique);
>    type Str_T is array (One_T range <>) of Character;
>    S : constant Str_T := "";
>
> I get compilation errors:
>
>    nullstr.adb:11:26: null string literal not allowed for type
>       "Str_T"  defined at line 9
>    nullstr.adb:11:26: static expression fails Constraint_Check

Interesting. I'm pretty sure that used to work. Has that changed in a recent
revision?

--
Jeff Carter
"My little plum, I am like Robin Hood. I take from
the rich, and I give to the poor. ... Us poor."
Poppy
96

Mehdi Saada

unread,
Dec 25, 2017, 9:42:53 AM12/25/17
to
More importantly, why would anyone use an enumeration type with only one value ? I don't get it.

Dmitry A. Kazakov

unread,
Dec 25, 2017, 12:03:03 PM12/25/17
to
On 2017-12-25 15:42, Mehdi Saada wrote:
> More importantly, why would anyone use an enumeration type with only
> one value ? I don't get it.

Consider table-driven descent-recursive parser. The template is

case Statement_Table.Match (Source) is
when If_Token =>
...
when For_Token =>
...
end case;

Statement_Table is a type from a generic table package instantiated with
a token enumeration type. Should the table have only one expected match
the enumeration type has just one element.

Niklas Holsti

unread,
Dec 25, 2017, 1:27:28 PM12/25/17
to
On 17-12-25 16:42 , Mehdi Saada wrote:
> More importantly, why would anyone use an enumeration
> type with only one value ? I don't get it.

It's a limit case that comes up fairly often when one creates generic
software components parametrized by enumeration types, or other discrete
types. (Sometimes one would like to use such a component with an
enumeration type that has _no_ elements, but that seldom works...)

Null record types (with no components) and empty arrays are similar
limit cases. The number zero and the empty set are others. It is
important that language has sensible and consistent rules for these
limits (for example, a loop over an empty array should execute the loop
body zero times) so that generic code can be written without always
handling these limit cases separately.

Jacob Sparre Andersen

unread,
Dec 25, 2017, 3:12:39 PM12/25/17
to
Mehdi Saada <0012...@gmail.com> writes:

> More importantly, why would anyone use an enumeration type with only
> one value ? I don't get it.

Because it is planned that there may be more than one value. Or because
the type had more than one value, but doesn't have it in the current
version of the program.

If you look at a program which isn't a teenager anymore, both of those
cases can apply.

Greetings,

Jacob
--
"The three principal virtues of a programmer are Laziness,
Impatience, and Hubris" -- Larry Wall
0 new messages