On Mon, 22 Oct 2012 18:48:04 +0000 (UTC), glen herrmannsfeldt
<
g...@ugcs.caltech.edu> wrote:
> Dick Hendrickson <
dick.hen...@att.net> wrote:
>
> (snip, and previous snip, on why no subscripting, substringing,
> structure member selecting, and function calling on function results.)
>
<snip: believed or at least suspected to be ambiguous>
> In many other languages, substring is an intrinsic function, not
> an operator. That allows it to be used on any expression, which
> sometimes might be useful. The first language I knew with the
> substring operator was HP2000 BASIC. (Is that here Fortran inherited
> it from?)
>
Pedantic: As you said elsethread, strictly speaking, it's not an
operator in Fortran: in F77 it's one form of primary in an arithmetic
character or logical expression as applicable; in F90+ it's (more
formally) a data designator, which can be (and often is) an expression
primary. C's treatment of array subscripting and struct member
selection, and also simple and compound assignments, as operators
almost the same as traditional computational operators (like plus) and
relational and boolean operators -- rather than another category --
was unusual when it was created and still far from universal.
> Like Fortran, HP BASIC allows one to use the substring operator on
> the left side on an assignment, but restricts against the creation
> of a non-contiguous string. (String variables have a dynamic length
> up to the declared maximum. You can't leave holes in assignment.)
>
> PL/I has the SUBSTR function which allows for a substring of any
> string expression, or any expression that can be converted to string.
>
which with PL/I's expansive ideas about conversion is quite a lot <G>
especially since PL/I has both character strings AND bit strings.
> There is also the SUBSTR pseudo-variable that can be used on the left
> side of an assignment to assign to part of a string. There are no
> (last I knew) user-defined pseudo-variables, and PVs don't nest.
>
> You can't say SUBSTR(SUBSTR(S,1,10),4,3)="xx"; even if it
> might make sense.
>
Are you sure? Composing SUBSTR like that is silly and I wouldn't have
tried, but ISTR that I did use SUBSTR(UNSPEC(x),bits)=y. But it was
long ago and my memory could be suffering from alpha particles.
> Three other pseudo-variables are REAL, IMAG, and COMPLEX, <snip>
> Note for later that PL/I also allows for array expressions.
>
> > Some other points, in no particular order.
>
> > 1) Structures and arrays were added about the same time but shepherded
> > by different sub-groups and other sub-groups did other stuff. It's
> > possible that something was badly ambiguous in early versions and
> > rejected. Subsequent (simplified?) versions might have
> > removed/mitigated the ambiguity and nobody went back are rethought early
> > decisions. It's too bad, but that's the way people often work.
>
> and even more CHARACTER variables were added in Fortran 77, though I
> don't remember is substrings were, or came later.
>
Substrings are in F77; confirmed while checking my wording above.
> > 2) Selectors are pretty inefficient in the worst case. <snip>
> Note that as mentioned early in the thread, C allows subscripting
> function results, but C does NOT allow array expressions or functions
> returning arrays. C allows for functions returning pointers, and
> array subscripting is closely related to pointer indexing.
>
Continuing the above pedantry, C doesn't have what Fortran folks would
consider array expressions (add, multiply, transpose, etc). Formally
struct.memberarray is an expression in K&R C (vs s%a is a primary or
designator in Fortran) and array compound literal in C99 is a form of
postfix-expression (which is one step away from primary) vs
array-constructor is a designator in F90+.
> K&R C didn't allow for functions to return structures, but did
> allow for structure pointers. ANSI added functions returning
> structures, and structures can contain static sized arrays.
>
Yes (and also by-value argument, and assignment)
> > 3) As I recall, there was no demand. In the 80s many companies were
> > making parallel processors and often designing their own array
> > languages. None of them strongly advocated for allowing array function
> > selectors.
>
> Unlike C, where subscripting operates on pointers, subscripting
> Fortran array return values or expressions is likely to be
> inefficient. (Unless the optimizer figures it out.)
>
> > 4) F90 was viewed as being big and complex. This would be one more
> > complexity.
>
> > 5) Functions are merely one kind of expression. If we can select from
> > a function result, why not from an expression. <snip>
> Now, not that C does not allow for array expressions, so some that
> might otherwise be ambiguous aren't.
>
IHYM 'note'.
> In C, you an add or subtract to a pointer (which might represent
> an array), though you can't multiply or divide one. You can also
> subtract two pointers. The result is defined if both point to
> (possibly different positions) within the same array.
>
> C functions can validly return pointers to static arrays and to
> dynamically allocated arrays. In the latter case, the returned
> value should not be subscripted if it is the only pointer to
To be exact it causes a memory leak, which in general is a problem but
in some situations is okay. And exactly the same for member selection
from a returned structure. Nothing worse than a leak can happen ...
> the object. Local variables, including arrays, go away on
> function return (usually on the stack) and should not be used
> after the return. It is an easy mistake in C to return a pointer
> to a local auto array.
>
... whereas using 'auto' storage after the end of its lifetime is
always undefined and often gives at least garbage output.
> Note, though, that the structure member . is also used in object
> oriented languages like C++ and Java for the class method selector.
> It easily nests when you want to call a method in the class returned
> by the first call. You might, for example:
>
> String s="abcdefghijklmnop";
> System.out.println(s.substring(3,3).substring(1,1));
>
> in Java. (There are probably better example, but you can invoke any
Yes that is a silly example. Real ones that I have used often are
s .substring(...) .trim(), s .substring(...) .toLower() (or toUpper),
s .substring(...) .replace(...) or variants.
Further, some classes are _designed_ to be used this way. E.g.
StringBuffer and StringBuilder .append methods return the object they
were invoked on, so you can do b .append("X=") .append(42) etc.
C++ also uses the same idea with operator syntax e.g. the canonical
output_stream << x << y and input_stream >> x >> y .
> method of a class from any expression of the type of that class.
> (Well, you can invoke a method based on a reference to an Object of
> the class.) With garbage collection, you don't have to worry
> about what happens to all those Objects.
>
That's kind of redundant. All 'object' values in Java, including
variables and method returns, are references; there are no by-value
or by-copy objects in the language -- although there is a trivial
standard interface for classes that choose to implement clone().
Also the box types silently convert to and from primitive types, with
(only) the latter primitive types by-value.
This can be most surprising for arrays, because all arrays in Java are
objects (though not strictly classes) even arrays of primitive types.
{ int a = 3; int b = a; a = 5; /* b is still 3 */ }
{ int[] a = {3}; int[] b = a; a[0] = 5; /* b[0] is now 5 */ }
>
> Simplifying some, static methods are called based on the static
> type of the expression, where instance methods are called based
> on the current type. The difference is important in the case
> of subclasses.
>
More exactly, dispatched. The calls you can compile depend on the
*declared* type of the object you use. Also you can write a static
method call using the typename (class or interface) directly instead
of an object declared that type, and this is considered better style;
e.g. the Eclipse IDE by default puts ugly warning marks on
instance.static_method() calls.
For the more interesting non-static=instance methods, yes it does
dispatch on the runtime type as OO must. But if you have
class Foo { void walk () { ... } }
class BigFoo extends Foo { void shrink () { ... } }
then in a chunk of code
{ Foo x = new BigFoo ();
x.walk(); // okay, and also okay if x is actually a baseclass Foo
x.shrink(); // compile error, even though runtime x is a BigFoo
((BigFoo)x).shrink(); // okay
}
If you want to reach methods added (versus overridden) in known
subclasses you have to cast; usually to be safe you should cast only
after checking obj instanceof Subclass. If you don't know at coding
time (all) the possible subclasses of interest, at runtime you can use
reflection but it's more work and clutter.