ということは、思いつくまま書いて、必要になってから初めて抽象度の高い関数を定義し、それを元の関数から呼ぶようにリファクタリングするのが正解なのかなあ。でも、思いついたまま書いたら書いたで、規模や内容によっては後から分離したり抽象度を上げたりするのが難し(辛)くて、結局「最初から抽象度を高く定義しときゃよかったわ・・・」となり放置あるいわ断念ということもしばしば。
プログラミング自体やロジックを考えたりするのも簡単ではありませんが、こういう落とし所の判断も難しいれす。「現時点ではこれで良い。」と判断するのってなかなか勇気がいります。それとも判断しようとすること自体が余計なのか。必要になるまで判断しない、とか。lazy に。
例としては小粒過ぎるかもしれませんが、例えば
三つの数を引数としてとり、大きい二つの数の二乗の和を返す手続きを定義せよ。(SICP の 1.3 の問題ですね)
準備。
(define (square x) (* x x))
現実の開発でも大きな問題を細切れにしていくとこのくらいの粒度の問題に行きつきますよね。抽象度はばらけてますが、最後の例は抽象度が高いと思います。でもここまでやるか、ということです。その手前くらいで良いような気がします。
問題に対してバカ正直に
;; cond (define (sum-of-square-picked-big2 a b c) (cond ((and (< a b) (< a c))(+ (square b) (square c))) ((and (< b a) (< b c))(+ (square c) (square a))) (else (+ (square a)(square b))))) (sum-of-square-picked-big2 1 2 3) ;; -> 13 (sum-of-square-picked-big2 9 10 8) ;; -> 181ソートして先頭の2つを使う
;; sort (define (sum-of-square-picked-big2 . args) (let1 sls (sort args >) (+ (square (car sls)) (square (cadr sls))))) (sum-of-square-picked-big2 1 2 3) ;; -> 13 (sum-of-square-picked-big2 1 2 3 4 5 6 7 8 9 10) ;; -> 181
先頭から指定した分を取得して計算するケース
;; head (define (head n ls :key (sorter identity)) (let rec ((n n)(ls (sorter ls))(acc '())) (if (zero? n) acc (rec (- n 1)(cdr ls)(cons (car ls) acc))))) (define (pick-big n nums) (head n nums :sorter (lambda (ls) (sort ls >)))) (define (sum-of-square-picked-big2 . args) (apply + (map square (pick-big 2 args)))) (sum-of-square-picked-big2 1 2 3) ;; -> 13 (sum-of-square-picked-big2 9 10 8) ;; -> 181
「選択」して「計算」するとしたケース
(use gauche.experimental.app) (define (pick-calc picker calculator . args) ($ calculator $ picker args)) (define (sum-of-square-picked-big2 . args) (apply pick-calc (^l (pick-big 2 l)) (^l (apply + (map square l))) args)) (sum-of-square-picked-big2 1 2 3) ;; -> 13 (sum-of-square-picked-big2 1 2 3 4 5 6 7 8 9 10) ;; -> 181
ちょっと例が微妙過ぎたか。
0 件のコメント:
コメントを投稿