9章1節の練習問題:delete-1

srfi-1のdeleteは等価述語を満たす要素を全部削除してしまうけど、見つけた1つ目だけ消す手続きdelete-1を定義してみると

(define delete-1
  (lambda (elt lis . optional)
    (let-optionals* optional ((cmp-fn equal?))
      (define loop
        (lambda (lis)
          (cond
            ((null? lis) '())
            ((cmp-fn elt (car lis)) (cdr lis))
            (else (cons (car lis) (loop (cdr lis)))))))
      (loop lis))))

ってなるけど、第1引数で指定した要素が見つからなかった場合に、一切コピーしないようにdelete-1を書き換えてみよう、と。
ヒントを読まないと取っ掛かりが分からんかった ><


ヒントみて、初めて書けたやつ。

(define delete-1
  (lambda (elt lis . optionals)
    (let-optionals* optionals ((cmp-fn equal?))
      (cond
       ((null? lis) lis)
       ((member elt (cdr lis) cmp-fn)
        (if (cmp-fn elt (car lis))
            (cdr lis)
            (cons (car lis) (delete-1 elt (cdr lis) cmp-fn))))
       (else
        (if (cmp-fn elt (car lis))
            (cdr lis)
            lis))))))

null?のチェック要らなくね?ということで、condでnull?のチェック削除した。

(define delete-1
  (lambda (elt lis . optionals)
    (let-optionals* optionals ((cmp-fn equal?))
      (cond
       ((member elt (cdr lis) cmp-fn)
        (if (cmp-fn elt (car lis))
            (cdr lis)
            (cons (car lis) (delete-1 elt (cdr lis) cmp-fn))))
       (else
        (if (cmp-fn elt (car lis))
            (cdr lis)
            lis))))))

2個あるif式の

  • 条件式
  • それが真の場合の値

同じで、違うのは条件式が#fになった時の値だけなので、まとめて外に出してみた。

(define delete-1
  (lambda (elt lis . optionals)
    (let-optionals* optionals ((cmp-fn equal?))
      (cond
       ((cmp-fn elt (car lis)) (cdr lis))
       ((member elt (cdr lis) cmp-fn)
        (cons (car lis) (delete-1 elt (cdr lis) cmp-fn)))
       (else lis)))))

本に倣って、ローカル手続きにしてみた。

(define delete-1
  (lambda (elt lis . optionals)
    (let-optionals* optionals ((cmp-fn equal?))
      (define loop
        (lambda (lis)
          (cond
           ((cmp-fn elt (car lis)) (cdr lis))
           ((member elt (cdr lis) cmp-fn)
            (cons (car lis) (loop (cdr lis))))
           (else lis))))
      (loop lis))))

できた!(*´▽`)パァァ
...と思ったけど、このdelete-1は

(delete-1 'foo '())

を評価するとエラーになる。
null?のチェックを削除してから

(cmp-fn elt (car lis))

をmemberの前に出してしまったからだな。ってことでnull?のチェックを復活。

(define delete-1
  (lambda (elt lis . optionals)
    (let-optionals* optionals ((cmp-fn equal?))
      (define loop
        (lambda (lis)
          (cond
           ((null? lis) lis)
           ((cmp-fn elt (car lis)) (cdr lis))
           ((member elt (cdr lis) cmp-fn)
            (cons (car lis) (loop (cdr lis))))
           (else lis))))
      (loop lis))))

多分、これでOKじゃないかなー。

gosh> (let ((data (list 1 2 3 4 5)))
        (test* "non-copy delete-1" data (delete-1 6 data) eq?))
gosh> test non-copy delete-1, expects (1 2 3 4 5) ==> ok
#<undef>

非効率かも?(2009/03/08追記)

delete-1で毎回member手続きを実行しているけど、非効率な気がしてきた。
何か良い方法があるのかも知れないなぁ。