June 14, 2005
"Stewart Gordon" <smjg_1998@yahoo.com> wrote in message news:d8mved$1a1b$3@digitaldaemon.com...
> Ben Hinkle wrote:
>
>>> I reckon the only way to test for empty is:
>>>
>>> import std.stdio;
>>>
>>> template isEmpty(ArrayType) {
>>> bool isEmpty(ArrayType[] arr)
>>> {
>>> return (arr.length == 0 && arr.ptr != null);
>>> }
>>> }
>>
>> So an array with null ptr and 0 length is non-empty? That doesn't seem intuitive.
>
> No, because there's no array here.  There are three distinct cases to
> distinguish:
> - null array reference
> - empty array
> - non-empty array
>
> The code above is to test specifically for the empty array case, where a null array reference might have different semantics in the context.

I understand your definitions. IMO any isEmpty function should return true for an input that has a null ptr and zero length (I've avoided using the terms "array" or "empty" in that sentence to be absolutely clear). Or to put it another way "if a foreach does nothing then it is empty". The function above I would call isNonNullAndEmpty or something.

[snip]
>>  if (!str) {...}
>> is equivalent to
>>  if (str.length != 0) {...}
>> instead of being equivalent to
>>  if (str.ptr != null) {...}
>> as happens today.
>
> Huh? So that non-empty is false and empty or null is true? Sounds very counter-intuitive.
>
> From the bit of the docs I quoted, I would've expected
>
>     if (str) { ... }
>
> to be equivalent to
>
>     if (str.length != 0) { ... }

yeah - you're right. I wasn't paying close enough attention. sorry about that.


June 14, 2005
On Tue, 14 Jun 2005 14:12:23 -0400, Ben Hinkle wrote:

> IMO any isEmpty function should return true for an input that has a null ptr and zero length.

Or yet other words, for an array to be empty it must first be an array
(i.e. arr.ptr != 0), and the length must be zero (i.e. arr.length == 0).

There are four conditions:

 .ptr == 0   .len == 0
 .ptr == 0   .len != 0
 .ptr != 0   .len == 0
 .ptr != 0   .len != 0

By my reckoning, if the .ptr == 0 then the entity is not an array and so these two conditions should both return NULL. It the .ptr != 0 then the array exists and it either has zero or more elements, so if .len == 0 then it is EMPTY otherwise it is neither empty nor null. The apparent ambiguous situation of both .len and .ptr being zero is the problem area. I would pedantically call it null because there is no array.

This problem exists mainly because D sets .ptr to zero whenever the .len is set to zero, and that is not a good idea because we loose information when that happens. If I want to set the .ptr to zero I would have preferred that 'arr = null' or 'arr.ptr = 0' would set both to zero, and 'arr.length = 0' just sets the current length to zero but retains the RAM allocation.

In my code, I always use 'if (arr.length == 0)' to test for an empty array and never use 'if (!arr)'. I've been bitten by the lack of distinction between an empty array and a null array too. And by the way, D isn't consistent when it nullifies an array either, in that some situations 'string = ""' does *not* set .ptr to zero and other situations it does.

-- 
Derek Parnell
Melbourne, Australia
15/06/2005 6:36:43 AM
June 14, 2005
On Tue, 14 Jun 2005 10:31:39 -0400, Ben Hinkle <bhinkle@mathworks.com> wrote:

>> I reckon the only way to test for empty is:
>>
>> import std.stdio;
>>
>> template isEmpty(ArrayType) {
>> bool isEmpty(ArrayType[] arr)
>> {
>> return (arr.length == 0 && arr.ptr != null);
>> }
>> }
>
> So an array with null ptr and 0 length is non-empty? That doesn't seem
> intuitive.

Maybe, maybe not. Think about it this way, is a non existant glass and empty glass? I don't think you'll get too many people saying yes to that. A prerequisite to being "empty" is for the thing to exist at all. If it doesn't exist it cannot be empty.

Sure, in some cases you want to treat the empty and non-existant case as the same thing, you can, using .length. Sometimes you want to treat them differently, we can, using "a is null".

However, and this is the problem, D converts from one to the other when you set length to 0, and that is wrong.

> I think the ptr is irrelevant when testing if the array has any
> elements in it or not.

You're correct, but this is irrelevant. I'm not simply asking whether "the array has any elements in it". I'm first asking whether it exists or not, and then if it's got any elements. Thus the double check.

> In fact one could argue that Walter should change the
> implicit conversion of an array from returning the ptr to returning the
> length so that the test
>  if (!str) {...}
> is equivalent to
>  if (str.length != 0) {...}
> instead of being equivalent to
>  if (str.ptr != null) {...}
> as happens today.

We could, but in my mind that would be counter intuitive. "if (a)" tests a class references, pointers and intrinsics against null, or 0, if we change arrays we now have different behaviour for them as compared to everything else.

Regan
June 14, 2005
On Tue, 14 Jun 2005 14:12:23 -0400, Ben Hinkle <bhinkle@mathworks.com> wrote:
> "Stewart Gordon" <smjg_1998@yahoo.com> wrote in message
> news:d8mved$1a1b$3@digitaldaemon.com...
>> Ben Hinkle wrote:
>>
>>>> I reckon the only way to test for empty is:
>>>>
>>>> import std.stdio;
>>>>
>>>> template isEmpty(ArrayType) {
>>>> bool isEmpty(ArrayType[] arr)
>>>> {
>>>> return (arr.length == 0 && arr.ptr != null);
>>>> }
>>>> }
>>>
>>> So an array with null ptr and 0 length is non-empty? That doesn't seem
>>> intuitive.
>>
>> No, because there's no array here.  There are three distinct cases to
>> distinguish:
>> - null array reference
>> - empty array
>> - non-empty array
>>
>> The code above is to test specifically for the empty array case, where a
>> null array reference might have different semantics in the context.
>
> I understand your definitions. IMO any isEmpty function should return true
> for an input that has a null ptr and zero length (I've avoided using the
> terms "array" or "empty" in that sentence to be absolutely clear). Or to put
> it another way "if a foreach does nothing then it is empty".

I don't think that's a valid definition (due to my definition of empty, which I'll explain below). I think all you can state is "if a foreach does nothing then there are no elements". Using the glass example again:

"is a non existant glass empty?"

Logically, at least to me, the answer is "no".

Remember, the existance of the object is important, by only asking whether it contains anything you're ignoring whether it exists or not, quite simply a non-existant glass and an empty glass hold the exact same amount of liquid, none.

> The function
> above I would call isNonNullAndEmpty or something.

I wouldn't, because to me "empty" implies "existance" thus "non null".

Regan
June 14, 2005
"Derek Parnell" <derek@psych.ward> wrote in message news:2o7hnscxa0hj$.s1owhetd92i0$.dlg@40tude.net...
> This problem exists mainly because D sets .ptr to zero whenever the .len
is
> set to zero,

No, it doesn't. The reason it doesn't is to support the 'reserve some space in advance' idiom:

    array.length = 100;
    array.length = 0;

Now, array.ptr points to the start of the buffer that is at least 100 long. Then, the array can be appended to without undergoing reallocation.

> and that is not a good idea because we loose information when
> that happens. If I want to set the .ptr to zero I would have preferred
that
> 'arr = null'

That does set both to 0.

> or 'arr.ptr = 0' would set both to zero,

Having .length!=0 and .ptr==0 is not a legal configuration for an array.

> and 'arr.length = 0'
> just sets the current length to zero but retains the RAM allocation.

That's what it does now.

> In my code, I always use 'if (arr.length == 0)' to test for an empty array and never use 'if (!arr)'. I've been bitten by the lack of distinction between an empty array and a null array too. And by the way, D isn't consistent when it nullifies an array either, in that some situations 'string = ""' does *not* set .ptr to zero and other situations it does.

To test for an empty or null array, test for (array.length == 0). To test
for a null array, test for (array is null).


June 14, 2005
I agree. I'll fix the doc.


June 14, 2005
On Tue, 14 Jun 2005 15:14:08 -0700, Walter <newshound@digitalmars.com> wrote:
> "Derek Parnell" <derek@psych.ward> wrote in message
> news:2o7hnscxa0hj$.s1owhetd92i0$.dlg@40tude.net...
>> This problem exists mainly because D sets .ptr to zero whenever the .len
> is
>> set to zero,
>
> No, it doesn't. The reason it doesn't is to support the 'reserve some space
> in advance' idiom:
>
>     array.length = 100;
>     array.length = 0;
>
> Now, array.ptr points to the start of the buffer that is at least 100 long.
> Then, the array can be appended to without undergoing reallocation.

The please explain these results:

import std.stdio;

template isEmpty(ArrayType) {
	bool isEmpty(ArrayType[] arr)
	{
		return (arr.length == 0 && arr.ptr != null);
	}
}

int main(char [][] args)
{
	char[] string;

	writefln("Setting string to null:");
	string = null;
	writefln("string is ",(isEmpty!(char)(string))?"empty":"not empty");

	if (string is null) writefln("and, string is null");
	if (string == null) writefln("and, string == null");
	if (string == "") writefln("and, string == \"\"");

	writefln("");
	writefln("Setting string to \"\":");
	string = "";
	writefln("string is ",(isEmpty!(char)(string))?"empty":"not empty");

	if (string is null) writefln("and, string is null");
	if (string == null) writefln("and, string == null");
	if (string == "") writefln("and, string == \"\"");

	writefln("");
	writefln("Setting length to zero:");
	string.length = 0;
	writefln("string is ",(isEmpty!(char)(string))?"empty":"not empty");

	if (string is null) writefln("and, string is null");
	if (string == null) writefln("and, string == null");
	if (string == "") writefln("and, string == \"\"");

	return 0;
}

Setting string to null:
string is not empty
and, string is null
and, string == null
and, string == ""

Setting string to "":
string is empty
and, string == null
and, string == ""

Setting length to zero:
string is not empty
and, string is null
and, string == null
and, string == ""

Regan
June 14, 2005
"Walter" <newshound@digitalmars.com> wrote in message news:d8nl8j$20dp$1@digitaldaemon.com...
>
> "Derek Parnell" <derek@psych.ward> wrote in message news:2o7hnscxa0hj$.s1owhetd92i0$.dlg@40tude.net...
>> This problem exists mainly because D sets .ptr to zero whenever the .len is set to zero,
>
> No, it doesn't. The reason it doesn't is to support the 'reserve some
> space
> in advance' idiom:
>
>    array.length = 100;
>    array.length = 0;
>
> Now, array.ptr points to the start of the buffer that is at least 100
> long.
> Then, the array can be appended to without undergoing reallocation.

Derek is correct. _d_setarraylength in internal/gc/gc.d says

    if (newlength)
    {
    [snip]
    }
    else
    {
 newdata = null;
    }

    p.data = newdata;
    p.length = newlength;

so setting length to 0 nulls the ptr. Similarly setting from from 0 to any
non-zero value ignores the ptr and always allocates a new memory block.
To see it in action try
int main() {
  int[] x = new int[10];
  printf("%p\n",x.ptr);
  x.length = 0;
  printf("%p\n",x.ptr);
  x = new int[10];
  x = x[0 .. 0]; // doesn't reset the ptr
  printf("%p\n",x.ptr);
  x.length = 10; // will make a new allocation
  printf("%p\n",x.ptr);
  return 0;
}


June 14, 2005
On Tue, 14 Jun 2005 15:14:08 -0700, Walter wrote:

> "Derek Parnell" <derek@psych.ward> wrote in message news:2o7hnscxa0hj$.s1owhetd92i0$.dlg@40tude.net...
>> This problem exists mainly because D sets .ptr to zero whenever the .len
> is
>> set to zero,
> 
> No, it doesn't. The reason it doesn't is to support the 'reserve some space in advance' idiom:
> 
>     array.length = 100;
>     array.length = 0;
> 
> Now, array.ptr points to the start of the buffer that is at least 100 long. Then, the array can be appended to without undergoing reallocation.

Hmmm... then the code below produces unexpected results ...

<code>
import std.stdio;

void func(int w, char[] x, int y, bool z)
{
    char[] ok;
    char[] addr;
    char[] emp;
    char[] nul;

    ok = "Good";
    if (x.length != y)
        ok = "Bad1";
    if (z && x.ptr == null)
        ok = "Bad2";
    if (!z && x.ptr != null)
        ok = "Bad3";

    if (z)
        addr = "non-zero";
    else
        addr = "       0";

    emp = (x.length == 0 ? " empty" : "!empty");
    nul = (x is null ? " null"  : "!null");

    writefln("Test# %d: Actual Len=%d Addr=%8x\n"
             "       Expected Len=%d Addr=%s  (%s) %s %s\n",
                w, x.length, cast(ulong)x.ptr, y, addr, ok, emp, nul);
}

void main()
{
    char[] b;


    func(1, "abc", 3, true);
    func(2, "", 0, true);
    func(3, "abc".dup, 3, true);
    func(4, "".dup, 0, true);
    func(5, b, 0, false);
    b = "qwerty".dup;
    func(6, b, 6, true);
    b.length = 0;
    func(7, b, 0, true);
    b = null;
    func(8, b, 0, false);

    b = "poiuyt".dup;
    writefln("Addr is %8x with %d elements", cast(ulong)b.ptr, b.length);
    b.length = 0;
    writefln("Addr is %8x with %d elements", cast(ulong)b.ptr, b.length);
    b.length = 100;
    writefln("Addr is %8x with %d elements", cast(ulong)b.ptr, b.length);
    b.length = 0;
    writefln("Addr is %8x with %d elements", cast(ulong)b.ptr, b.length);

}
</code>

<results using v0.126>
Test# 1: Actual Len=3 Addr=  413188
       Expected Len=3 Addr=non-zero  (Good) !empty !null

Test# 2: Actual Len=0 Addr=  413198
       Expected Len=0 Addr=non-zero  (Good)  empty !null

Test# 3: Actual Len=3 Addr=  870fe0
       Expected Len=3 Addr=non-zero  (Good) !empty !null

Test# 4: Actual Len=0 Addr=       0
       Expected Len=0 Addr=non-zero  (Bad2)  empty  null

Test# 5: Actual Len=0 Addr=       0
       Expected Len=0 Addr=       0  (Good)  empty  null

Test# 6: Actual Len=6 Addr=  870fd0
       Expected Len=6 Addr=non-zero  (Good) !empty !null

Test# 7: Actual Len=0 Addr=       0
       Expected Len=0 Addr=non-zero  (Bad2)  empty  null

Test# 8: Actual Len=0 Addr=       0
       Expected Len=0 Addr=       0  (Good)  empty  null

Addr is   870fc0 with 6 elements
Addr is        0 with 0 elements
Addr is   872f00 with 100 elements
Addr is        0 with 0 elements
</results>

If what you is so, then tests #4, and #7 don't make sense to me. And the final tests confirm the anomaly.

>> and that is not a good idea because we loose information when
>> that happens. If I want to set the .ptr to zero I would have preferred
> that
>> 'arr = null'
> 
> That does set both to 0.

Good.

>> or 'arr.ptr = 0' would set both to zero,
> 
> Having .length!=0 and .ptr==0 is not a legal configuration for an array.

Agreed, and that's why whenever the pointer is set to zero then length should be too.

>> and 'arr.length = 0'
>> just sets the current length to zero but retains the RAM allocation.
> 
> That's what it does now.

See code example above which seems to say otherwise.

>> In my code, I always use 'if (arr.length == 0)' to test for an empty array and never use 'if (!arr)'. I've been bitten by the lack of distinction between an empty array and a null array too. And by the way, D isn't consistent when it nullifies an array either, in that some situations 'string = ""' does *not* set .ptr to zero and other situations it does.
> 
> To test for an empty or null array, test for (array.length == 0). To test
> for a null array, test for (array is null).

Walter, these are direct questions: Do you believe there is a distinction between an empty array and a null array? And if so, will D support the two concepts in a consistent manner?

-- 
Derek
Melbourne, Australia
15/06/2005 8:39:31 AM
June 14, 2005
Regan Heath wrote:
> On Tue, 14 Jun 2005 10:31:39 -0400, Ben Hinkle <bhinkle@mathworks.com>  wrote:
>> In fact one could argue that Walter should change the
>> implicit conversion of an array from returning the ptr to returning the
>> length so that the test
>>  if (!str) {...}
>> is equivalent to
>>  if (str.length != 0) {...}
>> instead of being equivalent to
>>  if (str.ptr != null) {...}
>> as happens today.
> 
> 
> We could, but in my mind that would be counter intuitive. "if (a)" tests a  class references, pointers and intrinsics against null, or 0, if we change  arrays we now have different behaviour for them as compared to everything  else.

Yeah, but we already have this kind of 'inconsistency':

# struct Foo
# {
# 	bool a = true;
# }
#
# void main()
# {
# 	Foo f;
# 	if (f) {}
# }

Yields: 'expression f of type Foo does not have a boolean value'. I prefer to percieve D's arrays as structs, so having to check their members: 'ptr' and 'length' would be pretty logical, consistent and less error prone than the current approach IHMO.



-- 
Tomasz Stachowiak  /+ a.k.a. h3r3tic +/