プログラムと統計/試行

まずは scheme で簡単にプログラムを統計的に処理することをやってみます。Lispではプログラム=データなので、気楽に試せます。まずはいくつかのシンボルの利用頻度の結果から。

gosh> (list-hist "gauche-compile.scm")
((car . 0.28992628992628994) (cdr . 0.19656019656019655) (null? . 0.14742014742014742) (cons . 0.13513513513513514) (cadr . 0.056511056511056514) (length . 0.04914004914004914) (reverse . 0.04668304668304668) (pair? . 0.02457002457002457) (append . 0.014742014742014743) (caddr . 0.009828009828009828) (caar . 0.007371007371007371) (cdar . 0.007371007371007371) (list? . 0.004914004914004914) (list-tail . 0.002457002457002457) (cdddr . 0.002457002457002457) (set-cdr! . 0.002457002457002457) (cddr . 0.002457002457002457))
gosh> (list-hist "toy-compile.scm")
((car . 0.19424460431654678) (null? . 0.16546762589928057) (cdr . 0.14388489208633093) (length . 0.14388489208633093) (pair? . 0.11510791366906475) (cons . 0.11510791366906475) (append . 0.07913669064748201) (cadr . 0.02877697841726619) (reverse . 0.014388489208633094))
gosh> 

上は gauche のcompile.scm(0.8.4)、下はわたしの書いているトイコンパイラでの結果です。結果はalistで、car はシンボル、cdr の数値はシンボルがソースファイル中に出現した割合で、全て足すと1になります。つまり適当に選んだシンボルの中での割合です。そもそも行数が違う(約4500行vs2000行)ので出現数では比較できないため、割合にしました。

  • どちらも car,cdr あたりの頻度は高め。
  • わたしの方は caar, set-car! などまったく使っていない。使う場面が無かったのか、使いこなせていないのか。
(define (list-hist file)
  (let1 target '(pair? cons car cdr set-car! set-cdr!
                           caar cadr cdar cddr
                           caaar caadr cadar caddr cdaar cdadr cddar cdddr
                           null? list? length append reverse list-tail )
    (count->% (filter (lambda (x)
                        (memv (car x) target)) (hist-file file)))))

(define (count->% alist)
  (let ((count (apply + (map cdr alist))))
    (map (lambda (x)
           (cons (car x) (* 1.0 (/ (cdr x) count)))) alist)))

(define (hist-file file)
  (let1 sexp-list (file->sexp-list file)
    (hist-sexp sexp-list)))

(define (hist-sexp sexp-list)
  (define (calc-hist exp ht)
    (cond
     ((null? exp)
      ht)
     ((pair? exp)
      (calc-hist (car exp) ht)
      (calc-hist (cdr exp) ht))
     (else
      (if (hash-table-exists? ht exp)
          (hash-table-put! ht exp (+ (hash-table-get ht exp) 1))
          (hash-table-put! ht exp 1)))))
  (let1 ht-result (make-hash-table)
    (calc-hist sexp-list ht-result)
    (sort (hash-table->alist ht-result)
          (lambda (x y)
            (> (cdr x) (cdr y))))))

これをもっと沢山の scheme ハッカーらの、複数のプログラムで、もっとシンボルも増やして網羅的にやると何か良いコードを書くためのヒントを得たいと思います。なお、不勉強なのでこのような試みがどこかで既に行われているのをご存知の方はコメントを頂けると嬉しいです。


続く。