Scheme - Replacing elements in a list with its ind

2019-08-11 02:25发布

问题:

I am trying to replace the elements in a scheme list with its position. For example, calling:

(position '((a b) c))

should return:

'((0 1) 2)

So far, my code keeps the list format, but the index is not updating.

(define (position term1)
  (define index 0)
    (cond [(null? term1) '()]
          [(list? term1) (cons (position (car term1)) (position(cdr term1)))]
          [else (+ 1 index) index]))

When (position '((a b) c)) is called, it returns

'((0 0) 0)

Can anybody explain why the index isn't updating?

回答1:

There are a couple things wrong: first notice that every time you recursively call position, index is bound to zero.

Second, look at your else branch. (+ 1 index) evaluates to 1 (it does not change any variables) and index evaluates to 0. This branch can only evaluate to one thing, so what happens is the last one is returned and the rest are discarded. This is where your zeroes come from.

It seems like within your function you are trying to keep a global state (the current index) and modify it each time you label a leaf. The "modifying state" part is not good functional style, but if you are okay with that then take a look at set!.



回答2:

Here is one solution using CPS:

#lang racket

(define (index xs [i 0] [continue (λ (xs i) xs)])
  (match xs
    [(cons x xs) (index x i
                        (λ (js j)
                          (index xs j
                                 (λ (ks k)
                                   (continue (cons js ks) k)))))]
    ['()         (continue '() i)]
    [x           (continue i (+ i 1))]))

; Example
;(index '((a b) (c d) x (e (f g) h)))
; '((0 1) (2 3) 4 (5 (6 7) 8))

Here (index xs i continue) replaces the elements in xs with their indices, the count starts from i. Let's say the result of indexing xs is js, then continue is called with the indexing result and the next index to be used: (continue js j).



回答3:

Daenerys Naharis already pointed out what's wrong, so let me point out some features of Scheme and Racket you may be unaware of that you could use in a solution that maintains functional style.

This is called a named let:

(let loop ((index 0)
           (result '()))
   (if (= index 10)
       (reverse result)
       (loop (+ 1 index) (cons index result)))

Within the let form, loop is bound as a function that takes all the local variables as arguments. Calling it recursively calls the let form itself. This way you can make index an argument without making it an argument of position. You can also put the result in an argument, which allows you to make the call to loop a tail call.

The other feature is less widespread among existing Scheme implementations: Optional arguments. In Racket, they're defined like this:

(define (position term1 (index 0)) ...)

Then position can be called with or without the index argument.



回答4:

An example using mutation that maintains it's own state so that each item of each list has a unique id.

Example Usage:

> (position '((a b) c))
'((0 1) 2)
> (position '((a b) c (d (e))))
'((3 4) 5 (6 (7)))

Example Implementation:

#lang racket

(provide position)

(define base -1)

(define (indexer)
  (set! base (add1 base))
  base)

(define (position list-of-x)
  (cond [(null? list-of-x) null]
        [else
         (define head (first list-of-x))
         (cond [(list? head) 
                (cons (position head)
                      (position (rest list-of-x)))]
               [else (cons (indexer) 
                           (position (rest list-of-x)))])]))


标签: scheme racket