file->sexp-list で遊ぶ

こんな風な関数を定義しておいて、

(use file.util)
(use util.match)
(use util.list)
(use gauche.sequence)
(use gauche.collection)

(define (sexp-hist sexp-list)
  (define (hist exp ht)
    (cond
     ((null? exp)
      ht)
     ((pair? exp)
      (hist (car exp) ht)
      (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)
    (hist sexp-list ht-result)
    ht-result))

(define (hist sexp-list)
  (let* ((ht (sexp-hist sexp-list))
         (alist (hash-table->alist ht)))
      (sort alist (lambda (x y)
                    (> (cdr x) (cdr y))))))

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

こんな感じで遊ぶ。

;; gauche のコンパイラ本体を見てみる
gosh> (define gh (hist-file "/Users/cranebird/Documents/Download/Gauche-0.8.14/src/compile.scm"))
gh
gosh> (length gh)
1163
;; シンボルは1163種。
gosh> (subseq gh 0 50)
((iform . 652) (cenv . 321) (ccb . 306) (quote . 254) (form . 252) (unquote . 242) (define . 208) (renv . 200) (ctx . 173) (args . 164) (else . 124) (if . 121) (car . 118) (let1 . 114) (#f . 110) (quasiquote . 100) (x . 99) (name . 99) (0 . 98) (+ . 96) (info . 94) (body . 92) ($*-src . 89) (pass1 . 87) (cdr . 80) (rec . 79) (error . 79) (i . 77) (r . 77) (1 . 76) (_ . 75) (y . 74) (let . 70) (match . 69) (list . 69) (pass3/rec . 65) (expr . 64) (V . 62) (obj . 61) (null? . 60) (penv . 59) (lvar . 59) (cond . 56) (cons . 55) (lambda . 55) (test . 55) (2 . 55) (has-tag? . 54) (exprs . 53) (and . 53))
;; トップ50。人気?のあるのは iform。コンパイラがあつかうデータ構造だから当然か。ccb って何だろうなぁ。ctx はコンテキスト、らしい。

データさえ作れればタブ区切りファイルにできて、後は R でグラフ化。今は集計まで scheme 側でしているが、R 側にほぼ生データを渡す方がベターかも。

(with-output-to-file "result.tsv"
  (lambda ()
    (for-each (lambda (s)
                (format #t "~s~a~s~%" (car s) #\tab (cdr s)))
              (hist-file "compiler.scm"))))


熟練した Lisper のコードと初心者のコードの頻度分布には何らかの差があるのでは。