Page 1 of 1
slice with negative "int-length"
Posted: Tue Apr 23, 2019 8:46 am
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.
Re: slice with negative "int-length"
Posted: Tue Apr 23, 2019 1:24 pm
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.
Re: slice with negative "int-length"
Posted: Tue Apr 23, 2019 2:09 pm
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.
Re: slice with negative "int-length"
Posted: Wed Apr 24, 2019 2:06 pm
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) ;;-> ""
Re: slice with negative "int-length"
Posted: Wed Apr 24, 2019 8:56 pm
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)))))
Re: slice with negative "int-length"
Posted: Thu Apr 25, 2019 8:00 am
by lyl
@rickyboy Really really appreciate your works on my post and your valueable suggestions!!