Thank you for this totally lucid explanation.It is bookmarked and saved in my APL reference notes.
respect…
Hi Hans-Peter,
this kind of surprises has puzzled me since the early days of APL.
They already existed in APL1 but never occurred in practice since
the number of constructs affected, for instance +//'ABC' was
small
and did not make much sense at that time.
The proliferation of operators in APL2 then not only increased the
number of affected constructs but also included constructs that
could
make sense. Your examples are instances of these constructs.
In C/C++ the order in which functions/operators are evaluated is
unambiguously defined by two attributes of each operator: its
precedence
and its associativity. The precedence defines in which order
different groups
of operators (like × or ÷ versus + or -) are computed while the
associativity
defines the order (like left-to-right or right-to-left) in which
operators with
the same precedence are computed.
In APL, however, the situation is rather different. The ISO
standard indirectly
specifies the well-known right-to-left evaluation of APL
expressions, but does
not specify an order when it comes to APL functions versus APL
operators.
Hybrids like /, ⌿, \, and ⍀ make matters even worse. IBM APL2 is
more specific
about that by essentially defining that:
(1) /, ⌿, \, and ⍀ are (monadic)
operators (even though their (single) left argument could
be a value), and
(2) arguments of operators bind stronger than arguments of
functions.
(3) the left operand of an operator can be a value or a
function,
Although these rules determine the order of evaluation in most
cases they do not,
at least as I understand the matter, catch all cases. The
missing piece is how two
handle the pattern A O P B where A and B are values and O and P
are monadic
operators (of which O allows a value as left argument). This
pattern has two
valid evaluations:
(A O) P B and A (O P) B
The first evaluation binds A to O, the derived function (A O) is
then bound to P,
giving another derived function ((A O) P) which is then
evaluated with argument B.
The second evaluation binds O to P, and the derived function (O
P) is then
evaluated with arguments A and B.
As already mentioned this this can only occur if an operator
allows a value as left
argument, which is fortunately only the case for a few
pathological operators,
(in particular /, ⌿, \, and ⍀).
I have tried to explain this in earlier versions of the GNU info
manual, but have removed
it recently due to a somewhat incorrect and misleading wording
in the manual.
With the above explanation in mind, I believe that GNU APL
behaves correctly even
though a different behaviour (with unfortunately different
resuilts) would be equally correct.
You can, BTW, watch the GNU parser at work by enabling logging
facility 32 (note
that the program counter PC of the virtual APL machine counts
from right to left).
]LOG 32
1 2 3 / ¨ 'ABC'
changed to Prefix[si=2])
============================================
[si=2 PC=0] Read token[0] (←0←)
VALUE1«≡⊏3⊐ABC» TC_VALUE
fifo[si=2 len=1 PC=1] is now : TC_VALUE at
Prefix.cc:564
[si=2 PC=1] Read token[1] (←0←) ¨ TC_OPER1
fifo[si=2 len=2 PC=2] is now : TC_OPER1 TC_VALUE
at Prefix.cc:564
[si=2 PC=2] Read token[2] (←0←) / TC_OPER1
fifo[si=2 len=3 PC=3] is now : TC_OPER1 TC_OPER1
TC_VALUE at Prefix.cc:564
phrase #297: M M matches, prio 42, calling
reduce_M_M__()
reduce_M_M__() returned: RA_CONTINUE
fifo[si=2 len=2 PC=3] is now : TC_FUN12 TC_VALUE
at Prefix.cc:564
[si=2 PC=3] Read token[2] (←0←) VALUE3«≡⊏3⊐1 2
3» TC_VALUE
fifo[si=2 len=3 PC=4] is now : TC_VALUE TC_FUN12
TC_VALUE at Prefix.cc:564
phrase #234: A F B matches, prio 33, calling
reduce_A_F_B_()
reduce_A_F_B_() returned: RA_CONTINUE
fifo[si=2 len=1 PC=4] is now : TC_VALUE at
Prefix.cc:564
[si=2 PC=4] Read token[1] (←0←) ENDL TC_END
fifo[si=2 len=2 PC=5] is now : TC_END TC_VALUE at
Prefix.cc:564
phrase #46: END B matches, prio 2, calling
reduce_END_B__()
A BB CCC
reduce_END_B__() returned: RA_PUSH_NEXT
[si=2 PC=5] Read token[0] (←0←) RETURN_STATS
TC_RETURN
fifo[si=2 len=1 PC=6] is now : TC_RETURN at
Prefix.cc:564
phrase #13: RETC matches, prio 1, calling
reduce_RETC___()
- end of ◊ context
reduce_RETC___() returned: RA_RETURN
Prefix::reduce_statements(si=2) returned VOID in
StateIndicator::run()
1 2 3 / ¨ 'ABC'
Finally, if you need GNU APL to behave differently, then you can
enforce a different order by means of { and }, similar to ( and
) for
values.
The default behaviour of GNU APL is this:
1 2 3 /¨ 'ABC' ⍝ / bound
to ¨
A BB CCC
which corresponds to pattern A (O P) B above. If you prefer (A
O) P B instead,
then you can do this:
{1 2 3/⍵} ¨ 'ABC' ⍝ 1 2 3
bound to /
AAAAAA BBBBBB CCCCCC
which corresponds to pattern (A O) P B above. This
is almost as easy as
using parentheses for values.
Best Regards,
Jürgen
On 11/26/20 1:28 PM, Hans-Peter Sorge
wrote:
Hi,
I thought so, APL is fun:-)
The following 4 expressions are just a repeat:
⍝1 - a vector replicates a scalar. The result is a simple vector
1 2 3/'A'
AAAAAA
⍝2 - scalar by scalar - simple vector as result
1 2 3/'ABC'
ABBCCC
⍝3 - as in ⍝1 but for each scalar a vector replicates a scalar.
The result is a nested vector of simple vectors
(⊂1 2 3)/¨'ABC'
AAAAAA BBBBBB CCCCCC
⍝4 - is consistent with ⍝2 the each-operator introduces one level
of depth
1 2 3/¨'ABC'
A BB CCC
The next result would be "surprising" to me as it is somewhere
between 2 and 3 and logic asside, "feels" inconsistent:
⍝4
⍝ 1 2 3/¨'ABC'
⍝ AAAAAA BBBBBB CCCCCC
Going a little further. The simple vector 'ABC' is being turned
into a nested vector.
⍝5,6
1 2 3/,¨'ABC'
A B B C C C
1 2 3/∊¨'ABC'
A B B C C C
⍝9 - scalar applied to simple vector
2/'ABC'
AABBCC
2/¨'ABC'
AA BB CC
⍝ 10 - as expected having a nested vector:
2/,¨'ABC'
A A B B C C
Here comes my irritation. Or some missing knowledge ....
⍝ 11 - if this is true ( from ⍝3 )
(⊂1 2 3)/¨'ABC'
AAAAAA BBBBBB CCCCCC
⍝ 12 - then this schould be different
(⊂1 2 3)/¨,¨'ABC'
AAAAAA BBBBBB CCCCCC
⍝ expected
A AA AAA B BB BBB C CC CCC
⍝ 13 - instead of domain error ..
(⊂1 2 3)/'ABC'
DOMAIN ERROR
(⊂1 2 3)/'ABC'
^ ^
⍝ the result could be
AAAAAABBBBBBCCCCCC
⍝ 14 - like in case of a simple vector, the nested vector
results are accordingly:
1 2 3/'AA' 'BB' 'CC'
AA BB BB CC CC CC
⍝like ⍝4
1 2 3/¨'AA' 'BB' 'CC'
AA BBBB CCCCCC
⍝ 15 - and , analogous to 12
(⊂1 2 3)/¨'AA' 'BB' 'CC'
LENGTH ERROR
(⊂1 2 3)/¨'AA' 'BB' 'CC'
^ ^
⍝ could then be
AA AA AA AA AA AA BB BB BB BB BB
BB CC CC CC CC CC CC
Just my thoughts..
Best Regards
Hans-Peter
Am 24.11.20 um 17:17 schrieb Dr.
Jürgen Sauermann:
Hi Adam,
thanks, see below.
Best Regards,
Jürgen
On 11/23/20 11:07 PM, Adám Brudzewsky wrote:
For the sake of
compatibility with IBM APL2
Speaking of which, what is GNU APL's official policy?
GNU APL's general policy regarding standards and the like is to
consider,
(in that order):
1. the IBM APL2 language reference manual,
2. the IBM APL2 implementation (PC demo version, can't aford the
commercial version),
3. the ISO standard,
4. other APL implementations and suggestions from bug-apl@gnu.org.
Sometimes, however, this can have undesirable consequences and
then
some sort of common sense is applied to change the order.
the ambiguity related to / ⌿ \ and ⍀ is not
resolved by these rules.
I see. That statement is probably a left-over from the early
days of GNU APL. When I started
with GNU APL, all I had was some vague recollection of APL1 from
the 1970s (my first APL
computer was an IBM 5110 desktop, a precursor of the IBM PC). At
that time functions were
functions and values were values. At some later time, triggered
by the ⍤-operator (which was
not implemented in IBM APL2 but defined in ISO), I changed to
the APL2 way of handling /
and \.
I have updated the documentation, SVN 1363.
The APL2 language reference does in fact make it
perfectly clear how / and friends are treated, namely as
operators. Always. Note:
Note that this makes APL2 ISO non-compliant. Indeed,
here, GNU APL follows Dyalog and NARS2000:
1 2 3/¨'ABC'
A BB CCC
While APL2 and APLX give:
1 2 3/¨'ABC'
AAAAAA BBBBBB CCCCCC
This is because 1 2 3/ is a derived
monadic function and ¨ maps the entire
function to each letter.
I believe older GNU APL versions would have given the APL2/APL X
results, while newer versions
give the other result. This is one of the examples where the
general GNU APL policy is not being
followed. If an incompatibility with APL2 exists long enough
with no complaints from the users,
then I believe backward compatibility with GNU APL is more
important than compatibility with IBM
APL2.
Hi Kacper, Adam,
thanks. For the sake of compatibility with IBM APL2
I have changed ⎕UCS so that
it accepts float and complex numbers that are near
to integers. My initial thinking was
that e.g. ⎕UCS of a complex number is most
likely a programming mistake so
that a DOMAIN ERROR would be more helpful than being
a burden. But APL2
compatibility is an even stronger argument in this
case.
I have also adjusted the integer domain which is now
-$80 ... $7FFFFFFF
so that no conflicts with signed bytes from ⎕FIO
should arise.
SVN 1362.
Best Regards,
Jürgen
On 11/22/20 11:11 PM, Kacper Gutowski wrote:
On Sun, Nov 22, 2020 at
03:19:19PM +0100, Dr. Jürgen Sauermann wrote:
Floating point and complex
numbers are not allowed as to avoid interference
with ⎕CT (i.e. how should rounding be
performed?).
I share your sentiment regarding the upper bound
of the ⎕UCS domain, but throwing a domain error on
⎕UCS1E2 looks like a bug to me too. 1E2 is
clearly an integer regardless of the
implementation details, and I would be surprised
if APL2 didn't accept it. I would expect rounding
to be the same as in all the other places that
require near-integers, like array indices.
The negative ones are also a bit weird. I wasn't
aware of their existence, and they seem to work in
surprising ways when passed to various variants of
⎕CR.
-k
|