proper lvalues and whatnot

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

proper lvalues and whatnot

Ross A. Lippert

One of the neat things about octave is its incorporation of
C-like syntax, with cool operators like ++ and += and so on.

I am wondering why it is not the case that assignment expressions
are not likewise lvalues?

Is there some reason why
octave> (v = zeros(1,2))++
should not give
 v = [1 1]
??

Also, I am wondering why it is that while subscripted vectors/matrices
are lvalues, that
octave> v = [1 1];
octave> v([1 1 2])++
does not give
 v = [3 2];
??

OK, so maybe I am a sick puppy.  But this is what I am really after.

> [i,j,nz] = find(M);
> (y=zeros(max(i),1))(i) += nz.*x(j)
and have
 y = M*x

I have a lot of sparse matrices I want to play with and this sort of
syntax would rock.


Is there a conceptual reason why I should not try to modify octave to
do this (i.e. how does this break the .m language as a language)?

Is there a technical reason why I should not try to modify octave to
do this (i.e. requires a total interpreter rewrite, I'd be in over my
head)?


-r


Reply | Threaded
Open this post in threaded view
|

proper lvalues and whatnot

John W. Eaton-6
On 18-Aug-2000, Ross A. Lippert <[hidden email]> wrote:

| One of the neat things about octave is its incorporation of
| C-like syntax, with cool operators like ++ and += and so on.
|
| I am wondering why it is not the case that assignment expressions
| are not likewise lvalues?
|
| Is there some reason why
| octave> (v = zeros(1,2))++
| should not give
|  v = [1 1]
| ??

Hmm.  Because the result of an assingment is not an lvalue, even in C?

| Also, I am wondering why it is that while subscripted vectors/matrices
| are lvalues, that
| octave> v = [1 1];
| octave> v([1 1 2])++
| does not give
|  v = [3 2];
| ??

Now I think this is a valid thing to want to do, and it corresponds to
something that does work in C:

  #include <stdio.h>

  int
  main (void)
  {
    int x[1];

    x[0] = 0;

    x[0]++;

    printf ("%d\n", x[0]);

    return 0;
  }

Octave also accepts this syntax, but maybe it doesn't do exactly what
you expect.  For example (using the current sources, which are close
to 2.1.31),

  octave:1> v = 1:10
  v =

     1   2   3   4   5   6   7   8   9  10

  octave:2> v([3, 4])++
  ans =

    3  4

  octave:3> v
  v =

     1   2   4   5   5   6   7   8   9  10


That seems reasonable, doesn't it?

But your example was


  octave:4> v = [1, 1]
  v =

    1  1

  octave:5> v([1, 1, 2])++
  ans =

    1  1  1

  octave:6> v
  v =

    2  2

which doesn't do exactly what you seem to expect.  Does the actual
Octave behavior make sense though?  I think it does, if you think
about it in the following way:

  expr++  <==>  expr += 1  <==>  expr = expr + 1

then what you have is

  v = [1, 1]
  v([1, 1, 2]) = v([1, 1, 2]) + 1
               = [1, 1, 1] + 1
               = [2, 2, 2]

and now the indexed assignment works in order, assigning LHS(i) =
RHS(i) for i = 1 to N.  You can see this behavior clearly by
performing the operation v([1,1,1]) = [1, 2, 3].

FWIW, I'm not sure that this should be considered guaranteed behavior
or just a quirk of the current implementation.

| OK, so maybe I am a sick puppy.  But this is what I am really after.
|
| > [i,j,nz] = find(M);
| > (y=zeros(max(i),1))(i) += nz.*x(j)
| and have
|  y = M*x

I guess I'm dense, but I don't understand what the purpose of this is,
or how it is equivalent to y = M*x.

| I have a lot of sparse matrices I want to play with and this sort of
| syntax would rock.

Are you using an actual sparse matrix data type or trying to emulate
them using full storage (but trying to avoid operations on zeros)?  If
the latter, wouldn't it make more sense to work on a sparse matrix
data type instead?

| Is there a conceptual reason why I should not try to modify octave to
| do this (i.e. how does this break the .m language as a language)?
|
| Is there a technical reason why I should not try to modify octave to
| do this (i.e. requires a total interpreter rewrite, I'd be in over my
| head)?

Seems to me that it is somewhat difficult to pin down exactly what
these operations should do, and then arrange for everyone to agree.

jwe


Reply | Threaded
Open this post in threaded view
|

Re: proper lvalues and whatnot

Ross A. Lippert
"John W. Eaton" wrote:
> Hmm.  Because the result of an assingment is not an lvalue, even in C?
You know, I'm a bonehead sometimes.  I could have sworn C let
you do (x=y)=z in addition to x=(y=z).  You are right, thanks.
I should also take it back that this would be a desirable feature
in octave for what I'm doing.

>   octave:1> v = 1:10
>   v =
>      1   2   3   4   5   6   7   8   9  10
>   octave:2> v([3, 4])++
>   ans =
>     3  4
>   octave:3> v
>   v =
>      1   2   4   5   5   6   7   8   9  10
> That seems reasonable, doesn't it?
Yes, that seems totally reasonable.

> But your example was
>   octave:4> v = [1, 1]
>   v =
>     1  1
>   octave:5> v([1, 1, 2])++
>   ans =
>     1  1  1
>   octave:6> v
>   v =
>     2  2
>
> which doesn't do exactly what you seem to expect.  Does the actual
> Octave behavior make sense though?  I think it does, if you think
> about it in the following way:
YEs the behavior does make sense.  I dipped into the ov.cc code
and found that an assign_op gets implemented by doing a bin_op
followed by a simple_assign, and it is the simple_assign which does
the subscripting.

To me, and maybe not to anyone else, what seems mmore logical is
v(I) += x(I) should be implemented as
for i=1:length(I), v(I(i)) += x(I(i)); end

> | OK, so maybe I am a sick puppy.  But this is what I am really after.
> |
> | > [i,j,nz] = find(M);
> | > (y=zeros(max(i),1))(i) += nz.*x(j)
> | and have
> |  y = M*x
>
> I guess I'm dense, but I don't understand what the purpose of this is,
> or how it is equivalent to y = M*x.
Actually, I don't need to assignment as lvalue thing bc I could just
do the following

octave> zeros(max(i),1)(i) += nz.*x(j)
ans = M*x

The purpose of this is that if M has only a few nonzero entries then
one could matrix multiply by
tmp = zeros(max(i),1);
for p=1:length(i), tmp(i(p)) += nz(p)*x(j(p)); end
If lvalue subscripting had the do-loop interpretation that I'd like,
then the above loop could be replaced with that += expression.

I think that vectors and matrices exist as types in order to
cut down on the amount of explicit looping required to express common
things.  I think the interpretation I have for indexing an lvalue has
a do-loop interpretation which could be useful for doing common
operations.

> Are you using an actual sparse matrix data type or trying to emulate
> them using full storage (but trying to avoid operations on zeros)?  If
> the latter, wouldn't it make more sense to work on a sparse matrix
> data type instead?
That latter.  The reason I think a sparse matrix type would be a bad
idea
is
1) different sparse matrix formats are used by different people for
different reasons and no one would be happy.
2) constructs exist (almost) in octave which allow me to do what I need
to do without having to really bend over that far backwards.
3) I think that giving a specific behavior subscripted lvalues would
be useful not just to sparse matrix computations but for other purposes.
That just happens to be the place where I currently find myself in the
need.


> Seems to me that it is somewhat difficult to pin down exactly what
> these operations should do, and then arrange for everyone to agree.
I agree.  I'd like for someone to tell me if they have something
essential
that depends on a specified looping behavior for subscripted lvalues.
Or
perhaps to point out just how this hasn't been well thought out.


I have noticed that the ov.cc file contains a readme for its assign_op
function suggesting that someone turn it into more than just a bin_op
simple_assign wrapper.  This indicates that there is some need for a
rewrite anyhow.  I'd volunteer to try if no one cries out that my
looping
idea would be a really bad one.



-r


Reply | Threaded
Open this post in threaded view
|

Re: proper lvalues and whatnot

John W. Eaton-6
On 18-Aug-2000, Ross A. Lippert <[hidden email]> wrote:

| To me, and maybe not to anyone else, what seems mmore logical is
| v(I) += x(I) should be implemented as
| for i=1:length(I), v(I(i)) += x(I(i)); end

That might be nice in some instances, but I think Octave must behave
as though

  a(idx) += b

and

  a(idx) = a(idx) + b

are equivalent.  I'm afraid that the behavior of the second statement
is not equivalent to your loop if idx has repeated values.

jwe