読者です 読者をやめる 読者になる 読者になる

(wat-aro)

無職から有職者にランクアップしました

プログラミングGauche 9.1練習問題

delete-1は見つからなかった場合もcond式のelse節でconsしているためにコピーしたリストを返す.
元のリストを返すように実装する. 以下が元のdelete-1

(define (delete-1 elt lis . options)
  (let-optionals* options ((cmp-fn equal?))
    (define (loop lis)
      (cond [(null? lis) ()]
            [(cmp-fn (car lis) elt) (cdr lis)]
            [else (cons (car lis)
                        (loop (cdr lis)))]))
    (loop lis)))
  • condをつかった実装.
(define (delete-1 elt lis . options)
  (let-optionals* options ((cmp-fn equal?))
    ;; 見つけた場合の処理
    (define (loop lis)
      (cond [(cmp-fn (car lis) elt) (cdr lis)]
            [else (cons (car lis)
                        (loop (cdr lis)))]))
    ;; member関数で要素があるか探す
    (cond [(and (member elt lis cmp-fn) #t) (loop lis)]
          [else lis])))

見つからない場合の処理はすでにmemberで行ってるのでnull?は省略.
(cond [(and (member elt lis cmp-fn) #t) (loop lis)]の部分が少しわかりにくい.
memberは要素が見つからなかった場合に元のリストを返すのでandに入れてリストが返って来た場合は#tを,#fが返ってきた場合は#fを返すようにした.

  • ifをつかった実装
(define (delete-1 elt lis . options)
  (let-optionals* options ((cmp-fn equal?))
    ;; 見つけた場合の処理
    (define (loop lis)
      (if (cmp-fn (car lis) elt)
          (cdr lis)
          (loop (cdr lis))))
    ;; member関数で要素があるか探す
    (if (member elt lis cmp-fn)
        (loop lis)
        lis)))

ifならリストが返って来た場合もthen節を実行してくれる.

memberで末尾再帰で探した後にdelete-1で非末尾再帰で削除するのは無駄が多い気も. もっといい書き方あるかな.

[追記]
ググったらもっといい書き方ありました.

プログラミングGauche 9.1 集合 練習問題 : Serendip - Webデザイン・プログラミング

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

eq?で比較しろって書かれていたのはこういうことだったのか.
重複している部分があるのでifの分岐を削除して書き換えます

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

元のコードに一行足すだけだったとは・・・