在Scheme中交换列表中的两个元素

3
我需要在Scheme语言中交换列表中输入索引处的两个元素。例如:
(swap-index 0 3 '(1 2 3 4 5))
输出结果为:(4 2 3 1 5)
有人可以帮忙吗?先谢谢了!:)

@wvxvw -- 同意。有没有可能解释一下?另外,为什么一个Clojure答案被接受作为Scheme问题的答案? - robbyphillips
3个回答

1

目前我想不到解决这个问题的方法,除非对整个列表进行三次迭代(每个list-ref一次,再加上一个build-list)。虽然不是最有效的解决方案,但是我们还是试一下吧:

(define (swap-index idx1 idx2 lst)
  (define (build-list lst idx e1 e2)
    (cond ((null? lst)
           '())
          ((= idx idx1)
           (cons e2 (build-list (cdr lst) (add1 idx) e1 e2)))
          ((= idx idx2)
           (cons e1 (build-list (cdr lst) (add1 idx) e1 e2)))
          (else
           (cons (car lst) (build-list (cdr lst) (add1 idx) e1 e2)))))
  (build-list lst 0 (list-ref lst idx1) (list-ref lst idx2)))

我假设给定列表中存在索引,否则list-ref将产生错误。索引可以以任何顺序传递,这意味着:idx1可以小于、等于或大于idx2。它按预期工作,在原地进行修改并返回一个新列表
(swap-index 0 3 '(1 2 3 4 5))
=> '(4 2 3 1 5)

1

此方法最多遍历列表两次:

(define (swap-index index1 index2 lst)
    ;; FIND-ELEMENTS -- 
    ;;  INPUT:  count, an integer; lst, a list
    ;; OUTPUT:  a pair of the form '(a . b)
    (define (find-elements count lst)
      (cond ((null? lst) '()) ; really, we should never reach this if indices are valid
            ((= count index1) ; found the first element, so hold on to it while we look for the next one
             (cons (car lst) (find-elements (+ 1 count) (cdr lst))))
            ((= count index2) (car lst)) ; found the second element, return part 2 of the pair
            (else ; since we only care about 2 elements we can just skip everything else
              (find-elements (+ 1 count) (cdr lst)))))
    ;; BUILD-LIST --
    ;;  INPUT:  count, an integer; elements, a pair; lst, a list
    ;; OUTPUT:  a new list
    (define (build-list count elements lst)
      (cond ((null? lst) '()) ; again, we shouldn't get here if indices are valid
            ((= count index1) ; reached first index, substitute 2nd element and keep going
             (cons (cdr elements) (build-list (+ 1 count) elements (cdr lst))))
            ((= count index2) ; reached second index, substitute 1st element and stop
             (cons (car elements) (cdr lst)))
            (else  ; everything else just gets added to the list per usual
              (cons (car lst) (build-list (+ 1 count) elements (cdr lst))))))
    (build-list 0 (find-elements 0 lst) lst)) ; call build-list using a call to find-elements as a parameter

首先,find-elements 通过列表查找并返回我们想要交换的元素的 cons 对。 注意: 此代码依赖于假设索引按顺序给出,因此最小的索引排在第一位。
接下来,build-list 获取来自 find-elements 的输出,以便在下一次遍历期间替换适当的元素。

0

这是Clojure的解决方案。我希望算法能够有所帮助。

(defn split [idx lst]
  (let [lst-rest (drop idx lst)]
   [(take idx lst) (first lst-rest) (rest lst-rest)]))

(defn swap-index [idx1 idx2 lst]
  (let [[lst1 e1 lst] (split idx1 lst)
        [lst2 e2 lst3] (split (dec (- idx2 idx1)) lst)]
    (concat lst1 [e2] lst2 [e1] lst3)))

=> (swap-index 0 3 [1 2 3 4 5])
   (4 2 3 1 5)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接