On 04.06.22 04:26, Waldek Hebisch wrote:
On Thu, Jun 02, 2022 at 11:07:06PM +0200, Ralf Hemmecke wrote:
I've just updated my branch
https://github.com/hemmecke/fricas/commits/elt-universalsegment
to be in line with the "intersection of indices" see testcases for
detail.
I do not think that in eager case returning empty (or shorter
aggregate) is good idea. Namely for streams normal if we have empty
stream and need an element, then we get error. OTOH in eager case we
depend on correct length and shortened aggregate will lead to
propagating wrong value.
To put it differenly, for vectors there are two consistent views,
namely curremnt one and alternative view that vectors are in fact
infinite but have only finite number of nonzero components. The
second view would give 0 for out of bound references. While
logically consistent, the second view pragmatically seem to be
inferior to current one. OTOH when we have stream of coefficients of
series, then empty stream mean that further coefficients are zero.
So at pragmatic level stream case and eager case (lists and vectors)
are different.
Our current code in eager case signals error early and this does not
cause trouble. As I wrote, signaling errors early for stream could
lead to trouble (for example, testing for empty before need would
break stream usage if done for all operations).
Your view may be sensible, but I am a bit worried about the actual
specification and whether user find that simple enough to actually use
that functionality. If the result is not predictable then nobody will
use list(u) or stream(u).
What you say above is basically that
list(u))::Stream(S)
and
(list::Stream(S))(u)
may give different results. Personally, I would find this a bit
confusing, but never mind, if the specification tells that this is the
case then that should be implemented.
So what actually is the specification?
We have...
LinearAggregate(S : Type) : Category ==
Join(IndexedAggregate(Integer, S), Collection(S),
Eltable(UniversalSegment Integer, %)) with
...
FiniteLinearAggregate(S : Type) : Category == Join(LinearAggregate S,
finiteAggregate)
and
StreamAggregate(S : Type) : Category ==
Join(UnaryRecursiveAggregate S, LinearAggregate S) with
...
and
ListAggregate(S : Type) : Category == Join(StreamAggregate S,
FiniteLinearAggregate S, ExtensibleLinearAggregate S) with
and
OneDimensionalArrayAggregate(S : Type) : Category ==
Join(FiniteLinearAggregate S, shallowlyMutable)
In other words, in all places where elt: (%, UniversalSegment) -> %
is implemented, it inherits the specification from
Eltable(UniversalSegment(Integer),%) with the documentation:
++ An eltable over domains D and I is a structure which can be viewed
++ as a function from D to I.
++ Examples of eltable structures range from data structures, e.g. those
++ of type \spadtype{List}, to algebraic structures, e.g.
++ \spadtype{Polynomial}.
Eltable(D : Type, I : Type) : Category == with
elt : (%, D) -> I
++ elt(u, i) (also written: u.i) returns the element of
++ u indexed by i.
++ Error: if i is not an index of u. (*)
It's a bit difficult to interpret "i is not an index of u" if i is a
segment, open or closed, with or without the "by step" part.
Let me appreviate U==>UniversalSegment(Integer).
What we should implement is a function (%, U)->%
Let's look at ListAggregate of OneDimensionalArrayAggregate. Their
elements are functions from a finite index set I into a set S.
Clearly, the error condition (*) tells me that an implementation
would have to give an error whenever u contains an integer that is
not in I. That clearly agrees with what you say about "error early in
eager structures".
Now Stream.
++ A stream is an implementation of a possibly infinite sequence using
++ a list of terms that have been computed and a function closure
++ to compute additional terms when needed.
That tells me that a stream is either a (lazy) list (= finite stream),
i.e. a function from a finite set I to S, or an infinite structure, i.e.
a function from N (natural numbers starting from 1) to S.
For finite streams, I would say that it should behave exactly as in the
list case. The only exception would be that the error occurs according
to the laziness, i.e. early in the list case and when a non-existing
index is accessed in the stream case.
For ininite streams there will be only an error if the lower index is
less then minIndex(x).
If x is a finite structure (eager of lazy) then I would interpret the
specification of elt in such a way that x(a.. by c) must always give
an error (in eager or lazy way).
Actually, that is not what I would want or what would be similar to
pythons slice semantics for lists of the semantics of Mathematica's span
semantics.
https://reference.wolfram.com/language/ref/Span.html
No it is not necessary that we must have the same semantics, but as a
convenience for the user I think that
[1,2,3,4](3..) (or [1,2,3,4](3.. by -1))
should give [3,4] (or [3,2,1]) instead of an error even though
the actual set of indices represented by the segment clearly contains an
index that falls out of the allowed range of the list.
So what do we want in this last case and how do we specify it in the
++-docstring of "elt"?
Ralf
--
You received this message because you are subscribed to the Google Groups "FriCAS -
computer algebra system" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/fricas-devel/44f69179-b9ef-1eb4-8baf-89bf23673a95%40hemmecke.org.