Question on $idx

Started by cameyo, March 16, 2026, 02:04:23 PM

Previous topic - Next topic

cameyo

(define (test lst)
  (let (len (length lst))
    (dolist (el lst)
      ; select and index of the list different from the current
      (while (!= (setq idx (rand len)) $idx)))
    (println "index: " idx)))
Why (test '(1 2 3 4 5)) fall (almost always) into an infinite loop?
Why (test '(1)) return always 0? The function should fall into an infinite loop.

I know the answer...

cameyo

The previous code is wrong. Sorry.

Let's write a function that associates each index of a list with a different index of the list itself.
(define (sel-idx lst)
  (let ((out '()) (len (length lst)))
    (dolist (el lst)
      ; select and index of the list different from the current
      (while (= (setq idx (rand len)) $idx))
      ;(println "coppia: " $idx idx)
      (push (list $idx idx) out -1))
    out))

Now if lst has only one value, the function should loop (because it's not possible to select an index other than 0):
(sel-idx '(1))
;-> ((0 0))  --> ERROR

If lst has more than one value, the function should work correctly:
(sel-idx '(1 2))
;-> ((0 1) (1 0))  --> OK
(sel-idx '(1 2))
;-> ((0 1) (1 1))  --> ERROR
Why doesn't the function produce correct results?
Because 'until' (and 'while') also have an internal variable $idx.
So we're not comparing the generated value 'idx' to $idx from 'dolist', but to $idx from 'until'.
To fix this, we need to copy the value '$idx' from 'dolist' into a variable and apply it within the 'until' loop.
(define (sel-idx-ok lst)
  (let ((out '()) (len (length lst)) (tmp 0))
    (dolist (el lst)
      (setq tmp $idx)
      ; select and index of the list different from the current
      (while (= (setq idx (rand len)) tmp))
      ;(println "coppia: " $idx idx)
      (push (list tmp idx) out -1))
    out))

(sel-idx-ok '(1))
;-> ... --> infinite loop
(sel-idx-ok '(1 2))
;-> ((0 1) (1 0)) --> OK
(sel-idx '(0 1 2 3 4 5 6 7 8 9))
;-> ((0 4) (1 9) (2 4) (3 8) (4 8) (5 9) (6 1) (7 1) (8 6) (9 6))