2009/12/02

scheme filter

にコメントが付いてました!

このfilterだと入れ子のリストは処理できないですね
たとえば
(1 2 (3 4) 5 (6 7))

ホンマや・・・。

ということで寝る前に

(define filter
  (lambda (pred l)
    (cond
     ((null? l) l)
     ((list? (car l))(filter pred (car l)))
     ((pred (car l))(cons (car l)
                         (filter pred (cdr l))))
     (else (filter pred (cdr l))))))

(filter even? '(1 2 3 4 5))
; -> (2 4)
(filter even? '(1 2 3 4 5 (6 7 8 9 (10 11 12 13)(14 15 16 (17 18 (19 20))))))
; -> (2 4 6 8 10 12)

結果は、リストのネストもそのままにすべきか?ということで・・・

(define filter
  (lambda (pred l)
    (cond
     ((null? l) l)
     ((list? (car l))(cons (filter pred (car l))
                           (filter pred (cdr l))))
     ((pred (car l))(cons (car l)
                         (filter pred (cdr l))))
     (else (filter pred (cdr l))))))

(filter even? '(1 2 3 4 5))
; -> (2 4)
(filter even? '(1 2 3 4 5 (6 7 8 9 (10 11 12 13)(14 15 16 (17 18 (19 20))))))
; -> (2 4 (6 8 (10 12) (14 16 (18 (20)))))
(filter even? '((((1 2 3) 4 5 6 (7 (8 9 10 (((11 12 13)) 14) 15)))) 16 17))
; -> ((((2) 4 6 ((8 10 (((12)) 14))))) 16)
(filter odd? '((((1 2 3) 4 5 6 (7 (8 9 10 (((11 12 13)) 14) 15)))) 16 17))
; -> ((((1 3) 5 (7 (9 (((11 13))) 15)))) 17)

 

どっちだろ。srfiだと・・・

(use srfi-1)
(filter even? '(1 2 3 4 5))
; -> (2 4)
(filter even? '(1 2 3 4 5 (6 7 8 9 (10 11 12 13)(14 15 16 (17 18 (19 20))))))
; -> *** ERROR: integer required, but got (6 7 8 9 (10 11 12 13) (14 15 16 (17 18 (19 20))))
(filter even? '((((1 2 3) 4 5 6 (7 (8 9 10 (((11 12 13)) 14) 15)))) 16 17))
; -> *** ERROR: integer required, but got (((1 2 3) 4 5 6 (7 (8 9 10 (((11 12 13)) 14) 15))))

 

今までまったくコメントチェックしてませんでした。今度から気をつけます。(サイドバーに"最近のコメント"を追加して、メールでも知らせてくれるようにした)

 

なんか久しぶりにScheme書いた気がした。

3 件のコメント:

  1. ううんと、ですね。

    そもそも、filterは

    (1 2 (3 4) 5 (6 7))

    なんか「処理できればおかしい」って事だと思うんですけど。

    例えば述語がeven?とかodd?なんかの場合、元々それらの手続きが「引数に整数を取る」ってのが前提なんですよ。

    (even? "hoge")

    とか

    (odd? "fuga")

    とか計算出来たら「困る」わけです。

    そうすると、基本的には、あるリストの要素がリストだったら「計算不可」に陥って当たり前なんですけどね。だって、リストにぶつかっちゃった以上、それはodd?とかeven?で扱えない要素なんですから。
    filterがそれを無視して、リスト内要素のリストの中に「勝手に潜っていく」ってのは危険なんじゃないか、って思います。
    srfiのfilterの動きの方が「マシ」ですよね。ERRORが起きてるのは「filterのせい」じゃなくって、与えた関数の引数が「おかしい」って言ってるわけですから。こっちの方が正しいと思います。

    ちなみに、元々srfiのfilterなんてのは、ANSI Common Lispのremove-if-notの移植だと思うんですが。

    CL-USER> (remove-if-not #'evenp '(1 2 3 4 5))
    (2 4)

    同じ動作ですよね。
    そもそもこちらでも(1 2 (3 4) 5 (6 7))なんてリストを喰わせて#'evenp や#'oddpなんかでフィルタリングかけようとしたら「エラーになります」。これも理由は同じで、そもそもevenpやoddpの引数が「整数じゃなきゃならない」からです。入れ子になったリストと遭遇すると、これは「整数じゃない」んで、remove-if-notがエラーを返すんじゃなくって、関数引数側がエラーを返すのです。

    ですから、「勝手に入れ子の内部に潜っていく」高階関数は、発想的には「面白い」んですが、出来ても危険なんじゃないか、って思います。データ型があるにはあるなりの理由があるんで。

    返信削除
  2. cametanさん
    なるほどー・・・。大変よくわかりました。
    リストの要素を処理対象にしているのであって、リストの要素がリストである場合は話が違うということで良いんですよね。

    ネストしたリストを処理したい場合、別途適した名前の関数を別途用意するのが妥当でしょうか?

    srfiのfilterはエラーを吐いたので「きっと意図的なんだろうけど・・・、なんでだろう?」と思いつつも寝ました^^;

    ありがとうございました。

    返信削除
  3. >ネストしたリストを処理したい場合、別途適した名前の関数を別途用意するのが妥当でしょうか?

    はい。
    そうした方が妥当だと思います。

    返信削除