I am new to Ada, I know is there a good way to start this program?
thanks
https://projecteuler.net/problem=26
On 2023-09-04 12:19, CSYH (QAQ) wrote:
I am new to Ada, I know is there a good way to start this program?
thanks
https://projecteuler.net/problem=26
First invent/discover the method (algorithm) for solving the problem,
without thinking about the programming language.
I don't think any language has built-in features that would lead to a
direct solution, although some functional language with lazy evaluation
could come close, because such languages can manipulate unbounded (potentially infinite) sequences of values. Such sequences can be
handled in Ada, too, but with more effort -- they are not "built in" to
Ada.
I am new to Ada, I know is there a good way to start this program?
thanks
https://projecteuler.net/problem=26
On 2023-09-04 13:06, Niklas Holsti wrote:
On 2023-09-04 12:19, CSYH (QAQ) wrote:
I am new to Ada, I know is there a good way to start this program?First invent/discover the method (algorithm) for solving the problem,
thanks
https://projecteuler.net/problem=26
without thinking about the programming language.
I don't think any language has built-in features that would lead to a
direct solution, although some functional language with lazy evaluation
could come close, because such languages can manipulate unbounded
(potentially infinite) sequences of values. Such sequences can be handled
in Ada, too, but with more effort -- they are not "built in" to Ada.
Infinite division does not require big numbers, which Ada 22 has, but I
wound not use them anyway because the performance would be abysmal.
BTW, Ada is perfect for numeric algorithms no need to resort to functional mess... (:-))
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-04 13:06, Niklas Holsti wrote:
On 2023-09-04 12:19, CSYH (QAQ) wrote:
I am new to Ada, I know is there a good way to start this program?First invent/discover the method (algorithm) for solving the problem,
thanks
https://projecteuler.net/problem=26
without thinking about the programming language.
I don't think any language has built-in features that would lead to a
direct solution, although some functional language with lazy evaluation
could come close, because such languages can manipulate unbounded
(potentially infinite) sequences of values. Such sequences can be handled >>> in Ada, too, but with more effort -- they are not "built in" to Ada.
Infinite division does not require big numbers, which Ada 22 has, but I
wound not use them anyway because the performance would be abysmal.
BTW, Ada is perfect for numeric algorithms no need to resort to functional >> mess... (:-))
Perfect? That's a bold claim!
Mind you, I don't think this problem is really a numerical one in that
sense. It needs some simple integer arithmetic but then every language
is perfect for that sort of arithmetic.
Using a functional mess (Haskell) a simple, native solution (i.e. using
no modules) is only 9 lines long.
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-04 18:01, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
BTW, Ada is perfect for numeric algorithms no need to resort to functional >>>> mess... (:-))Perfect? That's a bold claim!
Ada is a very improved descendant of Algol 60, which was designed to codify >> algorithms.
Yes, though I was respond to you narrower remark about being perfect for numeric algorithms.
Are you expending that to perfect for every kind of
algorithm?
(rather than about for example building
abstractions as in the case of OOP)
That's interesting. You don't consider using functions and procedures (possibly higher-order ones) to be a way to build abstractions?
On 2023-09-04 18:01, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-04 13:06, Niklas Holsti wrote:Perfect? That's a bold claim!
On 2023-09-04 12:19, CSYH (QAQ) wrote:
I am new to Ada, I know is there a good way to start this program?First invent/discover the method (algorithm) for solving the problem,
thanks
https://projecteuler.net/problem=26
without thinking about the programming language.
I don't think any language has built-in features that would lead to a
direct solution, although some functional language with lazy evaluation >>>> could come close, because such languages can manipulate unbounded
(potentially infinite) sequences of values. Such sequences can be handled >>>> in Ada, too, but with more effort -- they are not "built in" to Ada.
Infinite division does not require big numbers, which Ada 22 has, but I
wound not use them anyway because the performance would be abysmal.
BTW, Ada is perfect for numeric algorithms no need to resort to functional >>> mess... (:-))
Ada is a very improved descendant of Algol 60, which was designed to codify algorithms.
Mind you, I don't think this problem is really a numerical one in that
sense. It needs some simple integer arithmetic but then every language
is perfect for that sort of arithmetic.
That is still all about algorithms
(rather than about for example building
abstractions as in the case of OOP)
Using a functional mess (Haskell) a simple, native solution (i.e. using
no modules) is only 9 lines long.
Apart from the fundamental inconsistency of functional paradigm: computing
is about transition of states and nothing else; the imperative languages express solutions, i.e. an algorithm. Functional, and in general,
declarative languages express puzzles.
On 2023-09-04 22:18, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-04 18:01, Ben Bacarisse wrote:Yes, though I was respond to you narrower remark about being perfect for
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
BTW, Ada is perfect for numeric algorithms no need to resort to functionalPerfect? That's a bold claim!
mess... (:-))
Ada is a very improved descendant of Algol 60, which was designed to codify >>> algorithms.
numeric algorithms.
Yes, Ada is.
(rather than about for example building
abstractions as in the case of OOP)
That's interesting. You don't consider using functions and procedures
(possibly higher-order ones) to be a way to build abstractions?
No, they do not introduce new types and do not form some structure of their values. And "using" is not an abstraction anyway.
The term "abstraction" is usually taken to be more general than that so
as to include function (or procedural) abstraction.
Ada is good at that, but the syntax is sufficiently cumbersome that I
think it discourages people from exploiting that part of the language.
Mind you, I am no Ada expert so maybe it's simpler to do than I think.
Here's my Ada solution:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Ordered_Maps; use Ada.Containers;
procedure Euler_26 is
function Period(Divisor: Positive) return Positive is
I know it won't make this program shorter, but it would be interesting
to know how it might be done.
On 2023-09-05 01:16, Ben Bacarisse wrote:
The term "abstraction" is usually taken to be more general than that so
as to include function (or procedural) abstraction.
These are means of software decomposition rather than abstraction (of something).
Ada is good at that, but the syntax is sufficiently cumbersome that I
think it discourages people from exploiting that part of the language.
Mind you, I am no Ada expert so maybe it's simpler to do than I think.
If the program does not resemble electric transmission noise, some people call the language syntax cumbersome... (:-))
Here's my Ada solution:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Ordered_Maps; use Ada.Containers;
procedure Euler_26 is
function Period(Divisor: Positive) return Positive is
You cannot use a number here because the period may have leading
zeros.
I know it won't make this program shorter, but it would be interesting
to know how it might be done.
The goal of engineering is not making programs shorter, it is to make them understandable, safer, reusable, maintainable, extensible, integrable.
There was
boiler plate code in my program that could be abstracted out into a
generic function (or package?) so that any function can be maximised
over some range or, better yet, any iterable type (if that's how Ada
does things).
Can someone here show me how?
On 2023-09-05 01:16, Ben Bacarisse wrote:
The term "abstraction" is usually taken to be more general than that so
as to include function (or procedural) abstraction.
These are means of software decomposition rather than abstraction (of something).
Ada is good at that, but the syntax is sufficiently cumbersome that I
think it discourages people from exploiting that part of the language.
Mind you, I am no Ada expert so maybe it's simpler to do than I think.
If the program does not resemble electric transmission noise, some
people call the language syntax cumbersome... (:-))
Here's my Ada solution:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Ordered_Maps; use Ada.Containers;
procedure Euler_26 is
   function Period(Divisor: Positive) return Positive is
You cannot use a number here because the period may have leading zeros.
I know it won't make this program shorter, but it would be interesting
to know how it might be done.
The goal of engineering is not making programs shorter, it is to make
them understandable, safer, reusable, maintainable, extensible, integrable.
On 2023-09-05 17:18, Ben Bacarisse wrote:
There was
boiler plate code in my program that could be abstracted out into a
generic function (or package?) so that any function can be maximised
over some range or, better yet, any iterable type (if that's how Ada
does things).
Can someone here show me how?
You define some classes. Either generic or tagged. E.g. a generic class of functions that uses two generic classes of the argument and the value:
generic
-- Ordered argument
type Argument_Type is private;
with function Next (Value : Argument_Type)
return Argument_Type is <>;
with function "<" (Left, Right : Argument_Type)
return Boolean is <>;
with function "=" (Left, Right : Argument_Type)
return Boolean is <>;
-- Comparable value
type Value_Type is private;
with function "<" (Left, Right : Value_Type ) return Boolean is <>;
-- Function type
with function Func (Argument : Argument_Type) return Value_Type;
function Generic_Maximum_At (Left, Right : Argument_Type)
return Value_Type;
and the implementation
function Generic_Maximum_At (Left, Right : Argument_Type)
return Value_Type is
Argument : Argument_Type := Left;
Max : Value_Type;
Value : Value_Type;
begin
if Right < Left then
raise Constraint_Error with "Empty interval";
end if;
Max := Func (Argument);
while not (Argument = Right) loop
Argument := Next (Argument);
Value := Func (Argument);
if Max < Value then
Max := Value;
end if;
end loop;
return Max;
end Generic_Maximum_At;
or you can choose to pass the function as an argument:
generic
-- Ordered argument
type Argument_Type is private;
with function Next (Value : Argument_Type)
return Argument_Type is <>;
with function "<" (Left, Right : Argument_Type)
return Boolean is <>;
-- Comparable value
type Value_Type is private;
with function "<" (Left, Right : Value_Type) return Boolean is <>; function Generic_Maximum_At
( Left, Right : Argument_Type;
Func : access function (Argument : Argument_Type)
return Value_Type
) return Value_Type;
Or you can make it a package which is usually a better choice as one can
pack into it several entities sharing the same generic interface:
generic
-- Ordered argument
type Argument_Type is private;
with function Next (Value : Argument_Type)
return Argument_Type is <>;
with function "<" (Left, Right : Argument_Type )
return Boolean is <>;
with function "=" (Left, Right : Argument_Type)
return Boolean is <>;
-- Comparable value
type Value_Type is private;
with function "<" (Left, Right : Value_Type) return Boolean is <>;
package Generic_Discrete_Comparable_Valued is
function Maximum_At
( Left, Right : Argument_Type;
Func : access function (Argument : Argument_Type)
return Value_Type
) return Value_Type;
-- Other useless functions
end Generic_Discrete_Comparable_Valued;
The generic classes of arguments/values can be in turn factored out into reusable generic packages:
generic
-- Ordered argument
type Argument_Type is private;
with function Next (Value : Argument_Type) return Argument_Type is <>;
with function "<" (Left, Right : Argument_Type) return Boolean is <>;
with function "=" (Left, Right : Argument_Type) return Boolean is <>; package Generic_Arguments is
end Generic_Arguments;
generic
-- Comparable value
type Value_Type is private;
with function "<" (Left, Right : Value_Type ) return Boolean is <>; package Generic_Values is
end Generic_Values;
generic
with package Arguments is new Generic_Arguments (<>);
with package Values is new Generic_Values (<>);
package Generic_Discrete_Comparable_Valued is
use Arguments, Values;
function Maximum_At
( Left, Right : Argument_Type;
Func : access function (Argument : Argument_Type)
return Value_Type
) return Value_Type;
-- Other useless functions
end Generic_Discrete_Comparable_Valued;
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-05 17:18, Ben Bacarisse wrote:
There was
boiler plate code in my program that could be abstracted out into a
generic function (or package?) so that any function can be maximised
over some range or, better yet, any iterable type (if that's how Ada
does things).
Can someone here show me how?
You define some classes. Either generic or tagged. E.g. a generic class of >> functions that uses two generic classes of the argument and the value:
generic
-- Ordered argument
type Argument_Type is private;
with function Next (Value : Argument_Type)
return Argument_Type is <>;
with function "<" (Left, Right : Argument_Type)
return Boolean is <>;
with function "=" (Left, Right : Argument_Type)
return Boolean is <>;
-- Comparable value
type Value_Type is private;
with function "<" (Left, Right : Value_Type ) return Boolean is <>;
-- Function type
with function Func (Argument : Argument_Type) return Value_Type;
function Generic_Maximum_At (Left, Right : Argument_Type)
return Value_Type;
and the implementation
function Generic_Maximum_At (Left, Right : Argument_Type)
return Value_Type is
Argument : Argument_Type := Left;
Max : Value_Type;
Value : Value_Type;
begin
if Right < Left then
raise Constraint_Error with "Empty interval";
end if;
Max := Func (Argument);
while not (Argument = Right) loop
Argument := Next (Argument);
Value := Func (Argument);
if Max < Value then
Max := Value;
end if;
end loop;
return Max;
end Generic_Maximum_At;
or you can choose to pass the function as an argument:
generic
-- Ordered argument
type Argument_Type is private;
with function Next (Value : Argument_Type)
return Argument_Type is <>;
with function "<" (Left, Right : Argument_Type)
return Boolean is <>;
-- Comparable value
type Value_Type is private;
with function "<" (Left, Right : Value_Type) return Boolean is <>;
function Generic_Maximum_At
( Left, Right : Argument_Type;
Func : access function (Argument : Argument_Type)
return Value_Type
) return Value_Type;
Or you can make it a package which is usually a better choice as one can
pack into it several entities sharing the same generic interface:
generic
-- Ordered argument
type Argument_Type is private;
with function Next (Value : Argument_Type)
return Argument_Type is <>;
with function "<" (Left, Right : Argument_Type )
return Boolean is <>;
with function "=" (Left, Right : Argument_Type)
return Boolean is <>;
-- Comparable value
type Value_Type is private;
with function "<" (Left, Right : Value_Type) return Boolean is <>;
package Generic_Discrete_Comparable_Valued is
function Maximum_At
( Left, Right : Argument_Type;
Func : access function (Argument : Argument_Type)
return Value_Type
) return Value_Type;
-- Other useless functions
end Generic_Discrete_Comparable_Valued;
The generic classes of arguments/values can be in turn factored out into
reusable generic packages:
generic
-- Ordered argument
type Argument_Type is private;
with function Next (Value : Argument_Type) return Argument_Type is <>; >> with function "<" (Left, Right : Argument_Type) return Boolean is <>;
with function "=" (Left, Right : Argument_Type) return Boolean is <>;
package Generic_Arguments is
end Generic_Arguments;
generic
-- Comparable value
type Value_Type is private;
with function "<" (Left, Right : Value_Type ) return Boolean is <>;
package Generic_Values is
end Generic_Values;
generic
with package Arguments is new Generic_Arguments (<>);
with package Values is new Generic_Values (<>);
package Generic_Discrete_Comparable_Valued is
use Arguments, Values;
function Maximum_At
( Left, Right : Argument_Type;
Func : access function (Argument : Argument_Type)
return Value_Type
) return Value_Type;
-- Other useless functions
end Generic_Discrete_Comparable_Valued;
Thank you. I can't yet see how to use any of these alternatives, but
that's my problem.
Are there any good online sources on Ada generic
programming so I can find out how to implement and use this short of
package?
I am curious to know how reusable this is. Can the packages be
instantiated in such a way that the argument ranges over the elements
of, say, and Ordered_Map?
Maybe a more generic a solution would involve passing something that can
be iterated over, rather than two values of an "enumerated" type? I
mean enumerated in the mathematical sense -- it may be the wrong word in
Ada.
I am asking you but I am also the group. I appreciate your help,
but don't want you to feel any obligation to keep helping!
On 2023-09-06 03:10, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-05 17:18, Ben Bacarisse wrote:Thank you. I can't yet see how to use any of these alternatives, but
There was
boiler plate code in my program that could be abstracted out into a
generic function (or package?) so that any function can be maximised
over some range or, better yet, any iterable type (if that's how Ada
does things).
Can someone here show me how?
You define some classes. Either generic or tagged. E.g. a generic class of >>> functions that uses two generic classes of the argument and the value:
generic
-- Ordered argument
type Argument_Type is private;
with function Next (Value : Argument_Type)
return Argument_Type is <>;
with function "<" (Left, Right : Argument_Type)
return Boolean is <>;
with function "=" (Left, Right : Argument_Type)
return Boolean is <>;
-- Comparable value
type Value_Type is private;
with function "<" (Left, Right : Value_Type ) return Boolean is <>;
-- Function type
with function Func (Argument : Argument_Type) return Value_Type;
function Generic_Maximum_At (Left, Right : Argument_Type)
return Value_Type;
and the implementation
function Generic_Maximum_At (Left, Right : Argument_Type)
return Value_Type is
Argument : Argument_Type := Left;
Max : Value_Type;
Value : Value_Type;
begin
if Right < Left then
raise Constraint_Error with "Empty interval";
end if;
Max := Func (Argument);
while not (Argument = Right) loop
Argument := Next (Argument);
Value := Func (Argument);
if Max < Value then
Max := Value;
end if;
end loop;
return Max;
end Generic_Maximum_At;
or you can choose to pass the function as an argument:
generic
-- Ordered argument
type Argument_Type is private;
with function Next (Value : Argument_Type)
return Argument_Type is <>;
with function "<" (Left, Right : Argument_Type)
return Boolean is <>;
-- Comparable value
type Value_Type is private;
with function "<" (Left, Right : Value_Type) return Boolean is <>;
function Generic_Maximum_At
( Left, Right : Argument_Type;
Func : access function (Argument : Argument_Type)
return Value_Type
) return Value_Type;
Or you can make it a package which is usually a better choice as one can >>> pack into it several entities sharing the same generic interface:
generic
-- Ordered argument
type Argument_Type is private;
with function Next (Value : Argument_Type)
return Argument_Type is <>;
with function "<" (Left, Right : Argument_Type )
return Boolean is <>;
with function "=" (Left, Right : Argument_Type)
return Boolean is <>;
-- Comparable value
type Value_Type is private;
with function "<" (Left, Right : Value_Type) return Boolean is <>;
package Generic_Discrete_Comparable_Valued is
function Maximum_At
( Left, Right : Argument_Type;
Func : access function (Argument : Argument_Type)
return Value_Type
) return Value_Type;
-- Other useless functions
end Generic_Discrete_Comparable_Valued;
The generic classes of arguments/values can be in turn factored out into >>> reusable generic packages:
generic
-- Ordered argument
type Argument_Type is private;
with function Next (Value : Argument_Type) return Argument_Type is <>; >>> with function "<" (Left, Right : Argument_Type) return Boolean is <>; >>> with function "=" (Left, Right : Argument_Type) return Boolean is <>; >>> package Generic_Arguments is
end Generic_Arguments;
generic
-- Comparable value
type Value_Type is private;
with function "<" (Left, Right : Value_Type ) return Boolean is <>;
package Generic_Values is
end Generic_Values;
generic
with package Arguments is new Generic_Arguments (<>);
with package Values is new Generic_Values (<>);
package Generic_Discrete_Comparable_Valued is
use Arguments, Values;
function Maximum_At
( Left, Right : Argument_Type;
Func : access function (Argument : Argument_Type)
return Value_Type
) return Value_Type;
-- Other useless functions
end Generic_Discrete_Comparable_Valued;
that's my problem.
It is pretty much straightforward. E.g. the last one:
package Arguments is new Generic_Arguments (Integer, Integer'Succ);
package Values is new Generic_Values (Integer);
package Functions is
new Generic_Discrete_Comparable_Valued (Arguments, Values);
Now you can print the maximum of your Period function:
Put_Line
( "Max at"
& Integer'Image (Functions.Maximum_At (2, 999, Period'Access))
);
Are there any good online sources on Ada generic
programming so I can find out how to implement and use this short of
package?
Actually I provided an implementation above. Here it is again:
On 2023-09-06 17:16, Ben Bacarisse wrote:
I am curious to know how reusable this is. Can the packages be
instantiated in such a way that the argument ranges over the elements
of, say, and Ordered_Map?
Sure:
with Ada.Containers.Ordered_Maps;
package Integer_Maps is
new Ada.Containers.Ordered_Maps (Integer, Integer);
use Integer_Maps;
package Cursor_Arguments is new Generic_Arguments (Cursor);
package Map_Values is new Generic_Values (Integer);
package Map_Functions is
new Generic_Discrete_Comparable_Valued
(Cursor_Arguments, Map_Values);
Then given X is a map: X : Map;
Map_Functions.Maximum_At (X.First, X.Last, Element'Access)
Maybe a more generic a solution would involve passing something that can
be iterated over, rather than two values of an "enumerated" type? I
mean enumerated in the mathematical sense -- it may be the wrong word in
Ada.
Yes, but Ada does not have built-in range types. Therefore such design will not work out of the box with discrete types because 2..999 is not a proper object in Ada. However, talking about abstractions, you can create an interval type for the purpose or else use an ordered set of integers.
I am asking you but I am also the group. I appreciate your help,
but don't want you to feel any obligation to keep helping!
No problem.
I am new to Ada, I know is there a good way to start this program?
thanks
https://projecteuler.net/problem=26
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-06 17:16, Ben Bacarisse wrote:
I am curious to know how reusable this is. Can the packages be
instantiated in such a way that the argument ranges over the elements
of, say, and Ordered_Map?
Sure:
with Ada.Containers.Ordered_Maps;
package Integer_Maps is
new Ada.Containers.Ordered_Maps (Integer, Integer);
use Integer_Maps;
package Cursor_Arguments is new Generic_Arguments (Cursor);
Ah! So the arguments correspond to the "with" functions in the order
listed, and, since Cursor already has Next, there no need to specify anything.
There are a couple of details that prevent your Maximum_At function from working properly in this case though. First, we can't have an empty
map, because X.Last can't be compared with X.First when either is
No_Element, so the test for Right < Left fails before the desired error
can be raised.
Second, if I try to use a Vector rather than an Ordered_Map, I am told
that:
test2.adb:97:05: error: instantiation error at line 12
test2.adb:97:05: error: no visible subprogram matches the specification for "<"
It would seem that vector cursors can't be compared using < (at least by default). Maybe the installation needs more arguments.
Anyway, I am still not sure how to write a generic test for an empty
range.
It's possible I was not clear about what I was aiming for. I was hoping
to be able to find the maximum of some arbitrary function, taking the function's arguments from any sequential collection.
Either a simple
range of values, an array or vector of values, a list of values or even
an ordered map of values -- any ordered list of values.
The bottom line is the last argument should be something very general
like the Period function.
A fix (though it's not really ideal) would be to use function
composition here (inventing . as the composition operator):
Map_Functions.Maximum_At (X.First, X.Last, Period'Access . Element'Access)
but I don't think Ada has a function composition operator, does it?
Another solution would be to write Maximum_At so that it knows it has a cursor argument, but then I don't think it would work for native arrays, would it? And we'd loose plain ranges altogether.
But then (I think) the only function one could pass would be something
like Element as in you example above. Using an ordered set of integers
would not allow
Map_Functions.Maximum_At (Set.First, Set.Last, Period'Access)
would it?
I am asking you but I am also the group. I appreciate your help,
but don't want you to feel any obligation to keep helping!
No problem.
You seem to be on your own as far as helping out is concerned!
On 2023-09-07 01:32, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-06 17:16, Ben Bacarisse wrote:Ah! So the arguments correspond to the "with" functions in the order
I am curious to know how reusable this is. Can the packages be
instantiated in such a way that the argument ranges over the elements
of, say, and Ordered_Map?
Sure:
with Ada.Containers.Ordered_Maps;
package Integer_Maps is
new Ada.Containers.Ordered_Maps (Integer, Integer);
use Integer_Maps;
package Cursor_Arguments is new Generic_Arguments (Cursor);
listed, and, since Cursor already has Next, there no need to specify
anything.
Yes, because the formal argument is
with function Next (Value : Argument_Type)
return Argument_Type is <>;
If it were
with function Next (Value : Argument_Type)
return Argument_Type;
You would have to specify the actual. The part "is <>" tells to match a visible function Next.
There are a couple of details that prevent your Maximum_At function from
working properly in this case though. First, we can't have an empty
map, because X.Last can't be compared with X.First when either is
No_Element, so the test for Right < Left fails before the desired error
can be raised.
Yes, cursors is bad idea, in the end they all are pointers. No_Element is
an equivalent of null which shows.
However Maximum_At will propagate Constraint_Error if either of the bounds
is No_Element. So the implementation would work.
Second, if I try to use a Vector rather than an Ordered_Map, I am told
that:
test2.adb:97:05: error: instantiation error at line 12
test2.adb:97:05: error: no visible subprogram matches the specification for "<"
It would seem that vector cursors can't be compared using < (at least by
default). Maybe the installation needs more arguments.
Vector has a proper index type. All you have to do is. Given
package Integer_Vectors is
new Ada.Containers.Vectors (Integer, Integer);
Wrap Element into a function:
V : Integer_Vectors.Vector;
function Element (Index : Integer) return Integer is
begin
return V.Element (Index);
end Element;
...
and use the wrapper.
Anyway, I am still not sure how to write a generic test for an empty
range.
The problem is that the implementation of Cursor that breaks
abstraction. The abstraction of an argument does not permit ideal
non-values. Cursors and pointers have non-values. So if you want to test
for non-values ahead, instead of surprising the function, you need to add a test for value validity to the abstraction:
generic
-- Ordered argument
type Argument_Type is private;
with function Valid (Value : Argument_Type) return Boolean is <>;
...
package Generic_Arguments is
Then you would pass Has_Element for it. For integers you would use wrapped X'Valid (there is no Integer'Valid, unfortunately. Only X'Valid where X is
an object).
It's possible I was not clear about what I was aiming for. I was hoping
to be able to find the maximum of some arbitrary function, taking the
function's arguments from any sequential collection.
That is a different abstraction. You need a generic collection instead of generic ordered values. E.g.
generic
with package Arguments is new Ada.Containers.Ordered_Sets (<>);
with package Values is new Generic_Values (<>);
package Generic_Comparable_Valued is
use Arguments, Values;
function Maximum_At
( Domain : Set;
Func : access function (Argument : Element_Type)
return Value_Type
) return Value_Type;
-- Other useless functions
end Generic_Comparable_Valued;
package body Generic_Comparable_Valued is
function Maximum_At
( Domain : Set;
Func : access function (Argument : Element_Type)
return Value_Type
) return Value_Type is
Max : Value_Type;
Value : Value_Type;
Position : Cursor;
begin
if Domain.Is_Empty then
raise Constraint_Error with "Empty set";
end if;
Position := Domain.First;
Max := Func (Element (Position));
while Position /= Domain.Last loop
Position := Next (Position);
Value := Func (Element (Position));
if Max < Value then
Max := Value;
end if;
end loop;
return Max;
end Maximum_At;
end Generic_Comparable_Valued;
Either a simple
range of values, an array or vector of values, a list of values or even
an ordered map of values -- any ordered list of values.
In practice such abstraction have too much physical and mental
overhead. E.g. large sets of values implemented differently from Ada.Containers.Ordered_Sets depending on the operations required. For example, let you need a set complement? Usually programmers simply stick
with software patterns instead. Too much reliance of libraries make
programs incoherent.
The bottom line is the last argument should be something very general
like the Period function.
A fix (though it's not really ideal) would be to use function
composition here (inventing . as the composition operator):
Map_Functions.Maximum_At (X.First, X.Last, Period'Access
. Element'Access)
but I don't think Ada has a function composition operator, does it?
No as it would require closures.
So you can have a generic composition
operator, no problem, but not a first-class one. However you can simply add Maximum_At with four arguments to the package.
Another solution would be to write Maximum_At so that it knows it has a
cursor argument, but then I don't think it would work for native arrays,
would it? And we'd loose plain ranges altogether.
You can write a generic package creating array cursors:
generic
type Index_Type is (<>);
type Element_Type is private;
type Array_Type is array (Index_Type range <>) of Element_Type;
package Array_Cursors is
type Cursor is private;
function First (Container : Array_Type) return Cursor;
function Element (Position : Cursor) return Element_Type;
function "<" (Left, Right : Cursor) return Boolean;
...
private
package Dirty_Tricks is
new System.Address_To_Access_Conversions (Array_Type);
use Dirty_Tricks;
type Cursor is record
Domain : Object_Pointer;
Index : Index_Type;
end record;
end Array_Cursors;
package body Array_Cursors is
function "<" (Left, Right : Cursor) return Boolean is
begin
if Left.Domain = null or else Left.Domain /= Right.Domain then
raise Constraint_Error with "Incomparable cursors";
end if;
return Left.Index < Right.Index;
end "<";
function Element (Position : Cursor) return Element_Type is
begin
if Position.Domain = null or else
Position.Index not in Position.Domain'Range
then
raise Constraint_Error with "Invalid cursor";
else
return Position.Domain (Position.Index);
end if;
end Element;
function First (Container : Array_Type) return Cursor is
begin
if Container'Length = 0 then
raise Constraint_Error with "Empty array";
else
return (To_Pointer (Container'Address), Container'First);
end if;
end First;
end Array_Cursors;
You seem to be on your own as far as helping out is concerned!
Because it started as a numeric puzzle. You should have asked directly
about generics or tagged types instead.
A fix (though it's not really ideal) would be to use function
composition here (inventing . as the composition operator):
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-07 01:32, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-06 17:16, Ben Bacarisse wrote:Second, if I try to use a Vector rather than an Ordered_Map, I am told
that:
test2.adb:97:05: error: instantiation error at line 12
test2.adb:97:05: error: no visible subprogram matches the specification for "<"
It would seem that vector cursors can't be compared using < (at least by >>> default). Maybe the installation needs more arguments.
Vector has a proper index type. All you have to do is. Given
package Integer_Vectors is
new Ada.Containers.Vectors (Integer, Integer);
Wrap Element into a function:
V : Integer_Vectors.Vector;
function Element (Index : Integer) return Integer is
begin
return V.Element (Index);
end Element;
...
and use the wrapper.
Sure, but the hope was to write something that does not need new
code for new situations. That's what makes it reusable.
Then you would pass Has_Element for it. For integers you would use wrapped >> X'Valid (there is no Integer'Valid, unfortunately. Only X'Valid where X is >> an object).
It's definitely getting what I call cumbersome.
I don't think this is incoherent. The Haskell libraries ensure that any collection that is logically foldable is indeed foldable.
The bottom line is the last argument should be something very general
like the Period function.
A fix (though it's not really ideal) would be to use function
composition here (inventing . as the composition operator):
Map_Functions.Maximum_At (X.First, X.Last, Period'Access
. Element'Access)
but I don't think Ada has a function composition operator, does it?
No as it would require closures.
What closure is required for a function composition? There is no
environment to "close over".
That's a lot just to use something that is supposed to be reusable.
It only occurred to me after writing the non-generic solution. I
remember Ada as being something of a pioneer in it's attempt to provide generic solutions, so I wondered how far things had come. I don't think something really widely reusable is possible in this case.
On 07.09.23 01:32, Ben Bacarisse wrote:
A fix (though it's not really ideal) would be to use function
composition here (inventing . as the composition operator):
Hm. A stateful, composed function that needs to be applied
in a certain way. Is that so different from calling interface
subprograms of a certain type?
A wild guess: only "monads" would add substantial toppings
to the commonalities. Considering the computational powers of
C++'s "hair-raising template metaprogramming" [14.4], the idea
of "Ada generics" = "functional style" is probably limited
in scope.
So, does type composition help?
On 2023-09-08 03:32, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-07 01:32, Ben Bacarisse wrote:Sure, but the hope was to write something that does not need new
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-06 17:16, Ben Bacarisse wrote:Second, if I try to use a Vector rather than an Ordered_Map, I am told >>>> that:
test2.adb:97:05: error: instantiation error at line 12
test2.adb:97:05: error: no visible subprogram matches the specification for "<"
It would seem that vector cursors can't be compared using < (at least by >>>> default). Maybe the installation needs more arguments.
Vector has a proper index type. All you have to do is. Given
package Integer_Vectors is
new Ada.Containers.Vectors (Integer, Integer);
Wrap Element into a function:
V : Integer_Vectors.Vector;
function Element (Index : Integer) return Integer is
begin
return V.Element (Index);
end Element;
...
and use the wrapper.
code for new situations. That's what makes it reusable.
Why should it be? You wanted to find maximum of a function. Vector is
not a function.
Then you would pass Has_Element for it. For integers you would use wrapped >>> X'Valid (there is no Integer'Valid, unfortunately. Only X'Valid where X is >>> an object).It's definitely getting what I call cumbersome.
Yes, because you try too hard to make it work where it probably should
not.
I don't think this is incoherent. The Haskell libraries ensure that any
collection that is logically foldable is indeed foldable.
Ada arrays and library containers do not share interfaces.
Should the language allow adding
ad-hoc interfaces to existing types. Yes, and this is possible in Ada in
some very uncomfortable AKA cumbersome way, which is why "finding maximum"
is not a worthy abstraction in Ada.
What closure is required for a function composition? There is noA fix (though it's not really ideal) would be to use function
composition here (inventing . as the composition operator):
Map_Functions.Maximum_At (X.First, X.Last, Period'Access
. Element'Access)
but I don't think Ada has a function composition operator, does it?
No as it would require closures.
environment to "close over".
In Ada a function can use anything visible at its declaration point and at the location of its body. You can even declare a function inside a recursively called function and let it see local variables of each
recursive call, in effect having an infinite set of functions.
That's a lot just to use something that is supposed to be reusable.
[rant on]
An Ada programmer would just write a loop.
It only occurred to me after writing the non-generic solution. I
remember Ada as being something of a pioneer in it's attempt to provide
generic solutions, so I wondered how far things had come. I don't think
something really widely reusable is possible in this case.
As I said you think in a wrong direction of abstracting the language
"finding maximum" rather than the problem space, e.g. generalization to
other bases, other mathematical structures etc.
"G.B." <bauhaus@notmyhomepage.invalid> writes:
On 07.09.23 01:32, Ben Bacarisse wrote:
A fix (though it's not really ideal) would be to use function
composition here (inventing . as the composition operator):
Hm. A stateful, composed function that needs to be applied
in a certain way. Is that so different from calling interface
subprograms of a certain type?
There was nothing stateful (as I understand the term) in either function being composed.
So, does type composition help?
My turn to guess now: you are not being serious? I see no connection to monads or type composition.
And why bring C++ into it?
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-08 03:32, Ben Bacarisse wrote:I wanted the maximum of a function over a collection (range, array, map, etc). In some languages, collections can be scanned so you don't need
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-07 01:32, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
to know where the data come from.
Then you would pass Has_Element for it. For integers you would use wrapped >>>> X'Valid (there is no Integer'Valid, unfortunately. Only X'Valid where X is >>>> an object).It's definitely getting what I call cumbersome.
Yes, because you try too hard to make it work where it probably should
not.
If you think a resuable Ada function that can find the maximum of some F
over some 'collection' X is possible, I'd like to see how it's done.
I
can do it for some kinds of X but I have no idea how general it can be
made in Ada. I think the answer is either that it can't be very
general, or to make it very general is too much work, or that one should
not be trying in the first place.
I don't think this is incoherent. The Haskell libraries ensure that any >>> collection that is logically foldable is indeed foldable.
Ada arrays and library containers do not share interfaces.
I was pretty sure that was the case. Thanks for confirming. I think
that means there can be no truly generic solution. But maybe it's
possible at least for all container types in the library?
(But I note
that if you think it /shouldn't/ be done, I won't expect you to show me
how.)
Should the language allow adding
ad-hoc interfaces to existing types. Yes, and this is possible in Ada in
some very uncomfortable AKA cumbersome way, which is why "finding maximum" >> is not a worthy abstraction in Ada.
I suspected one might have to extend the interfaces.
What closure is required for a function composition? There is no
environment to "close over".
In Ada a function can use anything visible at its declaration point and at >> the location of its body. You can even declare a function inside a
recursively called function and let it see local variables of each
recursive call, in effect having an infinite set of functions.
At the point where I want Period.Element I can write the (almost)
one-line function that takes a Cursor and returns Period(Element(C))
entirely mechanically. Can't the compiler do that?
Note I'm not asking if it /should/ (it may not be "Ada-like" to do
that). I'm just curious if there really is a technical reason it can't
be done.
That's a lot just to use something that is supposed to be reusable.
[rant on]
An Ada programmer would just write a loop.
Yes, that's fine. Different languages have different objectives. Just
write the empty range test and the loop you need for each kind of
collection.
That was definitely the way things were done in the 80s.
It only occurred to me after writing the non-generic solution. I
remember Ada as being something of a pioneer in it's attempt to provide
generic solutions, so I wondered how far things had come. I don't think >>> something really widely reusable is possible in this case.
As I said you think in a wrong direction of abstracting the language
"finding maximum" rather than the problem space, e.g. generalization to
other bases, other mathematical structures etc.
Generalising to an arbitrary base is local to the function that finds
the answer for one element. It's an entirely separate axis of
generalisation to that of where the elements come from.
It's interesting to me that you consider one simply wrong and the other natural.
In some languages the "wrong" one does not even merit
consideration as it's just there for free.
On 08.09.23 23:02, Ben Bacarisse wrote:
"G.B." <bauhaus@notmyhomepage.invalid> writes:
On 07.09.23 01:32, Ben Bacarisse wrote:There was nothing stateful (as I understand the term) in either function
A fix (though it's not really ideal) would be to use function
composition here (inventing . as the composition operator):
Hm. A stateful, composed function that needs to be applied
in a certain way. Is that so different from calling interface
subprograms of a certain type?
being composed.
The "apparatus" that the computation needs in order to remember
"max so far" looks like part of its state to me.
Somehow
"the function" needs to operate this state and evaluate it.
Extend this to:
- find the maximum of [the maxima of] these n collections
- find the maximum in this stream at 10 seconds from now.
Is it possible, or practical, to define a pure function so that
calling it will remember the needed information, n >= 0
being arbitrary?
So, does type composition help?My turn to guess now: you are not being serious? I see no connection to
monads or type composition.
In the following sense:
There is an object of type So_Far that can remember
objects of any type T, them coming from collections
of type C-of-T.
I'm not sure if the new Ada.Iterator_Interfaces (LRM 5.5.1)
could solve this, also because I really don't know that yet.
But it looks like I'd need instances of specific containers
for instantiation. (That being consistent with Ada's approach
to the STL, I think.)
On 2023-09-09 02:25, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
[rant on]Yes, that's fine. Different languages have different objectives. Just
An Ada programmer would just write a loop.
write the empty range test and the loop you need for each kind of
collection.
You can loop in Ada over empty ranges, no problem.
That was definitely the way things were done in the 80s.
Yes, before the Dark Ages of Computing...
As I said you think in a wrong direction of abstracting the languageGeneralising to an arbitrary base is local to the function that finds
"finding maximum" rather than the problem space, e.g. generalization to
other bases, other mathematical structures etc.
the answer for one element. It's an entirely separate axis of
generalisation to that of where the elements come from.
It's interesting to me that you consider one simply wrong and the other
natural.
Because one is a software design artifact and another is the result of problem space analysis.
In some languages the "wrong" one does not even merit
consideration as it's just there for free.
Being a part of design it has all possible merits to consider and then
reject it. That is in the position of a puzzle solver. Now in the position
of a library designer, the Ada standard library has an [informal] interface that supports what you wanted:
1. Cursor
2. Key at the cursor
3. Element at the cursor
4. Iterate procedure
So, for the Ada standard library it might look like this:
generic
type Container_Type (<>) is limited private;
type Element_Type is private;
type Key_Type is private;
type Cursor_Type is private;
with function "<" (Left, Right : Element_Type) return Boolean is <>;
with function Key (Position : Cursor_Type) return Key_Type is <>;
with function Element
( Position : Cursor_Type
) return Element_Type is <>;
with procedure Iterate
( Container : Container_Type;
Process : not null access procedure
(Position : Cursor_Type)
) is <>;
function Generic_Container_Maximum_At (Container : Container_Type)
return Key_Type;
function Generic_Container_Maximum_At (Container : Container_Type)
return Key_Type is
Found : Boolean := False;
Max : Element_Type;
Result : Key_Type;
procedure Walker (Position : Cursor_Type) is
begin
if Found then
if Max < Element (Position) then
Result := Key (Position);
Max := Element (Position);
end if;
else
Result := Key (Position);
Max := Element (Position);
Found := True;
end if;
end Walker;
begin
Iterate (Container, Walker'Access);
if Found then
return Result;
else
raise Constraint_Error with "Empty container";
end if;
end Generic_Container_Maximum_At;
Instantiation:
package Integer_Maps is
new Ada.Containers.Ordered_Maps (Integer, Integer);
use Integer_Maps;
function Integer_Map_Max is
new Generic_Container_Maximum_At (Map, Integer, Integer, Cursor);
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-09 02:25, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
[rant on]Yes, that's fine. Different languages have different objectives. Just
An Ada programmer would just write a loop.
write the empty range test and the loop you need for each kind of
collection.
You can loop in Ada over empty ranges, no problem.
Yes, but the problem in hand (maximum of F over X) should raise an error
on an empty X. I know there are other options, but you chose to
raise an error so that's the design I was talking about.
That was definitely the way things were done in the 80s.
Yes, before the Dark Ages of Computing...
Eh? There have been repeated updates to the Ada language. Are they
taking Ada into the dark ages? If so, what was the golden age of Ada
when its design was perfect for numerical algorithms?
As I said you think in a wrong direction of abstracting the languageGeneralising to an arbitrary base is local to the function that finds
"finding maximum" rather than the problem space, e.g. generalization to >>>> other bases, other mathematical structures etc.
the answer for one element. It's an entirely separate axis of
generalisation to that of where the elements come from.
It's interesting to me that you consider one simply wrong and the other
natural.
Because one is a software design artifact and another is the result of
problem space analysis.
Which one the wrong one?
This is probably the closest we can get to a universal solution.
Vectors don't have a Key function but I am sure I could find out what
should be provided there.
The "apparatus" that the computation needs in order to remember
"max so far" looks like part of its state to me. Somehow
"the function" needs to operate this state and evaluate it.
On 2023-09-10 03:20, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-09 02:25, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
Which one the wrong one?As I said you think in a wrong direction of abstracting the language >>>>> "finding maximum" rather than the problem space, e.g. generalization to >>>>> other bases, other mathematical structures etc.Generalising to an arbitrary base is local to the function that finds
the answer for one element. It's an entirely separate axis of
generalisation to that of where the elements come from.
It's interesting to me that you consider one simply wrong and the other >>>> natural.
Because one is a software design artifact and another is the result of
problem space analysis.
None automatically is. The point is avoiding overdesigning a numeric
puzzle.
[...]
This is probably the closest we can get to a universal solution.
Vectors don't have a Key function but I am sure I could find out what
should be provided there.
Vector has To_Index for Key.
In general, note that Ada does not require you to use any library. I personally dislike cursors in particular because of their "functional"
style. I prefer plain element position and loop iteration of ordered structures. A container library based on this paradigm would use other generic abstraction.
Furthermore, I prefer dynamic polymorphism of tagged types over parametric one of generics. Therefore to me Maximum_At should rather be a class-wide
or primitive operation than a generic.
In Ada you have freedom to choose your way, which also massively reduces universality of any abstraction, which will never apply universally.
I would like to have means to deal with this problem by means of ad-hoc supertypes, but that will never happen due to lack of interest in reworking the language type system and because in "Dark Ages" there is virtually no research on fundamental language construction topics.
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-10 03:20, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-09 02:25, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
Which one the wrong one?As I said you think in a wrong direction of abstracting the language >>>>>> "finding maximum" rather than the problem space, e.g. generalization to >>>>>> other bases, other mathematical structures etc.Generalising to an arbitrary base is local to the function that finds >>>>> the answer for one element. It's an entirely separate axis of
generalisation to that of where the elements come from.
It's interesting to me that you consider one simply wrong and the other >>>>> natural.
Because one is a software design artifact and another is the result of >>>> problem space analysis.
None automatically is. The point is avoiding overdesigning a numeric
puzzle.
Ah, I thought your criticise was intended to be general -- that
"abstracting the language 'finding maximum' rather than the problem
space" was always wrong, but it seems you meant only in the case of a
puzzle like this. Numeric puzzles like this should only be generalised
in a few "approved" directions?
Since prime numbers are crucial here, I had already tried a couple of
prime sieves in one of my solutions. In that Ada solution, I would
probably have to store the primes somewhere and maximise over that.
That's what got me thinking about a general "maximise F over X" function because if Ada had a simple way to do that, I could try various ways to
write the sieve -- the primes might end up in an array, a set or a map,
and it would make no difference to the rest of the code.
But the conclusion seems to be that maximising over any container is
just too simple to be worth making it a reusable component in Ada. And
even then it would not (as far as I can tell) work for native arrays.
[...]
This is probably the closest we can get to a universal solution.
Vectors don't have a Key function but I am sure I could find out what
should be provided there.
Vector has To_Index for Key.
In general, note that Ada does not require you to use any library. I
personally dislike cursors in particular because of their "functional"
style. I prefer plain element position and loop iteration of ordered
structures. A container library based on this paradigm would use other
generic abstraction.
Furthermore, I prefer dynamic polymorphism of tagged types over parametric >> one of generics. Therefore to me Maximum_At should rather be a class-wide
or primitive operation than a generic.
I was looking for whatever design you thought best, since you know Ada infinitely better that I do.
It would be a shame if something I said
has ended up causing you to propose solutions you don't think are the
best ones for this example.
In Ada you have freedom to choose your way, which also massively reduces
universality of any abstraction, which will never apply universally.
That's a strange remark. You have to do things the Ada way. The
freedom is only in choosing how to combine the specific tools in Ada's toolbox, and Ada also constrains how the tools can be combined.
I would like to have means to deal with this problem by means of ad-hoc
supertypes, but that will never happen due to lack of interest in reworking >> the language type system and because in "Dark Ages" there is virtually no
research on fundamental language construction topics.
I don't believe that to be the case. I can believe that there is little research into overhauling Ada's type system, but not in general.
On 2023-09-10 21:22, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-10 03:20, Ben Bacarisse wrote:Ah, I thought your criticise was intended to be general -- that
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-09 02:25, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
Which one the wrong one?As I said you think in a wrong direction of abstracting the language >>>>>>> "finding maximum" rather than the problem space, e.g. generalization to >>>>>>> other bases, other mathematical structures etc.Generalising to an arbitrary base is local to the function that finds >>>>>> the answer for one element. It's an entirely separate axis of
generalisation to that of where the elements come from.
It's interesting to me that you consider one simply wrong and the other >>>>>> natural.
Because one is a software design artifact and another is the result of >>>>> problem space analysis.
None automatically is. The point is avoiding overdesigning a numeric
puzzle.
"abstracting the language 'finding maximum' rather than the problem
space" was always wrong, but it seems you meant only in the case of a
puzzle like this. Numeric puzzles like this should only be generalised
in a few "approved" directions?
Yes, in the direction of numeric problem space. Universal finding maximum
is another problem space, e.g. a container library design etc.
Since prime numbers are crucial here, I had already tried a couple of
prime sieves in one of my solutions. In that Ada solution, I would
probably have to store the primes somewhere and maximise over that.
That's what got me thinking about a general "maximise F over X" function
because if Ada had a simple way to do that, I could try various ways to
write the sieve -- the primes might end up in an array, a set or a map,
and it would make no difference to the rest of the code.
And this is exactly wrong.
You should think about whether storing
represents an issue, e.g. in terms of performance and/or space. If it does you should consider suitable implementation of storage that provides
required overall performance of needed operations, like insertion, search, cleaning up etc.
But the conclusion seems to be that maximising over any container is
just too simple to be worth making it a reusable component in Ada. And
even then it would not (as far as I can tell) work for native arrays.
You do not need *any* container. You need a container, just one.
[...]I was looking for whatever design you thought best, since you know Ada
This is probably the closest we can get to a universal solution.
Vectors don't have a Key function but I am sure I could find out what
should be provided there.
Vector has To_Index for Key.
In general, note that Ada does not require you to use any library. I
personally dislike cursors in particular because of their "functional"
style. I prefer plain element position and loop iteration of ordered
structures. A container library based on this paradigm would use other
generic abstraction.
Furthermore, I prefer dynamic polymorphism of tagged types over parametric >>> one of generics. Therefore to me Maximum_At should rather be a class-wide >>> or primitive operation than a generic.
infinitely better that I do.
The best design is plain loop.
It would be a shame if something I said
has ended up causing you to propose solutions you don't think are the
best ones for this example.
My understanding was that you wanted to see how to use the Ada standard library containers with generics.
Generic programming in Ada (programming in terms of sets of types) is a
huge, almost infinite topic. One should be rather specific.
In Ada you have freedom to choose your way, which also massively reduces >>> universality of any abstraction, which will never apply universally.That's a strange remark. You have to do things the Ada way. The
freedom is only in choosing how to combine the specific tools in Ada's
toolbox, and Ada also constrains how the tools can be combined.
There are more than one way to skin a cat in Ada. You can choose one drawer in the Ada toolbox and feel comfortable with what it provides all your
life.
"Ada way" among Ada users rather refers to an approach to software engineering in general. Like upfront specification, separation and careful design of interfaces, modular design, problem space driven choice of types, earliest possible error detection etc.
I would like to have means to deal with this problem by means of ad-hocI don't believe that to be the case. I can believe that there is little
supertypes, but that will never happen due to lack of interest in reworking >>> the language type system and because in "Dark Ages" there is virtually no >>> research on fundamental language construction topics.
research into overhauling Ada's type system, but not in general.
I am not aware of any substantial contributions since Cardelli
etc. Recently designed languages represent a pitiful mess of old wrong
ideas in an ongoing competition to create something more flawed than K&R
C...
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-10 21:22, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
You should think about whether storing
represents an issue, e.g. in terms of performance and/or space. If it does >> you should consider suitable implementation of storage that provides
required overall performance of needed operations, like insertion, search, >> cleaning up etc.
Yes, in Ada. Since I can't use universal algorithms, it pays to decide
all this first because changes will be costly.
But in some other
languages I can try various schemes and measure or profile to see what time/space trade-offs there are between different designs.
This is
easiest when I don't have to worry about all the changes that simply switching from, say, a list to an array will incur.
But the conclusion seems to be that maximising over any container is
just too simple to be worth making it a reusable component in Ada. And
even then it would not (as far as I can tell) work for native arrays.
You do not need *any* container. You need a container, just one.
Yes, in Ada. The cost of changing a design is going to be
non-negligible, so we must make sure you get it right before too much
code is written.
It would be a shame if something I said
has ended up causing you to propose solutions you don't think are the
best ones for this example.
My understanding was that you wanted to see how to use the Ada standard
library containers with generics.
Well that's what it turned out to be. At first I did not know that
built-in types like arrays can't be covered in the same way.
Generic programming in Ada (programming in terms of sets of types) is a
huge, almost infinite topic. One should be rather specific.
Sorry. I was hoping that generalising from a range to an array or some
other container would not be the huge topic it turned out to be.
In Ada you have freedom to choose your way, which also massively reduces >>>> universality of any abstraction, which will never apply universally.That's a strange remark. You have to do things the Ada way. The
freedom is only in choosing how to combine the specific tools in Ada's
toolbox, and Ada also constrains how the tools can be combined.
There are more than one way to skin a cat in Ada. You can choose one drawer >> in the Ada toolbox and feel comfortable with what it provides all your
life.
"Ada way" among Ada users rather refers to an approach to software
engineering in general. Like upfront specification, separation and careful >> design of interfaces, modular design, problem space driven choice of types, >> earliest possible error detection etc.
Yes, I remember the 80s! It's rare to have specifications that don't
change these days.
And general remarks like "problem space driven
choice of types" apply to all languages.
What matters is what types the
language offers, and what the interfaces to those types are.
On 2023-09-11 18:13, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
You should think about whether storing
represents an issue, e.g. in terms of performance and/or space. If it does >>> you should consider suitable implementation of storage that provides
required overall performance of needed operations, like insertion, search, >>> cleaning up etc.
Yes, in Ada. Since I can't use universal algorithms, it pays to decide
all this first because changes will be costly.
There are no universally applicable algorithms.
My understanding was that you wanted to see how to use the Ada standard
library containers with generics.
Well that's what it turned out to be. At first I did not know that
built-in types like arrays can't be covered in the same way.
I know no language where primitive built-in types may have classes.
What matters is what types the
language offers, and what the interfaces to those types are.
That does not matter at all. Matters the type algebra by which programmer
can create types suitable to model the problem space entities.
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
On 2023-09-11 18:13, Ben Bacarisse wrote:
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
You should think about whether storing
represents an issue, e.g. in terms of performance and/or space. If it does >>>> you should consider suitable implementation of storage that provides
required overall performance of needed operations, like insertion, search, >>>> cleaning up etc.
Yes, in Ada. Since I can't use universal algorithms, it pays to decide
all this first because changes will be costly.
There are no universally applicable algorithms.
This may be just a case where we are using terms to refer to different things. I find it hard to believe you don't know what I am referring to since we've had a productive exchange examining an example in detail,
but I can agree it's not a good term. I simply could not come up with a better one on the fly.
So I'll re-phrase it avoiding the disputed term: simple fibledychops are
not available to the Ada programmer, but they are available in some
other languages. I suspect you are not interested in what simple fibledychops are, since their absence from Ada means they are not of any importance (and may even be, in your opinion, detrimental to writing
good programs). If you really want to know what a fibledychop is, I can
have a go at saying more about what they it, but would that be
worthwhile? I think you are sure they are a bad idea already.
My understanding was that you wanted to see how to use the Ada standard >>>> library containers with generics.
Well that's what it turned out to be. At first I did not know that
built-in types like arrays can't be covered in the same way.
I know no language where primitive built-in types may have classes.
Haskell's type classes are very nice -- every type belongs to one or
more classes that determine the permitted operations.
In ML for example,
the interface to arrays is defined in ML so they can support a universal
set of operations shared with many other types, but they are usually implemented by the run-time environment for speed.
What matters is what types the
language offers, and what the interfaces to those types are.
That does not matter at all. Matters the type algebra by which programmer
can create types suitable to model the problem space entities.
Yes, but... First, almost every language comes with some predefined
types. If the problem space needs fast indexed access to a set of
entities, we don't usually have to define our own arrays or vectors.
Secondly, the problem space
has two components -- data and actions on that data. I suspect by
"problem space entities" you mean just the data because that what Ada
focuses on.
El dia dilluns, 4 de setembre de 2023 a les 11:19:53 UTC+2, CSYH (QAQ) va escriure:sorry for reply so late. I just do not know how to install the lib to my GNAT.
I am new to Ada, I know is there a good way to start this program?Hi CSHY,
thanks
https://projecteuler.net/problem=26
Please take a look at my Euler tools repository, https://github.com/rocher/euler_tools (not the best math lib you'll find, I know).
I used this library tools to solve problem 26 here: https://github.com/rocher/alice-project_euler-rocher
Let me know what you think.
Please take a look at my Euler tools repository, https://github.com/rocher/euler_tools (not the best math lib you'll find, I know).
I used this library tools to solve problem 26 here: https://github.com/rocher/alice-project_euler-rocher
Let me know what you think.
sorry for reply so late. I just do not know how to install the lib to my GNAT.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 399 |
Nodes: | 16 (2 / 14) |
Uptime: | 65:04:50 |
Calls: | 8,355 |
Calls today: | 15 |
Files: | 13,159 |
Messages: | 5,893,953 |
Posted today: | 1 |