# Python string indexes

**URL:** <https://racket.discourse.group/t/python-string-indexes/2175>\
**Category:** Questions & Answers\
**Created:** [July 30, 2023, 12:57pm UTC](https://racket.discourse.group/t/python-string-indexes/2175 "2023-07-30T12:57:59Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![matteo-daddio](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/matteo-daddio/32/1245_2.png) [@matteo-daddio](https://racket.discourse.group/u/matteo-daddio)\
**Post date:** [July 30, 2023, 12:57pm UTC](https://racket.discourse.group/t/python-string-indexes/2175/1 "2023-07-30T12:57:59Z")

</div>

Hello,

I learned some Python and I discovered that it uses negative indexes when dealing with strings.

Something like this:

```
(substring “racket.discourse.group” 7 -6)
=> “discourse”

(substring “racket.discourse.group” -5)
=> “group”

```

I think it’s nice. What do you think?

Matteo

---

<div class="post-metadata">

**Author:** ![shawnw](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/shawnw/32/1031_2.png) [@shawnw](https://racket.discourse.group/u/shawnw)\
**Post date:** [July 31, 2023, 12:53am UTC](https://racket.discourse.group/t/python-string-indexes/2175/2 "2023-07-31T00:53:22Z")

</div>

Quite a few languages support negative offsets like that, but not Racket or Scheme (Not even in any of the string SRFIs, I believe). Easy enough to implement, though. Example module:

```scheme
#lang racket/base

(require racket/contract (only-in racket/base [substring rkt:substring]))
(provide
 (contract-out
    (substring (->* (string?) (exact-integer? exact-integer?) string?))))

(define (normalize-ranges s start end)
  (values
   (if (< start 0)
       (+ (string-length s) start)
       start)
   (if (< end 0)
       (+ (string-length s) end)
       end)))

(define (substring s [start 0] [end (string-length s)])
  (let-values ([(start end) (normalize-ranges s start end)])
    (rkt:substring s start end)))

(module+ test
  (require rackunit)
  (check-equal? (substring "racket.discourse.group" 7 -6) "discourse")
  (check-equal? (substring "racket.discourse.group" -5) "group"))

```

* * *

You have smart quotes in your code snippets, btw, which will throw off anyone trying to copy and paste them. Like me for the test cases....

---

<div class="post-metadata">

**Author:** ![gus-massa](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/gus-massa/32/507_2.png) [@gus-massa](https://racket.discourse.group/u/gus-massa)\
**Post date:** [July 31, 2023, 1:14pm UTC](https://racket.discourse.group/t/python-string-indexes/2175/3 "2023-07-31T13:14:56Z")

</div>

I like the general idea, but I don't like the ambiguity of `0`.

```scheme
#lang racket
(require magic/compatibility/phyton)

(define (test s n)
  (python-substring s n -n))

(define S "Hello, Word!")

(test S 2) ; ==> "llo, Wor"
(test S 1) ; ==> "ello, Word"
(test S 0) ; ==> "" <-- !!! :(

```

---

<div class="post-metadata">

**Author:** ![matteo-daddio](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/matteo-daddio/32/1245_2.png) [@matteo-daddio](https://racket.discourse.group/u/matteo-daddio)\
**Post date:** [July 31, 2023, 5:48pm UTC](https://racket.discourse.group/t/python-string-indexes/2175/4 "2023-07-31T17:48:29Z")

</div>

To solve this problem _normalize-ranges_ should check if _end_ is less than 1 and not less than 0.

---

<div class="post-metadata">

**Author:** ![shawnw](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/shawnw/32/1031_2.png) [@shawnw](https://racket.discourse.group/u/shawnw)\
**Post date:** [August 1, 2023, 2:08am UTC](https://racket.discourse.group/t/python-string-indexes/2175/5 "2023-08-01T02:08:46Z")

</div>

You could if you wanted to, of course, but I deliberately didn't do any checking for things like out of bounds indexes. Left that to the original `substring`, which is going to do it anyways.

---

<div class="post-metadata">

**Author:** ![gus-massa](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/gus-massa/32/507_2.png) [@gus-massa](https://racket.discourse.group/u/gus-massa)\
**Post date:** [August 1, 2023, 2:18am UTC](https://racket.discourse.group/t/python-string-indexes/2175/6 "2023-08-01T02:18:32Z")

</div>

```scheme
#lang racket
(require magic/compatibility/phyton/other)

(define (test s n)
  (python-substring/other s 0 n))

(define S "Hello, Word!")

(test S 2) ; ==> "He"
(test S 1) ; ==> "H"
(test S 0) ; ==> "Hello, Word!" <-- !!! :(

```

I really like negative index, but the corer cases for `0` annoy me.

---

<div class="post-metadata">

**Author:** ![matteo-daddio](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/matteo-daddio/32/1245_2.png) [@matteo-daddio](https://racket.discourse.group/u/matteo-daddio)\
**Post date:** [August 1, 2023, 5:25am UTC](https://racket.discourse.group/t/python-string-indexes/2175/7 "2023-08-01T05:25:38Z")

</div>

> I really like negative index, but the corer cases for `0` annoy me.

Me too, probably that’s why racket doesn’t have negative indexes.

---

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [August 1, 2023, 11:10am UTC](https://racket.discourse.group/t/python-string-indexes/2175/8 "2023-08-01T11:10:54Z")

</div>

This discussion reminds me of the blog post

[http://scheme.dk/blog/2007/04/writing-spelling-corrector-in-plt.html](http://scheme.dk/blog/2007/04/writing-spelling-corrector-in-plt.html)

in which I ported Norvig's spelling checker from Python to Scheme.

In the blog post I use a little utility `concat` you might find useful.

The list comprehensions `list-ec` etc are from srfi 42. Today I would have used `for/list` and  
friends.

---

<div class="post-metadata">

**Author:** ![Kalimehtar](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/kalimehtar/32/394_2.png) [@Kalimehtar](https://racket.discourse.group/u/Kalimehtar)\
**Post date:** [August 6, 2023, 8:29am UTC](https://racket.discourse.group/t/python-string-indexes/2175/9 "2023-08-06T08:29:41Z")

</div>

> I like the general idea, but I don't like the ambiguity of 0.

No ambiguity. 0 is not negative and points to the first char in string.

Python-style also allows second index to be very large

```scheme
>>> "Hello"[0:1000]
'Hello'

```

So correct implementation should be

```scheme
(define (normalize-ranges s start end)
  (define l (string-length s))
  (values
    (if (< start 0) (+ l start) (min l start))
    (if (< end 0) (+ l end) (min l end))))

```

---

<div class="post-metadata">

**Author:** ![sschwarzer](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/sschwarzer/32/1940_2.png) [@sschwarzer](https://racket.discourse.group/u/sschwarzer)\
**Post date:** [August 6, 2023, 12:49pm UTC](https://racket.discourse.group/t/python-string-indexes/2175/10 "2023-08-06T12:49:34Z")

</div>

> [@Kalimehtar](#):
>
> So correct implementation should be

Still not right. 😉

```scheme
$ racket
Welcome to Racket v8.8 [cs].
> (define (normalize-ranges s start end) ; copied from Discourse thread
    (define l (string-length s))
    (values
      (if (< start 0) (+ l start) (min l start))
      (if (< end 0) (+ l end) (min l end))))
> (define s "Hello")
> (normalize-ranges s -100 -90)
-95
-85
> (normalize-ranges s -100 3)
-95
3
> (normalize-ranges s 5 3)
substring: ending index is smaller than starting index
  ending index: 3
  starting index: 5
  valid range: [0, 5]
  string: "Hello"
 [,bt for context]

```

Compare:

```python
$ python
Python 3.11.4 (main, Jun 7 2023, 00:00:00) [GCC 13.1.1 20230511 (Red Hat 13.1.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> s = "Hello"
>>> s[-100:-90]
''
>>> s[-100:3]
'Hel'
>>> s[5:3]
''

```

See [Common sequence operations](https://docs.python.org/3/library/stdtypes.html#common-sequence-operations) in the Python documentation.

---

<div class="post-metadata">

**Author:** ![Kalimehtar](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/kalimehtar/32/394_2.png) [@Kalimehtar](https://racket.discourse.group/u/Kalimehtar)\
**Post date:** [August 7, 2023, 10:43am UTC](https://racket.discourse.group/t/python-string-indexes/2175/11 "2023-08-07T10:43:47Z")

</div>

```scheme
(define (normalize-ranges s start end) ; copied from Discourse thread
    (define l (string-length s))
    (define (over ss) (if (< ss 0) (max 0 (+ l ss)) (min l ss)))
    (define start* (over start))
    (define end* (max start* (over end)))
    (values start* end*))

```
