R
robert maas, see http://tinyurl.com/uh3t
From: Chris Dollin said:Actually no. The idea was that an lvalue was the /value/ that was
obtained by evaluating an expression which was to be assigned to,
No wonder everyone is confused. In the statement:
x = 5*w;
x is not a value of any kind, it's a **name** of a **variable**
which probably has a value but the value most certainly isn't x. If
w has the value 3, then after executing that statement x will have
the value 15. 15 isn't an lvalue, is it? But you say it is!!
Part of the reason for introducing this distinction was to
formalise why the variable `a` in `a := a + 1` means two
different things in the two different places: the left-hand
`a` is evaluated for its lvalue and the right-hand one for
its rvalue.
That's bullshit. 'a' is the *name* of a *place* where data can be
stored and later retrieved. Depending on where a place is specified
in a statement, either the retrieval-from-place or storage-into-place
operation may occur. Some expressions denote a place, such as 'a'
in the above example, or chs[2] in the following example:
char chs[30];
chs[2] = 'a' + 5;
printf("The third character is '%c'.\n", chs[2]);
Some expressions don't denote a place, such as "'a' + 5" in the
above example. Such expressions can be used only to produce a value
*from* the expression, not to store a value gotten from elsewhere
*into* the expression.
There are basically three kinds of these expressions:
- Readonly, such as input channels (stdin), and 'a' + 5
- Writeonly, such as output channels (stdout, errout)
- Read/Write **place**s, as discussed above
Well I guess there's a fourth kind, something where you can read
and write but what you read isn't what you wrote there previously.
For example, it's possible in some langauges to define an
input/output channel as a single object, for example stdin/stdout
together, which is a useful concept if you want to perform
rubout/backspace processing of input in a user-friendly way.
In some languages, literals have lvalues, so the assignment
`1 := 2` is legal. Depending on the language semantics, `1`
may have a single lvalue, or a different one each time it
is evaluated. (The rvalue of `1` might or might not use its
lvalue.) While for assignment this looks like the rabid
and hungry sabre-toothed tiger, it makes more sense for
parameter-passing ...
I fail to see how it makes any sense at all. In Fortran on the IBM
1620, you could in fact do that. For example (forgive me if I don't
have the syntax exactly correct after 40 years):
SUBROUTINE MANGLE(N)
N = 5;
END
CALL MANGLE(3)
GOTO 3
5 ... (it goes to here, because the literal 3 in the table of
constants, used by the GOTO statement, has been mangled to have the
value 5 now, but the symbol table mapping line numbers to locations
in the compiled code still links 5 to this statement)
I got bitten by that at least once, a really difficult bug to diagnose.
the lvalue is the value you get /by evaluating on the left/; you
may then be able to store into (through?) it, or not.
That's a completely garbled way of thinking of it. To store a value
into a place you need to know what function to call to effect the
storage. Common Lisp clarifies the whole idea best with SETF.
There's a function CAR which returns the left side of a pair, and
the function RPLACA which stores a value in the left side of a
pair, leaving the right side unchanged. You can say (setf (car x)
y), i.e. in c notation car(x) = y. How does this work?? There's a
SETF method for CAR, whereby (setf (car x) y) is macro-expanded
into (rplaca x y).
Now consider CADDR, which is defined such that (CADDR x) means (CAR
(CDR (CDR x))). Now suppose you want to say (setf (caddr x) y).
What does that do? How does it work? There's a SETF method that
causes (setf (caddr x) y) to macro-expand to (rplaca (cddr x) y).
The important point is that it's not enough to evaluate the left
side of the assignment to see where the cell is that needs
modifying, you must also say what function to call to modify just
part of that cell, not the the whole thing. If you say (setf (cddr
x) y), it expands into (rplacd (cdr x) y), i.e. it goes as deep as
(cdr x) to get the object that needs modifying, but the it does
RPLACD instead of RPLACA of that object. It's not enough to say
where to make the change, you must say how specifically to make the
change there, whether to modify the left side or the right side.
You can go even smaller than the byte level of access. For example,
suppose you want to store a large number of ENUMs, where each ENUM
has four possible values 0,1,2,3, thus requiring only two bits
each, so you want to pack four ENUMs into each 8-bit byte, and have
a huge array of millions of these four-to-a-byte structures, and
you want to emulate a huge array that directly indexes the
individual ENUMs. It's easy in Common Lisp: First you define a
function for reading out the individual ENUM at location IX,
something like this (I'm using c notation here to make it easier
for you to understand):
ENUM four getfour(unsiged byte *arr, int ix) {
int arrix, subix, nshift;
arrix = ix/4;
subix = ix%4;
nshift = 2*subix;
return (ENUM four)((arr[arrix] >> nshift) & 3);
}
Now you define a function for storing an ENUM into the same place
you got it from, something like this:
void putfour(unsiged byte *arr, int ix, ENUM four newval) {
int arrix, subix, nshift;
arrix = ix/4;
subix = ix%4;
nshift = 2*subix;
arr[arrix] = (arr[arrix] & (~ (3 << nshift)))
| (((unsiged byte)newval) << nshift);
}
Now (what you can do *only* in Common Lisp), you define a SETF
method so that the code:
getfour(bigarr,ix) = newval;
will macro-expand into:
putfour(bigarr,ix,newval);
That makes it easy to copy a ENUM from one place to another,
for example:
getfour(bigarr,ix1) = getfour(bigarr,ix2);
exactly like you can already (in c) do:
arr[ix1] = arr[ix2];
so you don't have to write this ugly assymtric code instead:
putfour(bigarr,ix1,getfour(bigarr,ix2));
putarr(arr,ix1,arr[ix2]);
In summary, there's no value (usefulness) to an "lvalue" as you explain it.
What is misnomered an "lvalue" is really c's version of a setf method,
which unfortunately can't be extended by users as it can in lisp.