slice with negative "int-length"

Q&A's, tips, howto's
Locked
lyl
Posts: 44
Joined: Sun Mar 25, 2018 5:00 am

slice with negative "int-length"

Post by lyl »

I don't quite understand how the function slice works with negative "int-length".
The following example comes from the manual:

Code: Select all

(slice '(a b c d e f) 2 -2)  → (c d)
As explained in manual:
If int-length is negative, slice will take the parameter as offset counting from the end and copy up to that offset
As my view, -2 seems to point to "e"(-1 points to "f"), so why is not the result (c d e)?

And how to design a function like this:

Code: Select all

(myslice '(0 1 2 3 4 5) 2 -2) ->(1 2)
(myslice '(0 1 2 3 4 5) 4 -3) ->(2 3 4)
(myslice '(0 1 2 3 4 5) 4 -1) ->(4)
(myslice "newLISP" 4 -2) ->"LI"
That means the negative symbol will cause myslice select elements in opposite direction.

rickyboy
Posts: 607
Joined: Fri Apr 08, 2005 7:13 pm
Location: Front Royal, Virginia

Re: slice with negative "int-length"

Post by rickyboy »

Maybe it would have been better if the manual had said instead: "If int-length is negative, slice will take the parameter as offset counting from the end and copy up to but not including that offset."

As far as the answer to your second question, I believe that all you need to do is transform the index and length arguments, when the length argument is negative.

Code: Select all

(define (myslice xs idx len)
  (if (< len 0)
      (slice xs (+ 1 idx len) (abs len))
      (slice xs idx len)))
Here's another version with implicit slicing. It's supposed to be faster, but arguably less readable.

Code: Select all

(define (myslice xs idx len)
  (if (< len 0)
      ((+ 1 idx len) (abs len) xs)
      (idx len xs)))
Beware, though, when your calling code exceeds the ends of the string or list argument -- sometimes you get errors, other times it wraps.

Code: Select all

> (myslice "newLISP" 4 -6)
"P"
> (myslice "newLISP" 4 -7)
"SP"
> (myslice "newLISP" 4 -75)

ERR: invalid string index
called from user function myslice
Of course, you can add checks for these conditions to myslice, if you want.
(λx. x x) (λx. x x)

rickyboy
Posts: 607
Joined: Fri Apr 08, 2005 7:13 pm
Location: Front Royal, Virginia

Re: slice with negative "int-length"

Post by rickyboy »

We might want to add one more thing. Notice that we can omit the length argument in slice.

Code: Select all

> (slice '(0 1 2 3 4 5) 3)
(3 4 5)
> (slice '(0 1 2 3 4 5) -3)
(3 4 5)
But not so with our version of myslice (from the previous post).

Code: Select all

> (myslice '(0 1 2 3 4 5) 3)

ERR: value expected in function + : len
called from user function myslice
But that's just a small fix away.

Code: Select all

(define (myslice xs idx (len (if (< idx 0)
                                 (- idx)
                                 (- (length xs) idx))))
  (if (< len 0)
      (slice xs (+ 1 idx len) (abs len))
      (slice xs idx len)))
Check:

Code: Select all

> (myslice '(0 1 2 3 4 5) 3)
(3 4 5)
> (myslice '(0 1 2 3 4 5) -3)
(3 4 5)
Now, let's regression test the negative length code (which is what you were really after) to convince ourselves that this fix didn't break that part of the code.

Code: Select all

> (myslice '(0 1 2 3 4 5) 2 -2)
(1 2)
> (myslice '(0 1 2 3 4 5) 4 -3)
(2 3 4)
> (myslice '(0 1 2 3 4 5) 4 -1)
(4)
> (myslice "newLISP" 4 -2)
"LI"
Seems to be OK.
(λx. x x) (λx. x x)

lyl
Posts: 44
Joined: Sun Mar 25, 2018 5:00 am

Re: slice with negative "int-length"

Post by lyl »

Thank you so much for your help @rickboy!! I learned a lot from you.
Based on your idea, I construct "myslice" as below:

Code: Select all

(define (myslice xs
		         idx
		         (len (if (< idx 0)
			    (- idx)
			    (- (length xs) idx))))
 
  (if (or (>= idx (length xs)) (< idx (- (length xs))))
      (if (list? xs) '()
	  (string? xs) "")

      (begin
	(setq idx (if (>= idx 0) idx (+ idx (length xs))))
	(if (< len 0)
	    (slice xs
		     (let (new-idx (+ 1 idx len)) (if (>= new-idx 0) new-idx 0))
		     (let (abs-len (abs len)) (if (<= abs-len (+ 1 idx)) abs-len (+ 1 idx)))
			   )
	    (slice xs idx len)
	    )
	)
      )
  )

Code: Select all

;; test
(setq a '(0 1 2 3 4 5))
(setq b "Hello World")
(myslice a 2) ;;->(2 3 4 5)
(myslice a 3 2) ;;->(3 4)
(myslice a 3 4) ;;->(3 4 5)
(myslice a 3 -4) ;;->(0 1 2 3)
(myslice a 3 -5) ;;->(0 1 2 3)
(myslice a 6 -2) ;;->()
(myslice a -7 2) ;;->()
(myslice a 8 -2) ;;->()
(myslice a -3 2) ;;(3 4)
(myslice a -3 -3) ;;(1 2 3)
(myslice a -3 -8) ;;(0 1 2 3)
(myslice b 4 3) ;;->"o W"
(myslice b 4 12) ;;->"o World"
(myslice b -4 -6) ;;-> "llo Wo"
(myslice b -4 -18) ;;-> "Hello Wo"
(myslice b 20 6) ;;-> ""

rickyboy
Posts: 607
Joined: Fri Apr 08, 2005 7:13 pm
Location: Front Royal, Virginia

Re: slice with negative "int-length"

Post by rickyboy »

Ah, I see what you did -- you put in the runtime checks on the bounds of the argument values. Very nice!

I'd suggest commenting it a bit. I do this in my own code when I believe there is a chance that it will take me more than 5 seconds to re-parse and understand it when I return to read it in the future -- and I have noticed that my future self always thanks my past self for doing it! :D

Code: Select all

(define (myslice xs idx (len (if (< idx 0)
                                 (- idx)
                                 (- (length xs) idx))))
  (if ;; 1. For degenerate `idx` cases, return "empty", rather than
      ;; signalling an exception.
      (or (>= idx (length xs))
          (< idx (- (length xs))))
      (if (list? xs)   '()
          (string? xs) "")
      ;; 2. Normal `idx` cases
      (let (;; ensure `idx` is in positive form, before we proceed
            ;; further.
            idx (if (>= idx 0)
                    idx
                    (+ idx (length xs))))
        (if (< len 0)
            (slice xs
                   ;; ensure that the index argument is not out of
                   ;; bounds.
                   (let (new-idx (+ 1 idx len))
                     (if (>= new-idx 0) new-idx 0))
                   ;; ensure that the length argument is not out of
                   ;; bounds.
                   (let (abs-len (abs len))
                     (if (<= abs-len (+ 1 idx)) abs-len (+ 1 idx))))
            (slice xs idx len)))))
(λx. x x) (λx. x x)

lyl
Posts: 44
Joined: Sun Mar 25, 2018 5:00 am

Re: slice with negative "int-length"

Post by lyl »

@rickyboy Really really appreciate your works on my post and your valueable suggestions!!

Locked