with Ada.Containers.Vectors;
procedure Check is
package Integer_Vectors is
new Ada.Containers.Vectors
(Index_Type => Natural,
Element_Type => Integer);
package Nested_Vectors is
new Ada.Containers.Vectors
(Index_Type => Natural,
Element_Type => Integer_Vectors.Vector,
"=" => Integer_Vectors."=");
IV : Integer_Vectors.Vector;
NV : Nested_Vectors.Vector;
begin
IV.Append(42);
NV.Append(IV);
NV.Element(0).Append(43); -- Compile error here.
end Check;
The error message is, "Actual for Container must be a variable." I
understand what this means. The problem is that I'm trying to use an
expression as an argument for an 'in out' parameter. What I'm having
trouble figuring out is how to work around this. I tried using 'renames'
to give a simple name to NV.Element(0) but that didn't fool the compiler
(no surprise). I also tried playing around with anonymous access types
but I couldn't figure out how to declare the Element_Type of package
Nested_Vectors to be aliased. I'm not sure if it would have worked in
any case.
Surely there must be a straight forward way to do this!
Peter
I'd probably use Update_Element.
--
Jeff Carter
"I've got to stay here, but there's no reason
why you folks shouldn't go out into the lobby
until this thing blows over."
Horse Feathers
50
> Peter C. Chapin wrote:
>>
>> Surely there must be a straight forward way to do this!
>
> I'd probably use Update_Element.
Which one could argue is straightforward from a conceptual point of view,
but requires a lot of typing.
I think there was an old discussion about why it was not a good idea to have
functions returning accesses to the elements for this[*], which would enable
what Peter was attempting (since without something like C++ references
there's no way to do this with similar code). I have done so for my use
sometimes, but is ugly nonetheless. Exposing accesses to client code is not
nice, to put it mildly.
[*] Dangling pointers IIRC was the reason. In GNAT case I suspect that
dangling cursors is not any better since they don't do reference counting
anyway.
There is a fair bit of template code for
Update_Element. No need, I should think,
to not have a computer help you with this
sort of typing :-)
>> Surely there must be a straight forward way to do this!
>
> I'd probably use Update_Element.
Okay, thanks. That sounds like a reasonably approach.
I don't mind the extra typing, honestly, and some extra "complexity" is
also acceptable since this isn't something I need to do often (in
general). Being straight forward conceptually is more important and
Update_Element seems to satisfy that criteria.
Thanks!
Peter
Ada explicitly emphasizes ease of reading over ease of writing (ARM Introduction).
--
Jeff Carter
"Your mother was a hamster and your father smelt of elderberries."
Monty Python & the Holy Grail
06
> Alex R. Mosteo wrote:
>> Which one could argue is straightforward from a conceptual point of
>> view, but requires a lot of typing.
>
> Ada explicitly emphasizes ease of reading over ease of writing (ARM Introduction).
Not in this case.
I agree that the "extra typing" is not the problem. The problem is that
you have to read all that junk you typed, and it's mostly content free.
- Bob
To be fair, since I was the one throwing the typing bit, this kind of
"heavyweight" constructs are sparingly needed (in my use cases) when using
the standard containers. And in any case you can write a wrapper once and
hide it for good.
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing in e-mail?
Top-posting is what actually happens when one uses downwards closures.
In my case, I had to iterate map of vectors. "Iterate" procedure
passes my
closure just Cursor. I need to invoke Update_Element on Cursor, then
Iterate the Vector, then Update_Element on Vector.
4 levels so far.
Downwards closures is rather easy way to write write-only Ada code.
I've specified the order in which I wrote every line. There are just 2
levels here.
1 declare
5 procedure ...(...) is
9 procedure ...(...) is
...
10 begin
12 Iterate (..., ...'Acess);
11 end ...;
6 begin
8 Update_Element (..., ...'Access);
7 end ...;
2 begin
4 Iterate (...., ...'Access);
3 end;
Do you read in the same order?
I'd like to have a syntax like this:
Iterate (..., ..., new procedure (...) is
begin
...
end;);
Procedure name should be optional, I think.
> Downwards closures is rather easy way to write write-only Ada code.
>
> I've specified the order in which I wrote every line. There are just 2
> levels here.
>
> 1 declare
> 5 procedure ...(...) is
> 9 procedure ...(...) is
> ...
> 10 begin
> 12 Iterate (..., ...'Acess);
> 11 end ...;
> 6 begin
> 8 Update_Element (..., ...'Access);
> 7 end ...;
> 2 begin
> 4 Iterate (...., ...'Access);
> 3 end;
>
> Do you read in the same order?
>
> I'd like to have a syntax like this:
>
> Iterate (..., ..., new procedure (...) is
> begin
> ...
> end;);
>
> Procedure name should be optional, I think.
And the next step is
for Element in Vector loop
-- place "new procedure" here!
end loop;
There is nothing better for iteration than loops. Downward closures are
meant for different stuff.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
> A: Because it messes up the order in which people normally read text.
> Q: Why is top-posting such a bad thing?
> A: Top-posting.
> Q: What is the most annoying thing in e-mail?
>
> Top-posting is what actually happens when one uses downwards closures.
Nice analogy. I agree 100%
> Procedure name should be optional, I think.
Yes. Like a lambda expression in Lisp, but preferably with less verbose
syntax.
Requiring the body of a loop to have a name is an annoyance.
What are you going to call it? The_Loop_Body. Or something
equally meaningless.
- Bob
> And the next step is
>
> for Element in Vector loop
> -- place "new procedure" here!
> end loop;
Sather has an elegant iterator feature, which is along these lines.
> There is nothing better for iteration than loops.
Maybe things like "map" found in functional languages?
>...Downward closures are
> meant for different stuff.
I'd say your above "for..." syntax should be thought of as syntactic
sugar defined in terms of downward closures. Or perhaps in terms
of some coroutine-like concept.
- Bob
> "Dmitry A. Kazakov" <mai...@dmitry-kazakov.de> writes:
>
>> And the next step is
>>
>> for Element in Vector loop
>> -- place "new procedure" here!
>> end loop;
>
> Sather has an elegant iterator feature, which is along these lines.
>
>> There is nothing better for iteration than loops.
>
> Maybe things like "map" found in functional languages?
Iteration is meant to be stateful. Map, convolution, product etc are
different in my view.
>>...Downward closures are
>> meant for different stuff.
>
> I'd say your above "for..." syntax should be thought of as syntactic
> sugar defined in terms of downward closures. Or perhaps in terms
> of some coroutine-like concept.
I would say that downward closure is an argument of a map or some other
operation taking subprogram as an argument etc.
Concerning syntax, I think we should have introduced proper procedural
types with literals/aggregates of. Quite simple to do, IMO.
> Requiring the body of a loop to have a name is an annoyance.
> What are you going to call it? The_Loop_Body. Or something
> equally meaningless.
Is the loop body meaningless, too? If not, there is a name :-)
The loop body is usually self explanatory. E.g:
for Index in S'Range loop
S(Index) := To_Upper_Case(S(Index));
end loop;
Do you want me to call it "Convert_Current_Character_To_Upper_Case",
and wrap it in a procedure by that name?
- Bob
> Concerning syntax, I think we should have introduced proper procedural
> types with literals/aggregates of. Quite simple to do, IMO.
I mostly agree. What syntax would you propose for anonymous procedures?
- Bob
That is the most difficult part! It should allow both one-line coding style
when a mere [lazy] expression and a full-blown body with exception handlers
etc.
> The loop body is usually self explanatory. E.g:
I wish it were, yes. A loop body _should_ be
elf explanatory. FWIW, I have not seen a loop
with just single statement in quite a while.
Hope things are in different shape elsewhere.
> for Index in S'Range loop
> S(Index) := To_Upper_Case(S(Index));
> end loop;
>
> Do you want me to call it "Convert_Current_Character_To_Upper_Case",
> and wrap it in a procedure by that name?
No, but I think I'd prefer something like
Iterate(S, Process => To_Upper_Case);
That is, To_Upper_Case is a good name, already.
The loop above is a "for-all" loop, Index is used
for nothing but a concrete, clumsy, and---if you
will---redundant handle of the abstraction "for-all".
So why a detailed loop?
> Robert A Duff wrote:
>
>> The loop body is usually self explanatory. E.g:
>
> I wish it were, yes. A loop body _should_ be
> elf explanatory. FWIW, I have not seen a loop
> with just single statement in quite a while.
> Hope things are in different shape elsewhere.
Not sure what you mean. I'm saying that Ada's current support for
iterators requires you to wrap the loop body in a named procedure.
The fact that people don't normally wrap loop bodies in procedures
(when using 'for' and 'while' loops) proves that doing so is a burden.
I want to create procedures for exactly the code that represents
a coherent abstraction -- and that's not necessarily the body of a loop.
>> for Index in S'Range loop
>> S(Index) := To_Upper_Case(S(Index));
>> end loop;
>>
>> Do you want me to call it "Convert_Current_Character_To_Upper_Case",
>> and wrap it in a procedure by that name?
>
> No, but I think I'd prefer something like
>
> Iterate(S, Process => To_Upper_Case);
Without the 'Access. ;-)
Normally, the loop body needs access to some local variable (or
parameter). Here's another example: Suppose we have a procedure
to send a character to an output stream:
procedure Put(Stream: in out ...; C: Character);
And we want to write a procedure to send a String. So we write:
procedure Put(Stream: in out ...; S : String) is
begin
for I in S'Range loop
Put(Stream, S(I));
end loop;
end Put;
It would be silly to write:
procedure Put(Stream: in out ...; S : String) is
procedure Put(C: Character) is
begin
Put(Stream, C);
end Put;
begin
for I in S'Range loop
Put(S(I));
end loop;
end Put;
No matter what you call that inner Put, it doesn't deserve to be a
named procedure at all. Note that it has to be nested, because
it needs to refer to Stream -- we can't create a reusable one
and put it in a library.
But if we have a Vector of Characters, or Linked_List of Characters,
instead of an array of Characters, we would be forced to write that
silly nested procedure.
Not sure whether we are agreeing or disagreeing, here... ;-)
> That is, To_Upper_Case is a good name, already.
> The loop above is a "for-all" loop, Index is used
> for nothing but a concrete, clumsy, and---if you
> will---redundant handle of the abstraction "for-all".
> So why a detailed loop?
I definitely agree with this part. The Index is conceptually
unnecessary. Furthermore, the fact that arrays and Vectors use
totally different syntax for iteration and element selection
and so forth is a Bad Thing.
Ideally, we want to say something like:
for C: Character in S loop -- Whether S is an array, or some other sequence data structure
<do something with C> -- not a named procedure
end loop;
- Bob
But the fact that people don't normally bother to wrap loop bodies in
procedures is not a prove that to do must be wrong, or that giving
a loop name might not in fact be preferrable. Yes, it is work to
find a good name but OTOH, it is considered good style, in particular
in Ada culture, to Say What You Mean. AFAIK.
I agree that the example you have given might show
some silly formalism. But again, is this a typical
scenario warranting more anonymity in Ada?
Eiffel's new anonymous agents (access sub parameters with
partially bound arguments) have met some criticism, too, BTW.
Here is another example, one active and one passive
iteration. Does the loop body deserve a captivating
title? Then, maybe in the form of a named local procedure.
function Active (Data: Vector; Threshold: Amount) return Amount is
N: Positive; -- nth item
Result: Amount;
Item: Amount;
begin
Result := 0.0;
N := 1;
for Index in First_Index(Data) .. Last_Index(Data) loop
Item := Element(Data, Index);
if Item < Threshold then
Result := ((N - 1) * Result + Item) / N;
N := N + 1;
else
Skipped := Skipped + 1;
end if;
end loop;
return Result;
end Active;
function Passive (Data: Vector; Threshold: Amount) return Amount is
N: Positive; -- nth item
Result: Amount;
procedure Incremental_Average(Position: in Cursor) is
Item: constant Amount := Element(Position);
begin
if Item < Threshold then
Result := ((N - 1) * Result + Item) / N;
N := N + 1;
else
Skipped := Skipped + 1;
end if;
end Incremental_Average;
begin
Result := 0.0;
N := 1;
Iterate(Data, Process => Incremental_Average'Access);
return Result;
end Passive;
In Active, the new syntax you have suggested will shorten
the loop body a bit, making Item's declaration in Active's
declarative part unneccesary. But still, there will be a
few lines left, worth a comment, at least? If so,
then why not write a named body?
Writing quick, anonymous, non-recursive lamdas is fun,
but there is inherent danger in making them a language
feature, I think. The danger is that they are going to
be used like they are being used in Python, or
Perl: spanning multiple lines, trying to be clever,
avoid finding a name, write them once, never understand
them again.
See also the proliferation of anonymous access ;-)
(I recall Vdim Gunko (I think) saying that adding named 'class
pointers to the Qt4 (C++) binding is too much of a burdon...
> See also the proliferation of anonymous access ;-)
> (I recall Vdim Gunko (I think)
That was Vadim Godunko. I'm sorry.
I've lost the thread of this discussion, but the issue of iterators in Ada
with proper syntax is trivial IFF the language has the correct features. And
the feature that the language doesn't have that prevents a relatively easy
iterator is the one alluded to by the OP. I've spent a good portion of the
last two months thinking about these issues, and there is no getting around
the missing way to modify an element in place in a container. (I've several
times thought that I'd gotten around the problem, but it isn't practical to
do so.)
Watch Ada-Comment for my take on these problems, some coming very soon
(tonight, I hope).
Randy Brukardt.