The logic of (newLisp) list indexing

Started by pjot, December 28, 2007, 02:04:43 PM

Previous topic - Next topic

pjot

Hi,



Here I am again with an annoying question :-)



First a nice quote.


Quote
peter[~]$ newlisp

newLISP v.9.2.11 on Linux, execute 'newlisp -h' for more info.



> (set 'example '("a" "b" "c" "d" "e"))

("a" "b" "c" "d" "e")

>

> (length example)

5

> (nth 4 example)

"e"

> (nth 5 example)

"e"

> (nth 6 example)

"e"

> (nth 0 example)

"a"

> (nth -1 example)

"e"

> (nth -2 example)

"d"


This is all according to documentation, so far so good. I am not using implicit indexing here, since I want to illustrate my question with a clear example.



My question relates to the logic of indexing. If the index grows, and gets larger than the the amount of elements, the last element is returned. No matter what number higher than the amount of elements, the last element is returned.



However, if the index gets smaller, even smaller than 0, not the first element is returned, but the indexing wraps around and all of a sudden returns the last element of the list again.



In my point of view it would be more consequent to return the first element of a list if the index gets smaller than 0. Or the other way around: if the index gets higher than the total amount of elements in a list, also wrap around to return the first element (better idea).



So in a fake session:
Quote
peter[~]$ newlisp

newLISP v.9.2.11 on Linux, execute 'newlisp -h' for more info.



> (set 'example '("a" "b" "c" "d" "e"))

("a" "b" "c" "d" "e")

>

> (length example)

5

> (nth 4 example)

"e"

> (nth 5 example)

"a"

> (nth 6 example)

"b"

> (nth 7 example)

"c"

> (nth 0 example)

"a"

> (nth -1 example)

"e"

> (nth -2 example)

"d"

> (nth -3 example)

"c"




What is the reason for the current logic of list indexing? To me it makes kind of an inconsistent impression :-) ...but probably I am missing a point...????



Cheers

Peter

m i c h a e l

#1
Hi Peter!



Like newLISP, negative indices are used in a number of languages (Python, Ruby, etc.) to select from the end of the list . So -1 is the last element, -2 the penultimate (next to last) and so on.



m i c h a e l

pjot

#2
Michael!



Thanks for your quick reply!



But... How do these languages handle positive indexes?? I mean indexes higher than the amount of elements in a list?



Peter

m i c h a e l

#3
Peter,



Python gives an error:


>>> [1,2][2]
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
IndexError: list index out of range
>>>


and Ruby returns nil:


>> [1,2].at(2)
=> nil
>>


I think newLISP is unique in returning the last element for indices greater than the length of the list.



m i c h a e l

pjot

#4
Quote
I think newLISP is unique in returning the last element for indices greater than the length of the list.


Unique but not consistent. :-)



Of course newLisp is the best programming language on earth, but I am still curious why this decision was made.



If an index is counting backwards it wraps around a list, so why is this not the case when counting forward?



Maybe I have been drinking too much ;-) ..it is my second http://www.globalbeer.com/body_pages/pages-beer/GuldenDraak/GuldenDraak.html">Golden Dragon already!



Cheers

Peter

newBert

#5
Quote from: "pjot"
Quote
I think newLISP is unique in returning the last element for indices greater than the length of the list.


Unique but not consistent. :-)



Of course newLisp is the best programming language on earth, but I am still curious why this decision was made.



If an index is counting backwards it wraps around a list, so why is this not the case when counting forward?



Maybe I have been drinking too much ;-) ..it is my second http://www.globalbeer.com/body_pages/pages-beer/GuldenDraak/GuldenDraak.html">Golden Dragon already!



Cheers

Peter

In cervisia veritas !

;o)



In fact, I agree with you, about your remark as regards indexing and about your good taste for belgian beer.
<r><I>>Bertrand<e></e></I> − <COLOR color=\"#808080\">><B>newLISP<e></e></B> v.10.7.6 64-bit <B>>on Linux<e></e></B> (<I>>Linux Mint 20.1<e></e></I>)<e></e></COLOR></r>

newdep

#6
You could indeed speculate if returning a 'nil is 'better' because

it indicates an out-of-bounds in the list index.  Thought below are

all counters on the list.. A 'last or -1 is always indicating the last value.

a 1 or first always the first.. Using Loops like dolist or dotimes you

have the $idx value..



So actualy you only would need a 'nil if you would use it for indication..



(  '(1 2 3) 5) -> 3

(  '(1 2 3) -4) -> 1

( 3 '( 1 2 3) -> ()



(nth -5 '( 1 2 3)) -> 1

(nth 5 '( 1 2 3)) -> 3
-- (define? (Cornflakes))

Lutz

#7
We could change the behavior to be consistent with arrays, and throw an error message when indices are overrun, it would be fine with me, but I don't know how people feel about it, and would like to get more opinions on this matter. The code change would be trivial.



Are there people out there relying on bigger indices picking always the last (or the first) element?



Lutz

pjot

#8
Actually I'ld rather have a wrap-around behaviour, similar when index couting backwards in a list.



Like this:


Quote
> (set 'example '("a" "b" "c" "d" "e"))

("a" "b" "c" "d" "e")

>

> (length example)

5

> (nth 4 example)

"e"

> (nth 5 example)

"a"

> (nth 6 example)

"b"

> (nth 7 example)

"c"


Peter

pjot

#9
By the way, how is this possible:
Quote
peter[~]$ newlisp

newLISP v.9.2.11 on Linux, execute 'newlisp -h' for more info.



> (set 'L '("1" "2" "3"))

("1" "2" "3")

> (nth -5 L)

"1"

> (nth -4 L)

"1"

> (nth -3 L)

"1"

> (set 'example '("a" "b" "c" "d" "e"))

("a" "b" "c" "d" "e")

> (nth 0 example)

"a"

> (nth -1 example)

"e"

> (nth -2 example)

"d"


With the list 'L' a negative number returns the first item, with the list 'example' it wraps around. And yet, they are the same type of lists.



Peter

Lutz

#10
Its not wrapping around, negative indices go from the right to the left and stick to the first element when overshooting.



Nothing inconsistent, here.



Again, I can change to throw and error when indices overshoot the end (too high positive index) or the beginning (to high negative index. It would then behave like arrays, which also work with negative indices.



Lutz

cormullion

#11
I have mixed feelings!



I think the idea of wrapping round is interesting. The idea of flagging an out of range access with nil is also interesting, and slightly better in a 'formal' type of way.



Presumably the motivation behind the current behaviour is a "don't throw an error just because you can" way of thinking which I sometimes think I see in the fabric of newLISP. newLISP is friendly enough to new users to overlook your minor slip-ups... i agree that changing it would probably be considered to be the 'right' thing to do though... And if I have code that breaks with the change perhaps it should be my code that gets tighter...

pjot

#12
OK now I see. There is no wrapping around in the first place and there has never been.



Negative indices just start counting at the end. This has the appearance of wrapping around a list.



Hm... in that case, for me it does not matter, as returning nil or the first element will not resolve my programming issues....



Thanks for explaining though!



Greetings

Peter

Jeremy Dunn

#13
Just to raise a further can of worms, why should indexing begin with 0 in the positive direction and -1 in the negative? Wouldn't it be more proper to have 1,-1 or 0,-0 to be consistent? About the only thing I like about the VB language is that it gives you the option to set your indexing to 0 or 1 as you please. Almost every language seems to want to preserve this 0,-1 nonsense simply because it is tradition to have bad human factors design. I would like to be able to write something like



(set-indexing 1)



to do like VB and have the choice of indexing. As for the original issue of whether an error should be thrown if the index exceeds the list count I think it should because it does forgive error a little bit too much. In a small program it is not such a big deal but in a large one it can be devil to find sometimes. It is a nice thing to have a function that needs a positive integer perform absolute value on the input but that does not seem to be as critical as knowing where you really are in a list.

pjot

#14
On second thought, the concept of wrapping around a list is still interesting. Though currently not implemented at all, it still could serve a lot of problematic situations.



In fact, each time when elements in a list need to be repeated, it will save a lot of code. Right now, a check must be created to see if the end of the list has been reached. I know, there is a function like (dolist) but what if the index variable is set in one function and the element is fetched in another function? To make this clear with a simplified example:

(define (counter)
    (inc 'nr)
    (if (> nr 5) (set 'nr 0))
    nr
)

(define (display, t)
    (set 't (counter))
    (println (mylist nr))
)


The (if) can be avoided when the index wraps around a list. This is a simple example, but if you need to go forward and backward in a list the checks and also the passing of variables can make things complicated and ugly....



Anyway, it is just an idea.



Cheers

Peter



(PS Jeremy: also good idea, it is the thing I like also in VB myself!)